PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.8.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.8.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 1 year ago MenuAbstract.php 3 months ago MenuAdmin.php 3 months ago MenuTop.php 1 year ago
MenuAbstract.php
324 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
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 * Base class for classes that manage one of Piwik's menus.
18 *
19 * There are three menus in Piwik, the main menu, the top menu and the admin menu.
20 * Each menu has a class that manages the menu's content. Each class invokes
21 * a different event to allow plugins to add new menu items.
22 *
23 * @static \Piwik\Menu\MenuAbstract getInstance()
24 */
25 abstract class MenuAbstract extends Singleton
26 {
27 protected $menu = null;
28 protected $menuEntries = array();
29 protected $menuEntriesToRemove = array();
30 protected $edits = array();
31 protected $renames = array();
32 protected $orderingApplied = \false;
33 protected $menuIcons = array();
34 /**
35 * Builds the menu, applies edits, renames
36 * and orders the entries.
37 *
38 * @return array
39 */
40 public function getMenu()
41 {
42 $this->buildMenu();
43 $this->applyEdits();
44 $this->applyRemoves();
45 $this->applyRenames();
46 $this->applyOrdering();
47 return $this->menu;
48 }
49 /**
50 * lets you register a menu icon for a certain menu category to replace the default arrow icon.
51 *
52 * @param string $menuName The translation key of a main menu category, eg 'Dashboard_Dashboard'
53 * @param string $iconCssClass The css class name of an icon, eg 'icon-user'
54 */
55 public function registerMenuIcon($menuName, $iconCssClass)
56 {
57 $this->menuIcons[$menuName] = $iconCssClass;
58 }
59 /**
60 * Returns a list of available plugin menu instances.
61 *
62 * @return \Piwik\Plugin\Menu[]
63 */
64 protected function getAllMenus()
65 {
66 $cacheId = 'Menus.all';
67 $cache = Cache::getTransientCache();
68 if ($cache->contains($cacheId)) {
69 return $cache->fetch($cacheId);
70 }
71 $components = PluginManager::getInstance()->findComponents('Menu', 'Piwik\\Plugin\\Menu');
72 $menus = array();
73 foreach ($components as $component) {
74 $menus[] = StaticContainer::get($component);
75 }
76 $cache->save($cacheId, $menus);
77 return $menus;
78 }
79 /**
80 * Adds a new entry to the menu.
81 *
82 * @param string $menuName The menu's category name. Can be a translation token.
83 * @param null|string $subMenuName The menu item's name. Can be a translation token.
84 * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
85 * that can be used to build the URL.
86 * @param int $order The order hint.
87 * @param bool|string $tooltip An optional tooltip to display or false to display the tooltip.
88 * @param bool|string $icon An icon classname, such as "icon-add". Only supported by admin menu
89 * @param bool|string $onclick Will execute the on click handler instead of executing the link. Only supported by admin menu.
90 * @param bool|string $attribute Will add this string as a link attribute.
91 * @param bool|string $help Will display a help icon that will pop a notification with help information.
92 * @param int $badgeCount If non-zero then a badge will be overlaid on the icon showing the provided count
93 * @param string $cssClass If a string is provided, it will be added as an extra CSS class to the menu item
94 * @since 2.7.0
95 * @api
96 */
97 public function addItem(string $menuName, ?string $subMenuName, $url, int $order = 50, $tooltip = \false, $icon = \false, $onclick = \false, $attribute = \false, $help = \false, int $badgeCount = 0, string $cssClass = '')
98 {
99 // make sure the idSite value used is numeric (hack-y fix for #3426)
100 if (isset($url['idSite']) && !is_numeric($url['idSite'])) {
101 $idSites = API::getInstance()->getSitesIdWithAtLeastViewAccess();
102 $url['idSite'] = reset($idSites);
103 }
104 $this->menuEntries[] = [$menuName, $subMenuName, $url, $order, $tooltip, $icon, $onclick, $attribute, $help, $badgeCount, $cssClass];
105 }
106 /**
107 * Removes an existing entry from the menu.
108 *
109 * @param string $menuName The menu's category name. Can be a translation token.
110 * @param bool|string $subMenuName The menu item's name. Can be a translation token.
111 * @api
112 */
113 public function remove($menuName, $subMenuName = \false)
114 {
115 $this->menuEntriesToRemove[] = array($menuName, $subMenuName);
116 }
117 /**
118 * Builds a single menu item
119 *
120 * @param string|array $url
121 * @param bool|string $tooltip Tooltip to display.
122 * @param bool|string $icon
123 * @param bool|string $onclick
124 * @param bool|string $attribute
125 * @param bool|string $help
126 */
127 private function buildMenuItem(string $menuName, ?string $subMenuName, $url, int $order = 50, $tooltip = \false, $icon = \false, $onclick = \false, $attribute = \false, $help = \false, int $badgeCount = 0, string $cssClass = '')
128 {
129 if (!isset($this->menu[$menuName])) {
130 $this->menu[$menuName] = array('_hasSubmenu' => \false, '_order' => $order);
131 }
132 if (empty($subMenuName)) {
133 $this->menu[$menuName]['_url'] = $url;
134 $this->menu[$menuName]['_order'] = $order;
135 $this->menu[$menuName]['_name'] = $menuName;
136 $this->menu[$menuName]['_tooltip'] = $tooltip;
137 $this->menu[$menuName]['_attribute'] = $attribute;
138 if (!empty($this->menuIcons[$menuName])) {
139 $this->menu[$menuName]['_icon'] = $this->menuIcons[$menuName];
140 } else {
141 $this->menu[$menuName]['_icon'] = '';
142 }
143 if (!empty($onclick)) {
144 $this->menu[$menuName]['_onclick'] = $onclick;
145 }
146 $this->menu[$menuName]['_help'] = $help ?: '';
147 $this->menu[$menuName]['_badgecount'] = $badgeCount;
148 $this->menu[$menuName]['_cssClass'] = $cssClass;
149 }
150 if (!empty($subMenuName)) {
151 $this->menu[$menuName][$subMenuName]['_url'] = $url;
152 $this->menu[$menuName][$subMenuName]['_order'] = $order;
153 $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName;
154 $this->menu[$menuName][$subMenuName]['_tooltip'] = $tooltip;
155 $this->menu[$menuName][$subMenuName]['_attribute'] = $attribute;
156 $this->menu[$menuName][$subMenuName]['_icon'] = $icon;
157 $this->menu[$menuName][$subMenuName]['_onclick'] = $onclick;
158 $this->menu[$menuName][$subMenuName]['_help'] = $help ?: '';
159 $this->menu[$menuName][$subMenuName]['_badgecount'] = $badgeCount;
160 $this->menu[$menuName][$subMenuName]['_cssClass'] = $cssClass;
161 $this->menu[$menuName]['_hasSubmenu'] = \true;
162 if (!array_key_exists('_tooltip', $this->menu[$menuName])) {
163 $this->menu[$menuName]['_tooltip'] = $tooltip;
164 }
165 }
166 }
167 /**
168 * Builds the menu from the $this->menuEntries variable.
169 */
170 private function buildMenu()
171 {
172 foreach ($this->menuEntries as $menuEntry) {
173 $this->buildMenuItem(...$menuEntry);
174 }
175 }
176 /**
177 * Renames a single menu entry.
178 *
179 * @param $mainMenuOriginal
180 * @param $subMenuOriginal
181 * @param $mainMenuRenamed
182 * @param $subMenuRenamed
183 * @api
184 */
185 public function rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed)
186 {
187 $this->renames[] = array($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed);
188 }
189 /**
190 * Edits a URL of an existing menu entry.
191 *
192 * @param $mainMenuToEdit
193 * @param $subMenuToEdit
194 * @param $newUrl
195 * @api
196 */
197 public function editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
198 {
199 $this->edits[] = array($mainMenuToEdit, $subMenuToEdit, $newUrl);
200 }
201 /**
202 * Applies all edits to the menu.
203 */
204 private function applyEdits()
205 {
206 foreach ($this->edits as $edit) {
207 $mainMenuToEdit = $edit[0];
208 $subMenuToEdit = $edit[1];
209 $newUrl = $edit[2];
210 if ($subMenuToEdit === null) {
211 if (isset($this->menu[$mainMenuToEdit])) {
212 $menuDataToEdit =& $this->menu[$mainMenuToEdit];
213 } else {
214 $menuDataToEdit = null;
215 }
216 } else {
217 if (isset($this->menu[$mainMenuToEdit][$subMenuToEdit])) {
218 $menuDataToEdit =& $this->menu[$mainMenuToEdit][$subMenuToEdit];
219 } else {
220 $menuDataToEdit = null;
221 }
222 }
223 if (empty($menuDataToEdit)) {
224 $this->buildMenuItem($mainMenuToEdit, $subMenuToEdit, $newUrl);
225 } else {
226 $menuDataToEdit['_url'] = $newUrl;
227 }
228 }
229 }
230 private function applyRemoves()
231 {
232 foreach ($this->menuEntriesToRemove as $menuToDelete) {
233 if (empty($menuToDelete[1])) {
234 // Delete Main Menu
235 if (isset($this->menu[$menuToDelete[0]])) {
236 unset($this->menu[$menuToDelete[0]]);
237 }
238 } else {
239 // Delete Sub Menu
240 if (isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) {
241 unset($this->menu[$menuToDelete[0]][$menuToDelete[1]]);
242 }
243 }
244 }
245 }
246 /**
247 * Applies renames to the menu.
248 */
249 private function applyRenames()
250 {
251 foreach ($this->renames as $rename) {
252 $mainMenuOriginal = $rename[0];
253 $subMenuOriginal = $rename[1];
254 $mainMenuRenamed = $rename[2];
255 $subMenuRenamed = $rename[3];
256 // Are we changing a submenu?
257 if (!empty($subMenuOriginal)) {
258 if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) {
259 $save = $this->menu[$mainMenuOriginal][$subMenuOriginal];
260 $save['_name'] = $subMenuRenamed;
261 unset($this->menu[$mainMenuOriginal][$subMenuOriginal]);
262 $this->menu[$mainMenuRenamed][$subMenuRenamed] = $save;
263 }
264 } elseif (isset($this->menu[$mainMenuOriginal])) {
265 // Changing a first-level element
266 $save = $this->menu[$mainMenuOriginal];
267 $save['_name'] = $mainMenuRenamed;
268 unset($this->menu[$mainMenuOriginal]);
269 $this->menu[$mainMenuRenamed] = $save;
270 }
271 }
272 }
273 /**
274 * Orders the menu according to their order.
275 */
276 private function applyOrdering()
277 {
278 if (empty($this->menu) || $this->orderingApplied) {
279 return;
280 }
281 uasort($this->menu, array($this, 'menuCompare'));
282 foreach ($this->menu as $key => &$element) {
283 if (is_null($element)) {
284 unset($this->menu[$key]);
285 } elseif ($element['_hasSubmenu']) {
286 uasort($element, array($this, 'menuCompare'));
287 }
288 }
289 $this->orderingApplied = \true;
290 }
291 /**
292 * Compares two menu entries. Used for ordering.
293 *
294 * @param array $itemOne
295 * @param array $itemTwo
296 * @return boolean
297 */
298 protected function menuCompare($itemOne, $itemTwo)
299 {
300 if (!is_array($itemOne) && !is_array($itemTwo)) {
301 return 0;
302 }
303 if (!is_array($itemOne) && is_array($itemTwo)) {
304 return -1;
305 }
306 if (is_array($itemOne) && !is_array($itemTwo)) {
307 return 1;
308 }
309 if (!isset($itemOne['_order']) && !isset($itemTwo['_order'])) {
310 return 0;
311 }
312 if (!isset($itemOne['_order']) && isset($itemTwo['_order'])) {
313 return -1;
314 }
315 if (isset($itemOne['_order']) && !isset($itemTwo['_order'])) {
316 return 1;
317 }
318 if ($itemOne['_order'] == $itemTwo['_order']) {
319 return strcmp($itemOne['_name'] ?? '', $itemTwo['_name'] ?? '');
320 }
321 return $itemOne['_order'] < $itemTwo['_order'] ? -1 : 1;
322 }
323 }
324