Completed
Push — 3.x ( e95e95...638cd1 )
by Oskar
05:54
created

src/Menu/Provider/GroupMenuProvider.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Menu\Provider;
15
16
use Knp\Menu\FactoryInterface;
17
use Knp\Menu\ItemInterface;
18
use Knp\Menu\Provider\MenuProviderInterface;
19
use Sonata\AdminBundle\Admin\Pool;
20
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
21
22
/**
23
 * Menu provider based on group options.
24
 *
25
 * @author Alexandru Furculita <[email protected]>
26
 */
27
class GroupMenuProvider implements MenuProviderInterface
28
{
29
    /**
30
     * @var FactoryInterface
31
     */
32
    private $menuFactory;
33
34
    /**
35
     * @var Pool
36
     */
37
    private $pool;
38
39
    /**
40
     * @var AuthorizationCheckerInterface
41
     */
42
    private $checker;
43
44
    /**
45
     * NEXT_MAJOR: Remove default value null of $checker.
46
     *
47
     * @param AuthorizationCheckerInterface|null $checker
48
     */
49
    public function __construct(FactoryInterface $menuFactory, Pool $pool, $checker = null)
50
    {
51
        $this->menuFactory = $menuFactory;
52
        $this->pool = $pool;
53
54
        /*
55
         * NEXT_MAJOR: Remove this if blocks.
56
         * NEXT_MAJOR: Move AuthorizationCheckerInterface check to method signature.
57
         */
58
        if (null === $checker) {
59
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
60
                'Passing no 3rd argument is deprecated since version 3.10 and will be mandatory in 4.0.
61
                Pass Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface as 3rd argument.',
62
                E_USER_DEPRECATED
63
            );
64
        } elseif (!$checker instanceof AuthorizationCheckerInterface) {
65
            throw new \InvalidArgumentException(
66
                'Argument 3 must be an instance of \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
67
            );
68
        }
69
70
        $this->checker = $checker;
71
    }
72
73
    /**
74
     * Retrieves the menu based on the group options.
75
     *
76
     * @param string $name
77
     *
78
     * @throws \InvalidArgumentException if the menu does not exists
79
     *
80
     * @return \Knp\Menu\ItemInterface
81
     */
82
    public function get($name, array $options = [])
83
    {
84
        $group = $options['group'];
85
86
        $menuItem = $this->menuFactory->createItem($options['name']);
87
88
        if (empty($group['on_top']) || false === $group['on_top']) {
89
            foreach ($group['items'] as $item) {
90
                if ($this->canGenerateMenuItem($item, $group)) {
91
                    $menuItem->addChild($this->generateMenuItem($item, $group));
92
                }
93
            }
94
95
            if (false === $menuItem->hasChildren()) {
96
                $menuItem->setDisplay(false);
97
            } elseif (!empty($group['keep_open'])) {
98
                $menuItem->setAttribute('class', 'keep-open');
99
                $menuItem->setExtra('keep_open', $group['keep_open']);
100
            }
101
        } elseif (1 === \count($group['items'])) {
102
            if ($this->canGenerateMenuItem($group['items'][0], $group)) {
103
                $menuItem = $this->generateMenuItem($group['items'][0], $group);
104
                $menuItem->setExtra('on_top', $group['on_top']);
105
            } else {
106
                $menuItem->setDisplay(false);
107
            }
108
        }
109
        $menuItem->setLabel($group['label']);
110
111
        return $menuItem;
112
    }
113
114
    /**
115
     * Checks whether a menu exists in this provider.
116
     *
117
     * @param string $name
118
     *
119
     * @return bool
120
     */
121
    public function has($name, array $options = [])
122
    {
123
        return 'sonata_group_menu' === $name;
124
    }
125
126
    private function canGenerateMenuItem(array $item, array $group): bool
127
    {
128
        if (isset($item['admin']) && !empty($item['admin'])) {
129
            $admin = $this->pool->getInstance($item['admin']);
130
131
            // skip menu item if no `list` url is available or user doesn't have the LIST access rights
132
            return $admin->hasRoute('list') && $admin->hasAccess('list');
133
        }
134
135
        //NEXT_MAJOR: Remove if statement of null checker.
136
        if (null === $this->checker) {
137
            return true;
138
        }
139
140
        // Making the checker behave affirmatively even if it's globally unanimous
141
        // Still must be granted unanimously to group and item
142
143
        $isItemGranted = true;
144
        if (!empty($item['roles'])) {
145
            $isItemGranted = false;
146
            foreach ($item['roles'] as $role) {
147
                if ($this->checker->isGranted([$role])) {
148
                    $isItemGranted = true;
149
                    break;
150
                }
151
            }
152
        }
153
154
        $isGroupGranted = true;
155
        if (!empty($group['roles'])) {
156
            $isGroupGranted = false;
157
            foreach ($group['roles'] as $role) {
158
                if ($this->checker->isGranted([$role])) {
159
                    $isGroupGranted = true;
160
                    break;
161
                }
162
            }
163
        }
164
165
        return $isItemGranted && $isGroupGranted;
166
    }
167
168
    private function generateMenuItem(array $item, array $group): ItemInterface
169
    {
170
        if (isset($item['admin']) && !empty($item['admin'])) {
171
            $admin = $this->pool->getInstance($item['admin']);
172
173
            $options = $admin->generateMenuUrl('list', [], $item['route_absolute']);
174
            $options['extras'] = [
175
                'label_catalogue' => $admin->getTranslationDomain(),
176
                'admin' => $admin,
177
            ];
178
179
            return $this->menuFactory->createItem($admin->getLabel(), $options);
180
        }
181
182
        return $this->menuFactory->createItem($item['label'], [
183
            'route' => $item['route'],
184
            'routeParameters' => $item['route_params'],
185
            'routeAbsolute' => $item['route_absolute'],
186
            'extras' => [
187
                'label_catalogue' => $group['label_catalogue'],
188
            ],
189
        ]);
190
    }
191
}
192