PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.13.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.13.2
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Menu / MenuAbstract.php
matomo / app / core / Menu Last commit date
Group.php 5 years ago MenuAbstract.php 3 years ago MenuAdmin.php 5 years ago MenuTop.php 5 years ago
MenuAbstract.php
375 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik\Menu;
10
11 use Piwik\Cache;
12 use Piwik\Container\StaticContainer;
13 use Piwik\Plugins\SitesManager\API;
14 use Piwik\Singleton;
15 use Piwik\Plugin\Manager as PluginManager;
16
17 /**
18 * Base class for classes that manage one of Piwik's menus.
19 *
20 * There are three menus in Piwik, the main menu, the top menu and the admin menu.
21 * Each menu has a class that manages the menu's content. Each class invokes
22 * a different event to allow plugins to add new menu items.
23 *
24 * @static \Piwik\Menu\MenuAbstract getInstance()
25 */
26 abstract class MenuAbstract extends Singleton
27 {
28
29 protected $menu = null;
30 protected $menuEntries = array();
31 protected $menuEntriesToRemove = array();
32 protected $edits = array();
33 protected $renames = array();
34 protected $orderingApplied = false;
35 protected $menuIcons = array();
36
37 /**
38 * Builds the menu, applies edits, renames
39 * and orders the entries.
40 *
41 * @return array
42 */
43 public function getMenu()
44 {
45 $this->buildMenu();
46 $this->applyEdits();
47 $this->applyRemoves();
48 $this->applyRenames();
49 $this->applyOrdering();
50 return $this->menu;
51 }
52
53 /**
54 * lets you register a menu icon for a certain menu category to replace the default arrow icon.
55 *
56 * @param string $menuName The translation key of a main menu category, eg 'Dashboard_Dashboard'
57 * @param string $iconCssClass The css class name of an icon, eg 'icon-user'
58 */
59 public function registerMenuIcon($menuName, $iconCssClass)
60 {
61 $this->menuIcons[$menuName] = $iconCssClass;
62 }
63
64 /**
65 * Returns a list of available plugin menu instances.
66 *
67 * @return \Piwik\Plugin\Menu[]
68 */
69 protected function getAllMenus()
70 {
71 $cacheId = 'Menus.all';
72 $cache = Cache::getTransientCache();
73
74 if ($cache->contains($cacheId)) {
75 return $cache->fetch($cacheId);
76 }
77
78 $components = PluginManager::getInstance()->findComponents('Menu', 'Piwik\\Plugin\\Menu');
79
80 $menus = array();
81 foreach ($components as $component) {
82 $menus[] = StaticContainer::get($component);
83 }
84
85 $cache->save($cacheId, $menus);
86
87 return $menus;
88 }
89
90 /**
91 * Adds a new entry to the menu.
92 *
93 * @param string $menuName The menu's category name. Can be a translation token.
94 * @param string $subMenuName The menu item's name. Can be a translation token.
95 * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
96 * that can be used to build the URL.
97 * @param int $order The order hint.
98 * @param bool|string $tooltip An optional tooltip to display or false to display the tooltip.
99 * @param bool|string $icon An icon classname, such as "icon-add". Only supported by admin menu
100 * @param bool|string $onclick Will execute the on click handler instead of executing the link. Only supported by admin menu.
101 * @param string $attribute Will add this string as a link attribute.
102 * @param bool|string $help Will display a help icon that will pop a notification with help information.
103 * @since 2.7.0
104 * @api
105 */
106 public function addItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false, $icon = false, $onclick = false, $attribute = false, $help = false)
107 {
108 // make sure the idSite value used is numeric (hack-y fix for #3426)
109 if (isset($url['idSite']) && !is_numeric($url['idSite'])) {
110 $idSites = API::getInstance()->getSitesIdWithAtLeastViewAccess();
111 $url['idSite'] = reset($idSites);
112 }
113
114 $this->menuEntries[] = array(
115 $menuName,
116 $subMenuName,
117 $url,
118 $order,
119 $tooltip,
120 $icon,
121 $onclick,
122 $attribute,
123 $help
124 );
125 }
126
127 /**
128 * Removes an existing entry from the menu.
129 *
130 * @param string $menuName The menu's category name. Can be a translation token.
131 * @param bool|string $subMenuName The menu item's name. Can be a translation token.
132 * @api
133 */
134 public function remove($menuName, $subMenuName = false)
135 {
136 $this->menuEntriesToRemove[] = array(
137 $menuName,
138 $subMenuName
139 );
140 }
141
142 /**
143 * Builds a single menu item
144 *
145 * @param string $menuName
146 * @param string $subMenuName
147 * @param string $url
148 * @param int $order
149 * @param bool|string $tooltip Tooltip to display.
150 */
151 private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false, $icon = false, $onclick = false, $attribute = false, $help = false)
152 {
153 if (!isset($this->menu[$menuName])) {
154 $this->menu[$menuName] = array(
155 '_hasSubmenu' => false,
156 '_order' => $order
157 );
158 }
159
160 if (empty($subMenuName)) {
161 $this->menu[$menuName]['_url'] = $url;
162 $this->menu[$menuName]['_order'] = $order;
163 $this->menu[$menuName]['_name'] = $menuName;
164 $this->menu[$menuName]['_tooltip'] = $tooltip;
165 $this->menu[$menuName]['_attribute'] = $attribute;
166 if (!empty($this->menuIcons[$menuName])) {
167 $this->menu[$menuName]['_icon'] = $this->menuIcons[$menuName];
168 } else {
169 $this->menu[$menuName]['_icon'] = '';
170 }
171 if (!empty($onclick)) {
172 $this->menu[$menuName]['_onclick'] = $onclick;
173 }
174 $this->menu[$menuName]['_help'] = $help ?: '';
175
176 }
177 if (!empty($subMenuName)) {
178 $this->menu[$menuName][$subMenuName]['_url'] = $url;
179 $this->menu[$menuName][$subMenuName]['_order'] = $order;
180 $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName;
181 $this->menu[$menuName][$subMenuName]['_tooltip'] = $tooltip;
182 $this->menu[$menuName][$subMenuName]['_attribute'] = $attribute;
183 $this->menu[$menuName][$subMenuName]['_icon'] = $icon;
184 $this->menu[$menuName][$subMenuName]['_onclick'] = $onclick;
185 $this->menu[$menuName][$subMenuName]['_help'] = $help ?: '';
186 $this->menu[$menuName]['_hasSubmenu'] = true;
187
188 if (!array_key_exists('_tooltip', $this->menu[$menuName])) {
189 $this->menu[$menuName]['_tooltip'] = $tooltip;
190 }
191 }
192 }
193
194 /**
195 * Builds the menu from the $this->menuEntries variable.
196 */
197 private function buildMenu()
198 {
199 foreach ($this->menuEntries as $menuEntry) {
200 $this->buildMenuItem($menuEntry[0], $menuEntry[1], $menuEntry[2], $menuEntry[3], $menuEntry[4],
201 $menuEntry[5], $menuEntry[6], $menuEntry[7], $menuEntry[8]);
202 }
203 }
204
205 /**
206 * Renames a single menu entry.
207 *
208 * @param $mainMenuOriginal
209 * @param $subMenuOriginal
210 * @param $mainMenuRenamed
211 * @param $subMenuRenamed
212 * @api
213 */
214 public function rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed)
215 {
216 $this->renames[] = array($mainMenuOriginal, $subMenuOriginal,
217 $mainMenuRenamed, $subMenuRenamed);
218 }
219
220 /**
221 * Edits a URL of an existing menu entry.
222 *
223 * @param $mainMenuToEdit
224 * @param $subMenuToEdit
225 * @param $newUrl
226 * @api
227 */
228 public function editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
229 {
230 $this->edits[] = array($mainMenuToEdit, $subMenuToEdit, $newUrl);
231 }
232
233 /**
234 * Applies all edits to the menu.
235 */
236 private function applyEdits()
237 {
238 foreach ($this->edits as $edit) {
239 $mainMenuToEdit = $edit[0];
240 $subMenuToEdit = $edit[1];
241 $newUrl = $edit[2];
242
243 if ($subMenuToEdit === null) {
244 if (isset($this->menu[$mainMenuToEdit])) {
245 $menuDataToEdit = &$this->menu[$mainMenuToEdit];
246 } else {
247 $menuDataToEdit = null;
248 }
249 } else {
250 if (isset($this->menu[$mainMenuToEdit][$subMenuToEdit])) {
251 $menuDataToEdit = &$this->menu[$mainMenuToEdit][$subMenuToEdit];
252 } else {
253 $menuDataToEdit = null;
254 }
255 }
256
257 if (empty($menuDataToEdit)) {
258 $this->buildMenuItem($mainMenuToEdit, $subMenuToEdit, $newUrl);
259 } else {
260 $menuDataToEdit['_url'] = $newUrl;
261 }
262 }
263 }
264
265 private function applyRemoves()
266 {
267 foreach ($this->menuEntriesToRemove as $menuToDelete) {
268 if (empty($menuToDelete[1])) {
269 // Delete Main Menu
270 if (isset($this->menu[$menuToDelete[0]])) {
271 unset($this->menu[$menuToDelete[0]]);
272 }
273 } else {
274 // Delete Sub Menu
275 if (isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) {
276 unset($this->menu[$menuToDelete[0]][$menuToDelete[1]]);
277 }
278 }
279 }
280 }
281 /**
282 * Applies renames to the menu.
283 */
284 private function applyRenames()
285 {
286 foreach ($this->renames as $rename) {
287 $mainMenuOriginal = $rename[0];
288 $subMenuOriginal = $rename[1];
289 $mainMenuRenamed = $rename[2];
290 $subMenuRenamed = $rename[3];
291
292 // Are we changing a submenu?
293 if (!empty($subMenuOriginal)) {
294 if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) {
295 $save = $this->menu[$mainMenuOriginal][$subMenuOriginal];
296 $save['_name'] = $subMenuRenamed;
297 unset($this->menu[$mainMenuOriginal][$subMenuOriginal]);
298 $this->menu[$mainMenuRenamed][$subMenuRenamed] = $save;
299 }
300 } // Changing a first-level element
301 elseif (isset($this->menu[$mainMenuOriginal])) {
302 $save = $this->menu[$mainMenuOriginal];
303 $save['_name'] = $mainMenuRenamed;
304 unset($this->menu[$mainMenuOriginal]);
305 $this->menu[$mainMenuRenamed] = $save;
306 }
307 }
308 }
309
310 /**
311 * Orders the menu according to their order.
312 */
313 private function applyOrdering()
314 {
315 if (empty($this->menu)
316 || $this->orderingApplied
317 ) {
318 return;
319 }
320
321 uasort($this->menu, array($this, 'menuCompare'));
322 foreach ($this->menu as $key => &$element) {
323 if (is_null($element)) {
324 unset($this->menu[$key]);
325 } elseif ($element['_hasSubmenu']) {
326 uasort($element, array($this, 'menuCompare'));
327 }
328 }
329
330 $this->orderingApplied = true;
331 }
332
333 /**
334 * Compares two menu entries. Used for ordering.
335 *
336 * @param array $itemOne
337 * @param array $itemTwo
338 * @return boolean
339 */
340 protected function menuCompare($itemOne, $itemTwo)
341 {
342 if (!is_array($itemOne) && !is_array($itemTwo)) {
343 return 0;
344 }
345
346 if (!is_array($itemOne) && is_array($itemTwo)) {
347 return -1;
348 }
349
350 if (is_array($itemOne) && !is_array($itemTwo)) {
351 return 1;
352 }
353
354 if (!isset($itemOne['_order']) && !isset($itemTwo['_order'])) {
355 return 0;
356 }
357
358 if (!isset($itemOne['_order']) && isset($itemTwo['_order'])) {
359 return -1;
360 }
361
362 if (isset($itemOne['_order']) && !isset($itemTwo['_order'])) {
363 return 1;
364 }
365
366 if ($itemOne['_order'] == $itemTwo['_order']) {
367 return strcmp(
368 $itemOne['_name'] ?? '',
369 $itemTwo['_name'] ?? '');
370 }
371
372 return ($itemOne['_order'] < $itemTwo['_order']) ? -1 : 1;
373 }
374 }
375