Apply PSR-12 code style (#3886)
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / menu / item / MenuItemNodeTree.class.php
1 <?php
2
3 namespace wcf\data\menu\item;
4
5 use wcf\system\page\PageLocationManager;
6 use wcf\system\request\RequestHandler;
7
8 /**
9 * Represents a menu item node tree.
10 *
11 * @author Marcel Werk
12 * @copyright 2001-2019 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\Data\Menu\Item
15 * @since 3.0
16 */
17 class MenuItemNodeTree
18 {
19 /**
20 * if `false`, individual menu item visibility will not be checked
21 * @var bool
22 */
23 public $checkVisibility;
24
25 /**
26 * menu id
27 * @var int
28 */
29 public $menuID;
30
31 /**
32 * list of menu items
33 * @var MenuItem[]
34 */
35 public $menuItems = [];
36
37 /**
38 * menu item structure
39 * @var mixed[]
40 */
41 public $menuItemStructure = [];
42
43 /**
44 * root node
45 * @var MenuItemNode
46 */
47 public $node;
48
49 /**
50 * number of visible items
51 * @var int
52 */
53 protected $visibleItemCount = 0;
54
55 /**
56 * Creates a new MenuItemNodeTree object.
57 *
58 * @param int $menuID menu id
59 * @param MenuItemList $menuItemList optional object to be provided when building the tree from cache
60 * @param bool $checkVisibility if `false`, individual menu item visibility will not be checked
61 */
62 public function __construct($menuID, ?MenuItemList $menuItemList = null, $checkVisibility = true)
63 {
64 $this->menuID = $menuID;
65 $this->checkVisibility = $checkVisibility;
66
67 // load menu items
68 if ($menuItemList === null) {
69 $menuItemList = new MenuItemList();
70 $menuItemList->getConditionBuilder()->add('menu_item.menuID = ?', [$this->menuID]);
71 $menuItemList->sqlOrderBy = "menu_item.showOrder";
72 $menuItemList->readObjects();
73 }
74
75 // find possible active menu items
76 $activeMenuItems = [];
77
78 if (!RequestHandler::getInstance()->isACPRequest()) {
79 $possibleLocations = PageLocationManager::getInstance()->getLocations();
80 $length = \count($possibleLocations);
81 for ($i = 0; $i < $length; $i++) {
82 foreach ($menuItemList as $menuItem) {
83 if ($menuItem->pageID == $possibleLocations[$i]['pageID'] && $menuItem->pageObjectID == $possibleLocations[$i]['pageObjectID']) {
84 if (!isset($activeMenuItems[$i])) {
85 $activeMenuItems[$i] = [];
86 }
87
88 $activeMenuItems[$i][] = $menuItem->itemID;
89 }
90 }
91 }
92 }
93
94 // build menu structure
95 foreach ($menuItemList as $menuItem) {
96 $this->menuItems[$menuItem->itemID] = $menuItem;
97
98 if (!isset($this->menuItemStructure[$menuItem->parentItemID])) {
99 $this->menuItemStructure[$menuItem->parentItemID] = [];
100 }
101 $this->menuItemStructure[$menuItem->parentItemID][] = $menuItem->itemID;
102 }
103
104 // generate node tree
105 $this->node = new MenuItemNode();
106 $this->node->setChildren($this->generateNodeTree(null, $this->node));
107
108 // mark nodes as active
109 if (!empty($activeMenuItems)) {
110 $nodeList = $this->getNodeList();
111 foreach ($activeMenuItems as $itemIDs) {
112 for ($i = 0, $length = \count($itemIDs); $i < $length; $i++) {
113 /** @var MenuItemNode $node */
114 foreach ($nodeList as $node) {
115 if ($node->itemID == $itemIDs[$i]) {
116 $node->setIsActive();
117
118 // only one effective item can be marked as active, use the first
119 // occurrence with the highest priority and ignore everything else
120 return;
121 }
122 }
123 }
124 }
125 }
126 }
127
128 /**
129 * Generates the node tree recursively.
130 *
131 * @param int $parentID parent menu item id
132 * @param MenuItemNode $parentNode parent menu item object
133 * @return MenuItemNode[] nested menu item tree
134 */
135 protected function generateNodeTree($parentID = null, ?MenuItemNode $parentNode = null)
136 {
137 $nodes = [];
138
139 $itemIDs = ($this->menuItemStructure[$parentID] ?? []);
140 foreach ($itemIDs as $itemID) {
141 $menuItem = $this->menuItems[$itemID];
142
143 if ($this->checkVisibility && !$menuItem->isVisible()) {
144 continue;
145 }
146
147 $node = new MenuItemNode(
148 $parentNode,
149 $menuItem,
150 ($parentNode !== null ? ($parentNode->getDepth() + 1) : 0)
151 );
152 $nodes[] = $node;
153
154 // get children
155 $node->setChildren($this->generateNodeTree($itemID, $node));
156
157 // increase item counter
158 $this->visibleItemCount++;
159 }
160
161 return $nodes;
162 }
163
164 /**
165 * Returns the menu item node tree.
166 *
167 * @return MenuItemNode[]
168 */
169 public function getNodeTree()
170 {
171 return $this->node->getChildren();
172 }
173
174 /**
175 * Returns the iterable node list.
176 *
177 * @return \RecursiveIteratorIterator
178 */
179 public function getNodeList()
180 {
181 return new \RecursiveIteratorIterator($this->node, \RecursiveIteratorIterator::SELF_FIRST);
182 }
183
184 /**
185 * Returns the number of visible items.
186 *
187 * @return int
188 */
189 public function getVisibleItemCount()
190 {
191 return $this->visibleItemCount;
192 }
193 }