Issues (3627)

app/bundles/CoreBundle/Menu/MenuHelper.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2014 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\CoreBundle\Menu;
13
14
use Mautic\CoreBundle\Helper\CoreParametersHelper;
15
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
16
use Mautic\PluginBundle\Helper\IntegrationHelper;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
20
/**
21
 * Class MenuHelper.
22
 */
23
class MenuHelper
24
{
25
    /**
26
     * @var CorePermissions
27
     */
28
    protected $security;
29
30
    /**
31
     * @var Request|null
32
     */
33
    protected $request;
34
35
    /**
36
     * Stores items that are assigned to another parent outside it's bundle.
37
     *
38
     * @var array
39
     */
40
    private $orphans = [];
41
42
    /**
43
     * @var CoreParametersHelper
44
     */
45
    private $coreParametersHelper;
46
47
    /**
48
     * @var IntegrationHelper
49
     */
50
    protected $integrationHelper;
51
52
    /**
53
     * MenuHelper constructor.
54
     */
55
    public function __construct(CorePermissions $security, RequestStack $requestStack, CoreParametersHelper $coreParametersHelper, IntegrationHelper $integrationHelper)
56
    {
57
        $this->security              = $security;
58
        $this->coreParametersHelper  = $coreParametersHelper;
59
        $this->request               = $requestStack->getCurrentRequest();
60
        $this->integrationHelper     = $integrationHelper;
61
    }
62
63
    /**
64
     * Converts menu config into something KNP menus expects.
65
     *
66
     * @param        $items
67
     * @param int    $depth
68
     * @param int    $defaultPriority
69
     * @param string $type
70
     */
71
    public function createMenuStructure(&$items, $depth = 0, $defaultPriority = 9999, $type = 'main')
72
    {
73
        foreach ($items as $k => &$i) {
74
            if (!is_array($i) || empty($i)) {
75
                continue;
76
            }
77
78
            // Remove the item if the checks fail
79
            if (false === $this->handleChecks($i)) {
80
                unset($items[$k]);
81
                continue;
82
            }
83
84
            //Set ID to route name
85
            if (!isset($i['id'])) {
86
                if (!empty($i['route'])) {
87
                    $i['id'] = $i['route'];
88
                } else {
89
                    $i['id'] = 'menu-item-'.uniqid();
90
                }
91
            }
92
93
            //Set link attributes
94
            if (!isset($i['linkAttributes'])) {
95
                $i['linkAttributes'] = [
96
                    'data-menu-link' => $i['id'],
97
                    'id'             => $i['id'],
98
                ];
99
            } elseif (!isset($i['linkAttributes']['id'])) {
100
                $i['linkAttributes']['id']             = $i['id'];
101
                $i['linkAttributes']['data-menu-link'] = $i['id'];
102
            } elseif (!isset($i['linkAttributes']['data-menu-link'])) {
103
                $i['linkAttributes']['data-menu-link'] = $i['id'];
104
            }
105
106
            $i['extras']          = [];
107
            $i['extras']['depth'] = $depth;
108
109
            // Note a divider
110
            if (!empty($i['divider'])) {
111
                $i['extras']['divider'] = true;
112
            }
113
114
            // Note a header
115
            if (!empty($i['header'])) {
116
                $i['extras']['header'] = $i['header'];
117
            }
118
119
            //Set the icon class for the menu item
120
            if (!empty($i['iconClass'])) {
121
                $i['extras']['iconClass'] = $i['iconClass'];
122
            }
123
124
            //Set the actual route name so that it's available to the menu template
125
            if (isset($i['route'])) {
126
                $i['extras']['routeName'] = $i['route'];
127
            }
128
129
            //Repeat for sub items
130
            if (isset($i['children'])) {
131
                $this->createMenuStructure($i['children'], $depth + 1, $defaultPriority);
132
            }
133
134
            // Determine if this item needs to be listed in a bundle outside it's own
135
            if (isset($i['parent'])) {
136
                if (!isset($this->orphans[$type])) {
137
                    $this->orphans[$type] = [];
138
                }
139
140
                if (!isset($this->orphans[$type][$i['parent']])) {
141
                    $this->orphans[$type][$i['parent']] = [];
142
                }
143
144
                $this->orphans[$type][$i['parent']][$k] = $i;
145
146
                unset($items[$k]);
147
148
            // Don't set a default priority here as it'll assume that of it's parent
149
            } elseif (!isset($i['priority'])) {
150
                // Ensure a priority for non-orphans
151
                $i['priority'] = $defaultPriority;
152
            }
153
        }
154
    }
155
156
    /**
157
     * Get and reset orphaned menu items.
158
     *
159
     * @param string $type
160
     *
161
     * @return mixed
162
     */
163
    public function resetOrphans($type = 'main')
164
    {
165
        $orphans              = (isset($this->orphans[$type])) ? $this->orphans[$type] : [];
166
        $this->orphans[$type] = [];
167
168
        return $orphans;
169
    }
170
171
    /**
172
     * Give orphaned menu items a home.
173
     *
174
     * @param bool $appendOrphans
175
     * @param int  $depth
176
     */
177
    public function placeOrphans(array &$menuItems, $appendOrphans = false, $depth = 1, $type = 'main')
178
    {
179
        foreach ($menuItems as $key => &$items) {
180
            if (isset($this->orphans[$type]) && isset($this->orphans[$type][$key])) {
181
                $priority = (isset($items['priority'])) ? $items['priority'] : 9999;
182
                foreach ($this->orphans[$type][$key] as &$orphan) {
183
                    if (!isset($orphan['extras'])) {
184
                        $orphan['extras'] = [];
185
                    }
186
                    $orphan['extras']['depth'] = $depth;
187
                    if (!isset($orphan['priority'])) {
188
                        $orphan['priority'] = $priority;
189
                    }
190
                }
191
192
                $items['children'] =
193
                    (!isset($items['children']))
194
                    ?
195
                    $this->orphans[$type][$key]
196
                    :
197
                    array_merge($items['children'], $this->orphans[$type][$key]);
198
                unset($this->orphans[$type][$key]);
199
            } elseif (isset($items['children'])) {
200
                foreach ($items['children'] as $subItems) {
201
                    $this->placeOrphans($subItems, false, $depth + 1, $type);
202
                }
203
            }
204
        }
205
206
        // Append orphans that couldn't find a home
207
        if ($appendOrphans && !empty($this->orphans[$type])) {
208
            $menuItems            = array_merge($menuItems, $this->orphans[$type]);
209
            $this->orphans[$type] = [];
210
        }
211
    }
212
213
    /**
214
     * Sort menu items by priority.
215
     *
216
     * @param $menuItems
217
     * @param $defaultPriority
218
     */
219
    public function sortByPriority(&$menuItems, $defaultPriority = 9999)
220
    {
221
        foreach ($menuItems as &$items) {
222
            $parentPriority = (isset($items['priority'])) ? $items['priority'] : $defaultPriority;
223
            if (isset($items['children'])) {
224
                $this->sortByPriority($items['children'], $parentPriority);
225
            }
226
        }
227
228
        uasort(
229
            $menuItems,
230
            function ($a, $b) use ($defaultPriority) {
231
                $ap = (isset($a['priority']) ? (int) $a['priority'] : $defaultPriority);
232
                $bp = (isset($b['priority']) ? (int) $b['priority'] : $defaultPriority);
233
234
                if ($ap == $bp) {
235
                    return 0;
236
                }
237
238
                return ($ap > $bp) ? -1 : 1;
239
            }
240
        );
241
    }
242
243
    /**
244
     * @param $name
245
     *
246
     * @return mixed
247
     */
248
    protected function getParameter($name)
249
    {
250
        return $this->coreParametersHelper->get($name, false);
251
    }
252
253
    /**
254
     * @param string $integrationName
255
     *
256
     * @return bool
257
     */
258
    protected function handleIntegrationChecks($integrationName, array $config)
259
    {
260
        $integration = $this->integrationHelper->getIntegrationObject($integrationName);
261
262
        if (!$integration) {
263
            return false;
264
        }
265
266
        $settings = $integration->getIntegrationSettings();
267
268
        $passChecks = true;
269
270
        foreach ($config as $key => $value) {
271
            switch ($key) {
272
                case 'enabled':
273
                    $passChecks = $settings->getIsPublished() == $value;
274
                    break;
275
                case 'features':
276
                    $supportedFeatures = $settings->getSupportedFeatures();
277
278
                    foreach ($value as $featureName) {
279
                        if (!in_array($featureName, $supportedFeatures)) {
280
                            $passChecks = false;
281
                            break;
282
                        }
283
                    }
284
                    break;
285
            }
286
        }
287
288
        return $passChecks;
289
    }
290
291
    /**
292
     * @param string $name
293
     * @param mixed  $value
294
     *
295
     * @return bool
296
     */
297
    protected function handleParametersChecks($name, $value)
298
    {
299
        return $this->getParameter($name) == $value;
300
    }
301
302
    /**
303
     * @param string $name
304
     * @param mixed  $value
305
     *
306
     * @return bool
307
     */
308
    protected function handleRequestChecks($name, $value)
309
    {
310
        return $this->request->get($name) == $value;
0 ignored issues
show
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

310
        return $this->request->/** @scrutinizer ignore-call */ get($name) == $value;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
311
    }
312
313
    /**
314
     * @param $accessLevel
315
     *
316
     * @return bool
317
     */
318
    protected function handleAccessCheck($accessLevel)
319
    {
320
        switch ($accessLevel) {
321
            case 'admin':
322
                return $this->security->isAdmin();
323
            default:
324
                return $this->security->isGranted($accessLevel, 'MATCH_ONE');
325
        }
326
    }
327
328
    /**
329
     * Handle access check and other checks for menu items.
330
     *
331
     * @return bool Returns false if the item fails the access check or any other checks
332
     */
333
    protected function handleChecks(array $menuItem)
334
    {
335
        if (isset($menuItem['access']) && false === $this->handleAccessCheck($menuItem['access'])) {
336
            return false;
337
        }
338
339
        if (isset($menuItem['checks']) && is_array($menuItem['checks'])) {
340
            foreach ($menuItem['checks'] as $checkGroup => $checkConfig) {
341
                $checkMethod = 'handle'.ucfirst($checkGroup).'Checks';
342
343
                if (!method_exists($this, $checkMethod)) {
344
                    continue;
345
                }
346
347
                foreach ($checkConfig as $name => $value) {
348
                    if (false === $this->$checkMethod($name, $value)) {
349
                        return false;
350
                    }
351
                }
352
            }
353
        }
354
355
        return true;
356
    }
357
}
358