PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.7.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.7.0
5.13.0 5.12.1 5.12.0 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 8 months ago MenuAdmin.php 8 months ago MenuTop.php 1 year ago
MenuAbstract.php
329 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 $menuName
121 * @param string|null $subMenuName
122 * @param string|array $url
123 * @param int $order
124 * @param bool|string $tooltip Tooltip to display.
125 * @param bool|string $icon
126 * @param bool|string $onclick
127 * @param bool|string $attribute
128 * @param bool|string $help
129 * @param int $badgeCount
130 * @param string $cssClass
131 */
132 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 = '')
133 {
134 if (!isset($this->menu[$menuName])) {
135 $this->menu[$menuName] = array('_hasSubmenu' => \false, '_order' => $order);
136 }
137 if (empty($subMenuName)) {
138 $this->menu[$menuName]['_url'] = $url;
139 $this->menu[$menuName]['_order'] = $order;
140 $this->menu[$menuName]['_name'] = $menuName;
141 $this->menu[$menuName]['_tooltip'] = $tooltip;
142 $this->menu[$menuName]['_attribute'] = $attribute;
143 if (!empty($this->menuIcons[$menuName])) {
144 $this->menu[$menuName]['_icon'] = $this->menuIcons[$menuName];
145 } else {
146 $this->menu[$menuName]['_icon'] = '';
147 }
148 if (!empty($onclick)) {
149 $this->menu[$menuName]['_onclick'] = $onclick;
150 }
151 $this->menu[$menuName]['_help'] = $help ?: '';
152 $this->menu[$menuName]['_badgecount'] = $badgeCount;
153 $this->menu[$menuName]['_cssClass'] = $cssClass;
154 }
155 if (!empty($subMenuName)) {
156 $this->menu[$menuName][$subMenuName]['_url'] = $url;
157 $this->menu[$menuName][$subMenuName]['_order'] = $order;
158 $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName;
159 $this->menu[$menuName][$subMenuName]['_tooltip'] = $tooltip;
160 $this->menu[$menuName][$subMenuName]['_attribute'] = $attribute;
161 $this->menu[$menuName][$subMenuName]['_icon'] = $icon;
162 $this->menu[$menuName][$subMenuName]['_onclick'] = $onclick;
163 $this->menu[$menuName][$subMenuName]['_help'] = $help ?: '';
164 $this->menu[$menuName][$subMenuName]['_badgecount'] = $badgeCount;
165 $this->menu[$menuName][$subMenuName]['_cssClass'] = $cssClass;
166 $this->menu[$menuName]['_hasSubmenu'] = \true;
167 if (!array_key_exists('_tooltip', $this->menu[$menuName])) {
168 $this->menu[$menuName]['_tooltip'] = $tooltip;
169 }
170 }
171 }
172 /**
173 * Builds the menu from the $this->menuEntries variable.
174 */
175 private function buildMenu()
176 {
177 foreach ($this->menuEntries as $menuEntry) {
178 $this->buildMenuItem(...$menuEntry);
179 }
180 }
181 /**
182 * Renames a single menu entry.
183 *
184 * @param $mainMenuOriginal
185 * @param $subMenuOriginal
186 * @param $mainMenuRenamed
187 * @param $subMenuRenamed
188 * @api
189 */
190 public function rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed)
191 {
192 $this->renames[] = array($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed);
193 }
194 /**
195 * Edits a URL of an existing menu entry.
196 *
197 * @param $mainMenuToEdit
198 * @param $subMenuToEdit
199 * @param $newUrl
200 * @api
201 */
202 public function editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
203 {
204 $this->edits[] = array($mainMenuToEdit, $subMenuToEdit, $newUrl);
205 }
206 /**
207 * Applies all edits to the menu.
208 */
209 private function applyEdits()
210 {
211 foreach ($this->edits as $edit) {
212 $mainMenuToEdit = $edit[0];
213 $subMenuToEdit = $edit[1];
214 $newUrl = $edit[2];
215 if ($subMenuToEdit === null) {
216 if (isset($this->menu[$mainMenuToEdit])) {
217 $menuDataToEdit =& $this->menu[$mainMenuToEdit];
218 } else {
219 $menuDataToEdit = null;
220 }
221 } else {
222 if (isset($this->menu[$mainMenuToEdit][$subMenuToEdit])) {
223 $menuDataToEdit =& $this->menu[$mainMenuToEdit][$subMenuToEdit];
224 } else {
225 $menuDataToEdit = null;
226 }
227 }
228 if (empty($menuDataToEdit)) {
229 $this->buildMenuItem($mainMenuToEdit, $subMenuToEdit, $newUrl);
230 } else {
231 $menuDataToEdit['_url'] = $newUrl;
232 }
233 }
234 }
235 private function applyRemoves()
236 {
237 foreach ($this->menuEntriesToRemove as $menuToDelete) {
238 if (empty($menuToDelete[1])) {
239 // Delete Main Menu
240 if (isset($this->menu[$menuToDelete[0]])) {
241 unset($this->menu[$menuToDelete[0]]);
242 }
243 } else {
244 // Delete Sub Menu
245 if (isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) {
246 unset($this->menu[$menuToDelete[0]][$menuToDelete[1]]);
247 }
248 }
249 }
250 }
251 /**
252 * Applies renames to the menu.
253 */
254 private function applyRenames()
255 {
256 foreach ($this->renames as $rename) {
257 $mainMenuOriginal = $rename[0];
258 $subMenuOriginal = $rename[1];
259 $mainMenuRenamed = $rename[2];
260 $subMenuRenamed = $rename[3];
261 // Are we changing a submenu?
262 if (!empty($subMenuOriginal)) {
263 if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) {
264 $save = $this->menu[$mainMenuOriginal][$subMenuOriginal];
265 $save['_name'] = $subMenuRenamed;
266 unset($this->menu[$mainMenuOriginal][$subMenuOriginal]);
267 $this->menu[$mainMenuRenamed][$subMenuRenamed] = $save;
268 }
269 } elseif (isset($this->menu[$mainMenuOriginal])) {
270 // Changing a first-level element
271 $save = $this->menu[$mainMenuOriginal];
272 $save['_name'] = $mainMenuRenamed;
273 unset($this->menu[$mainMenuOriginal]);
274 $this->menu[$mainMenuRenamed] = $save;
275 }
276 }
277 }
278 /**
279 * Orders the menu according to their order.
280 */
281 private function applyOrdering()
282 {
283 if (empty($this->menu) || $this->orderingApplied) {
284 return;
285 }
286 uasort($this->menu, array($this, 'menuCompare'));
287 foreach ($this->menu as $key => &$element) {
288 if (is_null($element)) {
289 unset($this->menu[$key]);
290 } elseif ($element['_hasSubmenu']) {
291 uasort($element, array($this, 'menuCompare'));
292 }
293 }
294 $this->orderingApplied = \true;
295 }
296 /**
297 * Compares two menu entries. Used for ordering.
298 *
299 * @param array $itemOne
300 * @param array $itemTwo
301 * @return boolean
302 */
303 protected function menuCompare($itemOne, $itemTwo)
304 {
305 if (!is_array($itemOne) && !is_array($itemTwo)) {
306 return 0;
307 }
308 if (!is_array($itemOne) && is_array($itemTwo)) {
309 return -1;
310 }
311 if (is_array($itemOne) && !is_array($itemTwo)) {
312 return 1;
313 }
314 if (!isset($itemOne['_order']) && !isset($itemTwo['_order'])) {
315 return 0;
316 }
317 if (!isset($itemOne['_order']) && isset($itemTwo['_order'])) {
318 return -1;
319 }
320 if (isset($itemOne['_order']) && !isset($itemTwo['_order'])) {
321 return 1;
322 }
323 if ($itemOne['_order'] == $itemTwo['_order']) {
324 return strcmp($itemOne['_name'] ?? '', $itemTwo['_name'] ?? '');
325 }
326 return $itemOne['_order'] < $itemTwo['_order'] ? -1 : 1;
327 }
328 }
329