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