PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/core/Menu/MenuAbstract.php

https://github.com/CodeYellowBV/piwik
PHP | 298 lines | 177 code | 25 blank | 96 comment | 30 complexity | b348056e3e21b4f42282f139836a4c80 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Menu;
  10. use Piwik\Common;
  11. use Piwik\Log;
  12. use Piwik\Plugins\SitesManager\API;
  13. use Piwik\Singleton;
  14. use Piwik\Plugin\Manager as PluginManager;
  15. /**
  16. * Base class for classes that manage one of Piwik's menus.
  17. *
  18. * There are three menus in Piwik, the main menu, the top menu and the admin menu.
  19. * Each menu has a class that manages the menu's content. Each class invokes
  20. * a different event to allow plugins to add new menu items.
  21. *
  22. * @static \Piwik\Menu\MenuAbstract getInstance()
  23. */
  24. abstract class MenuAbstract extends Singleton
  25. {
  26. protected $menu = null;
  27. protected $menuEntries = array();
  28. protected $menuEntriesToRemove = array();
  29. protected $edits = array();
  30. protected $renames = array();
  31. protected $orderingApplied = false;
  32. protected static $menus = array();
  33. /**
  34. * Builds the menu, applies edits, renames
  35. * and orders the entries.
  36. *
  37. * @return Array
  38. */
  39. public function getMenu()
  40. {
  41. $this->buildMenu();
  42. $this->applyEdits();
  43. $this->applyRenames();
  44. $this->applyRemoves();
  45. $this->applyOrdering();
  46. return $this->menu;
  47. }
  48. /**
  49. * Returns a list of available plugin menu instances.
  50. *
  51. * @return \Piwik\Plugin\Menu[]
  52. */
  53. protected function getAvailableMenus()
  54. {
  55. if (!empty(self::$menus)) {
  56. return self::$menus;
  57. }
  58. self::$menus = PluginManager::getInstance()->findComponents('Menu', 'Piwik\\Plugin\\Menu');
  59. return self::$menus;
  60. }
  61. /**
  62. * Adds a new entry to the menu.
  63. *
  64. * @param string $menuName The menu's category name. Can be a translation token.
  65. * @param string $subMenuName The menu item's name. Can be a translation token.
  66. * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
  67. * that can be used to build the URL.
  68. * @param boolean $displayedForCurrentUser Whether this menu entry should be displayed for the
  69. * current user. If false, the entry will not be added.
  70. * @param int $order The order hint.
  71. * @param bool|string $tooltip An optional tooltip to display or false to display the tooltip.
  72. * @api
  73. */
  74. public function add($menuName, $subMenuName, $url, $displayedForCurrentUser = true, $order = 50, $tooltip = false)
  75. {
  76. if (!$displayedForCurrentUser) {
  77. return;
  78. }
  79. // make sure the idSite value used is numeric (hack-y fix for #3426)
  80. if (!is_numeric(Common::getRequestVar('idSite', false))) {
  81. $idSites = API::getInstance()->getSitesIdWithAtLeastViewAccess();
  82. $url['idSite'] = reset($idSites);
  83. }
  84. $this->menuEntries[] = array(
  85. $menuName,
  86. $subMenuName,
  87. $url,
  88. $order,
  89. $tooltip
  90. );
  91. }
  92. /**
  93. * Removes an existing entry from the menu.
  94. *
  95. * @param string $menuName The menu's category name. Can be a translation token.
  96. * @param bool|string $subMenuName The menu item's name. Can be a translation token.
  97. * @api
  98. */
  99. public function remove($menuName, $subMenuName = false)
  100. {
  101. $this->menuEntriesToRemove[] = array(
  102. $menuName,
  103. $subMenuName
  104. );
  105. }
  106. /**
  107. * Builds a single menu item
  108. *
  109. * @param string $menuName
  110. * @param string $subMenuName
  111. * @param string $url
  112. * @param int $order
  113. * @param bool|string $tooltip Tooltip to display.
  114. */
  115. private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false)
  116. {
  117. if (!isset($this->menu[$menuName]) || empty($subMenuName)) {
  118. $this->menu[$menuName]['_url'] = $url;
  119. if (empty($subMenuName)) {
  120. $this->menu[$menuName]['_order'] = $order;
  121. }
  122. $this->menu[$menuName]['_name'] = $menuName;
  123. $this->menu[$menuName]['_hasSubmenu'] = false;
  124. $this->menu[$menuName]['_tooltip'] = $tooltip;
  125. }
  126. if (!empty($subMenuName)) {
  127. $this->menu[$menuName][$subMenuName]['_url'] = $url;
  128. $this->menu[$menuName][$subMenuName]['_order'] = $order;
  129. $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName;
  130. $this->menu[$menuName][$subMenuName]['_tooltip'] = $tooltip;
  131. $this->menu[$menuName]['_hasSubmenu'] = true;
  132. $this->menu[$menuName]['_tooltip'] = $tooltip;
  133. }
  134. }
  135. /**
  136. * Builds the menu from the $this->menuEntries variable.
  137. */
  138. private function buildMenu()
  139. {
  140. foreach ($this->menuEntries as $menuEntry) {
  141. $this->buildMenuItem($menuEntry[0], $menuEntry[1], $menuEntry[2], $menuEntry[3], $menuEntry[4]);
  142. }
  143. }
  144. /**
  145. * Renames a single menu entry.
  146. *
  147. * @param $mainMenuOriginal
  148. * @param $subMenuOriginal
  149. * @param $mainMenuRenamed
  150. * @param $subMenuRenamed
  151. * @api
  152. */
  153. public function rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed)
  154. {
  155. $this->renames[] = array($mainMenuOriginal, $subMenuOriginal,
  156. $mainMenuRenamed, $subMenuRenamed);
  157. }
  158. /**
  159. * Edits a URL of an existing menu entry.
  160. *
  161. * @param $mainMenuToEdit
  162. * @param $subMenuToEdit
  163. * @param $newUrl
  164. * @api
  165. */
  166. public function editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
  167. {
  168. $this->edits[] = array($mainMenuToEdit, $subMenuToEdit, $newUrl);
  169. }
  170. /**
  171. * Applies all edits to the menu.
  172. */
  173. private function applyEdits()
  174. {
  175. foreach ($this->edits as $edit) {
  176. $mainMenuToEdit = $edit[0];
  177. $subMenuToEdit = $edit[1];
  178. $newUrl = $edit[2];
  179. if ($subMenuToEdit === null) {
  180. $menuDataToEdit = @$this->menu[$mainMenuToEdit];
  181. } else {
  182. $menuDataToEdit = @$this->menu[$mainMenuToEdit][$subMenuToEdit];
  183. }
  184. if (empty($menuDataToEdit)) {
  185. $this->buildMenuItem($mainMenuToEdit, $subMenuToEdit, $newUrl);
  186. } else {
  187. $menuDataToEdit['_url'] = $newUrl;
  188. }
  189. }
  190. }
  191. private function applyRemoves()
  192. {
  193. foreach($this->menuEntriesToRemove as $menuToDelete) {
  194. if(empty($menuToDelete[1])) {
  195. // Delete Main Menu
  196. if(isset($this->menu[$menuToDelete[0]])) {
  197. unset($this->menu[$menuToDelete[0]]);
  198. }
  199. } else {
  200. // Delete Sub Menu
  201. if(isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) {
  202. unset($this->menu[$menuToDelete[0]][$menuToDelete[1]]);
  203. }
  204. }
  205. }
  206. }
  207. /**
  208. * Applies renames to the menu.
  209. */
  210. private function applyRenames()
  211. {
  212. foreach ($this->renames as $rename) {
  213. $mainMenuOriginal = $rename[0];
  214. $subMenuOriginal = $rename[1];
  215. $mainMenuRenamed = $rename[2];
  216. $subMenuRenamed = $rename[3];
  217. // Are we changing a submenu?
  218. if (!empty($subMenuOriginal)) {
  219. if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) {
  220. $save = $this->menu[$mainMenuOriginal][$subMenuOriginal];
  221. $save['_name'] = $subMenuRenamed;
  222. unset($this->menu[$mainMenuOriginal][$subMenuOriginal]);
  223. $this->menu[$mainMenuRenamed][$subMenuRenamed] = $save;
  224. }
  225. } // Changing a first-level element
  226. else if (isset($this->menu[$mainMenuOriginal])) {
  227. $save = $this->menu[$mainMenuOriginal];
  228. $save['_name'] = $mainMenuRenamed;
  229. unset($this->menu[$mainMenuOriginal]);
  230. $this->menu[$mainMenuRenamed] = $save;
  231. }
  232. }
  233. }
  234. /**
  235. * Orders the menu according to their order.
  236. */
  237. private function applyOrdering()
  238. {
  239. if (empty($this->menu)
  240. || $this->orderingApplied
  241. ) {
  242. return;
  243. }
  244. uasort($this->menu, array($this, 'menuCompare'));
  245. foreach ($this->menu as $key => &$element) {
  246. if (is_null($element)) {
  247. unset($this->menu[$key]);
  248. } else if ($element['_hasSubmenu']) {
  249. uasort($element, array($this, 'menuCompare'));
  250. }
  251. }
  252. $this->orderingApplied = true;
  253. }
  254. /**
  255. * Compares two menu entries. Used for ordering.
  256. *
  257. * @param array $itemOne
  258. * @param array $itemTwo
  259. * @return boolean
  260. */
  261. protected function menuCompare($itemOne, $itemTwo)
  262. {
  263. if (!is_array($itemOne) || !is_array($itemTwo)
  264. || !isset($itemOne['_order']) || !isset($itemTwo['_order'])
  265. ) {
  266. return 0;
  267. }
  268. if ($itemOne['_order'] == $itemTwo['_order']) {
  269. return strcmp($itemOne['_name'], $itemTwo['_name']);
  270. }
  271. return ($itemOne['_order'] < $itemTwo['_order']) ? -1 : 1;
  272. }
  273. }