Completed
Pull Request — development (#3050)
by John
08:57
created

Menu::processMenuData()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 0
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This class contains a standard way of displaying side/drop down menus.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 * license:   BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1
15
 *
16
 */
17
18
declare(strict_types = 1);
19
20
namespace ElkArte\Menu;
21
22
use HttpReq;
23
24
/**
25
 * This class implements a standard way of creating menus
26
 */
27
class Menu
28
{
29
	/**
30
	 * Instance of HttpReq
31
	 * @var HttpReq
32
	 */
33
	protected $req;
34
35
	/**
36
	 * Will hold the created $context
37
	 * @var array
38
	 */
39
	protected $menu_context = [];
40
41
	/**
42
	 * Used for profile menu for own / any
43
	 * @var string
44
	 */
45
	protected $permission_set;
46
47
	/**
48
	 * If we found the menu item selected
49
	 * @var bool
50
	 */
51
	protected $found_section = false;
52
53
	/**
54
	 * If we can't find the selection, we pick for them
55
	 * @var string
56
	 */
57
	protected $current_area = '';
58
59
	/**
60
	 * The current subaction of the system
61
	 * @var string
62
	 */
63
	protected $current_subaction = '';
64
65
	/**
66
	 * Will hold the selected menu data that is returned to the caller
67
	 * @var array
68
	 */
69
	private $include_data = [];
70
71
	/**
72
	 * Unique menu number
73
	 * @var int
74
	 */
75
	private $max_menu_id;
76
77
	/**
78
	 * Holds menu options set by AddOptions
79
	 * @var array
80
	 */
81
	public $menuOptions = [];
82
83
	/**
84
	 * Holds menu definition structure set by AddAreas
85
	 * @var array
86
	 */
87
	public $menuData = [];
88
89
	/**
90
	 * Initial processing for the menu
91
	 */
92
	public function __construct(HttpReq $req = null)
93
	{
94
		global $context;
95
96
		// Access to post/get data
97
		$this->req = $req ?: HttpReq::instance();
98
99
		// Every menu gets a unique ID, these are shown in first in, first out order.
100
		$this->max_menu_id = ($context['max_menu_id'] ?? 0) + 1;
101
102
		// This will be all the data for this menu
103
		$this->menu_context = [];
104
105
		// This is necessary only in profile (at least for the core), but we do it always because it's easier
106
		$this->permission_set = !empty($context['user']['is_owner']) ? 'own' : 'any';
107
108
		// We may have a current subaction
109
		$this->current_subaction = $context['current_subaction'] ?? null;
110
	}
111
112
	/**
113
	 * Create a menu
114
	 *
115
	 * @return array
116
	 * @throws \Elk_Exception
117
	 */
118
	public function prepareMenu(): array
119
	{
120
		// Process the menu Options
121
		$this->processMenuOptions();
122
123
		// Check the menus urls
124
		$this->setBaseUrl();
125
126
		// Process the menu Data
127
		$this->processMenuData();
128
129
		// Check the menus urls
130
		$this->setActiveButtons();
131
132
		// Make sure we created some awesome sauce
133
		if (!$this->validateData())
134
		{
135
			throw new \Elk_Exception('no_access', false);
136
		}
137
138
		// Finally - return information on the selected item.
139
		return $this->include_data + [
140
			'current_action' => $this->menu_context['current_action'],
141
			'current_area' => $this->current_area,
142
			'current_section' => !empty($this->menu_context['current_section']) ? $this->menu_context['current_section'] : '',
143
			'current_subsection' => $this->current_subaction,
144
		];
145
146
	}
147
148
	/**
149
	 * Prepares tabs for the template.
150
	 *
151
	 * This should be called after the area is dispatched, because areas
152
	 * are usually in their own file. Those files, once dispatched to, hold
153
	 * some data for the tabs which must be specially combined with subaction
154
	 * data for everything to work properly.
155
	 *
156
	 * Seems complicated, yes.
157
	 */
158
	public function prepareTabData(): void
159
	{
160
		global $context;
161
162
		// Handy shortcut.
163
		$tab_context = &$context['menu_data_' . $this->max_menu_id]['tab_data'];
164
165
		// Tabs are really just subactions.
166
		if (isset($tab_context['tabs'], $this->menu_context['sections'][$this->menu_context['current_section']]['areas'][$this->current_area]['subsections']))
167
		{
168
			$tab_context['tabs'] = array_replace_recursive(
169
				$tab_context['tabs'],
170
				$this->menu_context['sections'][$this->menu_context['current_section']]['areas'][$this->current_area]['subsections']
171
			);
172
173
			// Has it been deemed selected?
174
			$tab_context = array_merge($tab_context, $tab_context['tabs'][$this->current_subaction]);
175
		}
176
	}
177
178
	/**
179
	 * Performs a sanity check that a menu was created successfully
180
	 *
181
	 *   - If it fails to find valid data, will reset max_menu_id and any menu context created
182
	 *
183
	 * @return bool
184
	 */
185
	private function validateData(): bool
186
	{
187
		if (empty($this->menu_context['sections']))
188
		{
189
			return false;
190
		}
191
192
		// Check we had something - for sanity sake.
193
		return !empty($this->include_data);
194
	}
195
196
	/**
197
	 * Process the array of MenuOptions passed to the class
198
	 */
199
	protected function processMenuOptions(): void
200
	{
201
		global $context;
202
203
		// What is the general action of this menu i.e. $scripturl?action=XYZ.
204
		$this->menu_context['current_action'] = $this->menuOptions['action'] ?? $context['current_action'];
205
206
		// What is the current area selected?
207
		$this->current_area = $this->req->getQuery('area', 'trim|strval', $this->menuOptions['area'] ?? '');
208
209
		$this->buildAdditionalParams();
210
		$this->buildTemplateVars();
211
	}
212
213
	/**
214
	 * Build the menuOption additional parameters for use in the url
215
	 */
216
	private function buildAdditionalParams(): void
217
	{
218
		global $context;
219
220
		$this->menu_context['extra_parameters'] = '';
221
222
		if (!empty($this->menuOptions['extra_url_parameters']))
223
		{
224
			foreach ($this->menuOptions['extra_url_parameters'] as $key => $value)
225
			{
226
				$this->menu_context['extra_parameters'] .= ';' . $key . '=' . $value;
227
			}
228
		}
229
230
		// Only include the session ID in the URL if it's strictly necessary.
231
		if (empty($this->menuOptions['disable_url_session_check']))
232
		{
233
			$this->menu_context['extra_parameters'] .= ';' . $context['session_var'] . '=' . $context['session_id'];
234
		}
235
	}
236
237
	/**
238
	 * Process the menuData array passed to the class
239
	 *
240
	 *   - Only processes areas that are enabled and that the user has permissions
241
	 */
242
	protected function processMenuData(): void
243
	{
244
		// Now setup the context correctly.
245
		foreach ($this->menuData as $section_id => $section)
246
		{
247
			// Is this section enabled? and do they have permissions?
248
			if ($section->isEnabled() && $this->checkPermissions($section))
249
			{
250
				$this->setSectionContext($section_id, $section);
251
252
				// Process this menu section
253
				$this->processSectionAreas($section_id, $section);
254
			}
255
		}
256
	}
257
258
	/**
259
	 * Determines if the user has the permissions to access the section/area
260
	 *
261
	 * If said item did not provide any permission to check, full
262
	 * unfettered access is assumed.
263
	 *
264
	 * The profile areas are a bit different in that each permission is
265
	 * divided into two sets: "own" for owner and "any" for everyone else.
266
	 *
267
	 * @param MenuItem $obj area or section being checked
268
	 *
269
	 * @return bool
270
	 */
271
	private function checkPermissions(MenuItem $obj): bool
272
	{
273
		if (!empty($obj->getPermission()))
274
		{
275
			// The profile menu has slightly different permissions
276
			if (is_array($obj->getPermission()) && isset($obj->getPermission()['own'], $obj->getPermission()['any']))
277
			{
278
				return allowedTo($obj->getPermission()[$this->permission_set]);
279
			}
280
281
			return allowedTo($obj->getPermission());
282
		}
283
284
		return true;
285
	}
286
287
	/**
288
	 * Checks if the area has a label or not
289
	 *
290
	 * @return bool
291
	 */
292
	private function areaHasLabel(string $area_id, MenuArea $area): bool
293
	{
294
		global $txt;
295
296
		return !empty($area->getLabel()) || isset($txt[$area_id]);
297
	}
298
299
	/**
300
	 * Main processing for creating the menu items for all sections
301
	 *
302
	 * @param string      $section_id
303
	 * @param MenuSection $section
304
	 */
305
	protected function processSectionAreas(string $section_id, MenuSection $section): void
306
	{
307
		// Now we cycle through the sections to pick the right area.
308
		foreach ($section->getAreas() as $area_id => $area)
309
		{
310
			// Is the area enabled, Does the user have permission and it has some form of a name
311
			if ($area->isEnabled() && $this->checkPermissions($area) && $this->areaHasLabel($area_id, $area))
312
			{
313
				// Make sure we have a valid current area
314
				$this->setFirstAreaCurrent($section_id, $area_id, $area);
315
316
				// If this is hidden from view don't do the rest.
317
				if (!$area->isHidden())
318
				{
319
					// First time this section?
320
					$this->setAreaContext($section_id, $area_id, $area);
321
322
					// Maybe a custom url
323
					$this->setAreaUrl($section_id, $area_id, $area);
324
325
					// Even a little icon
326
					$this->setAreaIcon($section_id, $area_id, $area);
327
328
					// Did it have subsections?
329
					$this->processAreaSubsections($section_id, $area_id, $area);
330
				}
331
332
				// Is this the current section?
333
				$this->checkCurrentSection($section_id, $area_id, $area);
334
			}
335
		}
336
	}
337
338
	/**
339
	 * Checks the menu item to see if it is the currently selected one
340
	 *
341
	 * @param string   $section_id
342
	 * @param string   $area_id
343
	 * @param MenuArea $area
344
	 */
345
	private function checkCurrentSection(string $section_id, string $area_id, MenuArea $area): void
346
	{
347
		// Is this the current section?
348
		if ($this->current_area == $area_id && !$this->found_section)
349
		{
350
			$this->setAreaCurrent($section_id, $area_id, $area);
351
352
			// Only do this once, m'kay?
353
			$this->found_section = true;
354
		}
355
	}
356
357
	/**
358
	 * @param string   $section_id
359
	 * @param string   $area_id
360
	 * @param MenuArea $area
361
	 */
362
	private function setFirstAreaCurrent(string $section_id, string $area_id, MenuArea $area): void
363
	{
364
		// If we don't have an area then the first valid one is our choice.
365
		if (empty($this->current_area))
366
		{
367
			$this->setAreaCurrent($section_id, $area_id, $area);
368
		}
369
	}
370
371
	/**
372
	 * Simply sets the current area
373
	 *
374
	 * @param string   $section_id
375
	 * @param string   $area_id
376
	 * @param MenuArea $area
377
	 */
378
	private function setAreaCurrent(string $section_id, string $area_id, MenuArea $area): void
379
	{
380
		// Update the context if required - as we can have areas pretending to be others. ;)
381
		$this->menu_context['current_section'] = $section_id;
382
		$this->current_area = $area->getSelect() ?: $area_id;
383
		$this->include_data = $area->toArray();
384
	}
385
386
	/**
387
	 * @param MenuItem $obj
388
	 * @param integer  $idx
389
	 *
390
	 * @return string
391
	 */
392
	private function parseCounter(MenuItem $obj, int $idx): string
393
	{
394
		global $settings;
395
396
		$counter = '';
397
		if (isset($this->menuOptions['counters']) && !empty($this->menuOptions['counters'][$obj->getCounter()]))
398
		{
399
			$counter =
400
				sprintf($settings['menu_numeric_notice'][$idx], $this->menuOptions['counters'][$obj->getCounter()]);
401
		}
402
403
		return $counter;
404
	}
405
406
	/**
407
	 * Sets the various section ID items
408
	 *
409
	 * What it does:
410
	 *   - If the ID is not set, sets it and sets the section title
411
	 *   - Sets the section title
412
	 *
413
	 * @param string      $section_id
414
	 * @param MenuSection $section
415
	 */
416
	private function setSectionContext(string $section_id, MenuSection $section): void
417
	{
418
		global $txt;
419
420
		$this->menu_context['sections'][$section_id] = [
421
			'id' => $section_id,
422
			'label' => ($section->getLabel() ?: $txt[$section_id]) . $this->parseCounter($section, 0),
423
			'url' => $this->menu_context['base_url'] . $this->menu_context['extra_parameters'],
424
		];
425
	}
426
427
	/**
428
	 * Sets the various area items
429
	 *
430
	 * What it does:
431
	 *   - If the ID is not set, sets it and sets the area title
432
	 *   - Sets the area title
433
	 *
434
	 * @param string   $section_id
435
	 * @param string   $area_id
436
	 * @param MenuArea $area
437
	 */
438
	private function setAreaContext(string $section_id, string $area_id, MenuArea $area): void
439
	{
440
		global $txt;
441
442
		$this->menu_context['sections'][$section_id]['areas'][$area_id] = [
443
			'label' => ($area->getLabel() ?: $txt[$area_id]) . $this->parseCounter($area, 1),
444
		];
445
	}
446
447
	/**
448
	 * Set the URL for the menu item
449
	 *
450
	 * @param string   $section_id
451
	 * @param string   $area_id
452
	 * @param MenuArea $area
453
	 */
454
	private function setAreaUrl(string $section_id, string $area_id, MenuArea $area): void
455
	{
456
		$area->setUrl(
457
			$this->menu_context['sections'][$section_id]['areas'][$area_id]['url'] =
458
				$area->getUrl(
459
				) ?: $this->menu_context['base_url'] . ';area=' . $area_id . $this->menu_context['extra_parameters']
460
		);
461
	}
462
463
	/**
464
	 * Set the menu icon
465
	 *
466
	 * @param string   $section_id
467
	 * @param string   $area_id
468
	 * @param MenuArea $area
469
	 */
470
	private function setAreaIcon(string $section_id, string $area_id, MenuArea $area): void
471
	{
472
		global $settings;
473
474
		// Work out where we should get our menu images from.
475
		$image_path = file_exists($settings['theme_dir'] . '/images/admin/change_menu.png')
476
			? $settings['images_url'] . '/admin'
477
			: $settings['default_images_url'] . '/admin';
478
479
		// Does this area have its own icon?
480
		if (!empty($area->getIcon()))
481
		{
482
			$this->menu_context['sections'][$section_id]['areas'][$area_id]['icon'] =
483
				'<img ' . (!empty($area->getClass()) ? 'class="' . $area->getClass(
484
					) . '" ' : 'style="background: none"') . ' src="' . $image_path . '/' . $area->getIcon(
485
				) . '" alt="" />&nbsp;&nbsp;';
486
		}
487
		else
488
		{
489
			$this->menu_context['sections'][$section_id]['areas'][$area_id]['icon'] = '';
490
		}
491
	}
492
493
	/**
494
	 * Processes all of the subsections for a menu item
495
	 *
496
	 * @param string   $section_id
497
	 * @param string   $area_id
498
	 * @param MenuArea $area
499
	 */
500
	protected function processAreaSubsections(string $section_id, string $area_id, MenuArea $area): void
501
	{
502
		// If there are subsections for this menu item
503
		if (!empty($area->getSubsections()))
504
		{
505
			$this->menu_context['sections'][$section_id]['areas'][$area_id]['subsections'] = [];
506
			$first_sub_id = '';
507
508
			// For each subsection process the options
509
			foreach ($area->getSubsections() as $sub_id => $sub)
510
			{
511
				if ($this->checkPermissions($sub) && $sub->isEnabled())
512
				{
513
					$this->menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$sub_id] = [
514
						'label' => $sub->getLabel() . $this->parseCounter($sub, 2),
515
					];
516
517
					$this->setSubsSectionUrl($section_id, $area_id, $sub_id, $sub);
518
519
					if ($this->current_area == $area_id)
520
					{
521
						if (empty($first_sub_id))
522
						{
523
							$first_sub_id = $sub_id;
524
						}
525
						$this->setCurrentSubSection($sub_id, $sub);
526
					}
527
				}
528
				else
529
				{
530
					// Mark it as disabled...
531
					$this->menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$sub_id]['disabled'] =
532
						true;
533
				}
534
			}
535
536
			// Is this the current subsection?
537
			if (empty($this->current_subaction))
538
			{
539
				$this->current_subaction = $first_sub_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $first_sub_id can also be of type integer. However, the property $current_subaction is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
540
			}
541
		}
542
	}
543
544
	/**
545
	 * @param string         $section_id
546
	 * @param string         $area_id
547
	 * @param string         $sub_id
548
	 * @param MenuSubsection $sub
549
	 */
550
	private function setSubsSectionUrl(string $section_id, string $area_id, string $sub_id, MenuSubsection $sub): void
551
	{
552
		$sub->setUrl(
553
			$this->menu_context['sections'][$section_id]['areas'][$area_id]['subsections'][$sub_id]['url'] =
554
				$sub->getUrl(
555
				) ?: $this->menu_context['base_url'] . ';area=' . $area_id . ';sa=' . $sub_id . $this->menu_context['extra_parameters']
556
		);
557
	}
558
559
	/**
560
	 * Set the current subsection
561
	 *
562
	 * @param string         $sub_id
563
	 * @param MenuSubsection $sub
564
	 */
565
	private function setCurrentSubSection(string $sub_id, MenuSubsection $sub): void
566
	{
567
		// Is this the current subsection?
568
		$sub_id_check = $this->req->getQuery('sa', 'trim', null);
569
		if (
570
			$sub_id_check == $sub_id
571
			|| in_array($sub_id_check, $sub->getActive(), true)
572
			|| empty($this->current_subaction) && $sub->isDefault()
573
		)
574
		{
575
			$this->current_subaction = $sub_id;
576
		}
577
	}
578
579
	/**
580
	 * Checks and updates base and section urls
581
	 */
582
	private function setBaseUrl(): void
583
	{
584
		global $scripturl;
585
586
		// Should we use a custom base url, or use the default?
587
		$this->menu_context['base_url'] =
588
			$this->menuOptions['base_url'] ?? $scripturl . '?action=' . $this->menu_context['current_action'];
589
	}
590
591
	/**
592
	 * Checks and updates base and section urls
593
	 */
594
	private function setActiveButtons(): void
595
	{
596
		// If there are sections quickly goes through all the sections to check if the base menu has an url
597
		if (!empty($this->menu_context['current_section']))
598
		{
599
			$this->menu_context['sections'][$this->menu_context['current_section']]['selected'] = true;
600
			$this->menu_context['sections'][$this->menu_context['current_section']]['areas'][$this->current_area]['selected'] =
601
				true;
602
603
			if (!empty($this->menu_context['sections'][$this->menu_context['current_section']]['areas'][$this->current_area]['subsections'][$this->current_subaction]))
604
			{
605
				$this->menu_context['sections'][$this->menu_context['current_section']]['areas'][$this->current_area]['subsections'][$this->current_subaction]['selected'] =
606
					true;
607
			}
608
		}
609
	}
610
611
	/**
612
	 * Add the base menu options for this menu
613
	 *
614
	 * @param array $menuOptions an array of options that can be used to override some default behaviours.
615
	 *                           It can accept the following indexes:
616
	 *                           - action                    => overrides the default action
617
	 *                           - current_area              => overrides the current area
618
	 *                           - extra_url_parameters      => an array or pairs or parameters to be added to the url
619
	 *                           - disable_url_session_check => (boolean) if true the session var/id are omitted from
620
	 *                           the url
621
	 *                           - base_url                  => an alternative base url
622
	 *                           - menu_type                 => alternative menu types?
623
	 *                           - can_toggle_drop_down      => (boolean) if the menu can "toggle"
624
	 *                           - template_name             => an alternative template to load (instead of Generic)
625
	 *                           - layer_name                => alternative layer name for the menu
626
	 *                           - hook                      => hook name to call integrate_ . 'hook name' . '_areas'
627
	 *                           - default_include_dir       => directory to include for function support
628
	 */
629
	public function addOptions(array $menuOptions): void
630
	{
631
		$this->menuOptions = array_merge($this->menuOptions, $menuOptions);
632
	}
633
634
	/**
635
	 * @param string      $id
636
	 * @param MenuSection $section
637
	 *
638
	 * @return $this
639
	 */
640
	public function addSection(string $id, MenuSection $section): Menu
641
	{
642
		$this->menuData[$id] = $section;
643
644
		return $this;
645
	}
646
647
	private function buildTemplateVars(): void
648
	{
649
		global $user_info, $options;
650
651
		if (empty($this->menuOptions['menu_type']))
652
		{
653
			$this->menuOptions['menu_type'] = empty($options['use_sidebar_menu']) ? 'dropdown' : 'sidebar';
654
		}
655
		$this->menuOptions['can_toggle_drop_down'] =
656
			!$user_info['is_guest'] || !empty($this->menuOptions['can_toggle_drop_down']);
657
658
		$this->menuOptions['template_name'] = $this->menuOptions['template_name'] ?? 'GenericMenu';
659
		$this->menuOptions['layer_name'] =
660
			($this->menuOptions['layer_name'] ?? 'generic_menu') . '_' . $this->menuOptions['menu_type'];
661
	}
662
663
	/**
664
	 * Finalizes items so the computed menu can be used
665
	 *
666
	 * What it does:
667
	 *   - Sets the menu layer in the template stack
668
	 *   - Loads context with the computed menu context
669
	 *   - Sets current subaction and current max menu id
670
	 */
671
	public function setContext(): void
672
	{
673
		global $context;
674
675
		// Almost there - load the template and add to the template layers.
676
		theme()->getTemplates()->load($this->menuOptions['template_name']);
677
		theme()->getLayers()->add($this->menuOptions['layer_name']);
678
679
		// Set it all to context for template consumption
680
		$this->menu_context['layer_name'] = $this->menuOptions['layer_name'];
681
		$this->menu_context['can_toggle_drop_down'] = $this->menuOptions['can_toggle_drop_down'];
682
		$context['max_menu_id'] = $this->max_menu_id;
683
		$context['current_subaction'] = $this->current_subaction;
684
		$this->menu_context['current_subsection'] = $this->current_subaction;
685
		$this->menu_context['current_area'] = $this->current_area;
686
		$context['menu_data_' . $this->max_menu_id] = $this->menu_context;
687
	}
688
689
	/**
690
	 * Delete a menu.
691
	 *
692
	 * Checks to see if this menu been loaded into context
693
	 * and, if so, resets $context['max_menu_id'] back to the
694
	 * last known menu (if any) and remove the template layer
695
	 * if there aren't any other known menus.
696
	 */
697
	public function destroy(): void
698
	{
699
		global $context;
700
701
		// Has this menu been loaded into context?
702
		if (isset($context[$menu_name = 'menu_data_' . $this->max_menu_id]))
703
		{
704
			// Decrement the pointer if this is the final menu in the series.
705 View Code Duplication
			if ($this->max_menu_id == $context['max_menu_id'])
706
			{
707
				$context['max_menu_id'] = max($context['max_menu_id'] - 1, 0);
708
			}
709
710
			// Remove the template layer if this was the only menu left.
711
			if ($context['max_menu_id'] == 0)
712
			{
713
				theme()->getLayers()->remove($context[$menu_name]['layer_name']);
714
			}
715
716
			unset($context[$menu_name]);
717
		}
718
	}
719
}
720