header.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. /**
  11. * Loads the main structure
  12. *
  13. * @see modManagerController::getHeader
  14. * @see modManagerController::loadController
  15. *
  16. * @var modX $modx
  17. * @var modManagerController $this
  18. *
  19. * @package modx
  20. * @subpackage manager.controllers
  21. */
  22. class TopMenu
  23. {
  24. /**
  25. * @var modManagerController
  26. */
  27. public $controller;
  28. /**
  29. * @var modX
  30. */
  31. public $modx;
  32. /**
  33. * The current menu HTML output
  34. *
  35. * @var string
  36. */
  37. protected $output = '';
  38. /**
  39. * Whether or not to display menus description
  40. *
  41. * @var bool
  42. */
  43. protected $showDescriptions = true;
  44. /**
  45. * Current menu index
  46. *
  47. * @var int
  48. */
  49. protected $order = 0;
  50. /**
  51. * Current children menu index
  52. *
  53. * @var int
  54. */
  55. protected $childrenCt = 0;
  56. public function __construct(modManagerController &$controller)
  57. {
  58. $this->controller =& $controller;
  59. $this->modx =& $controller->modx;
  60. $this->showDescriptions = (boolean) $this->modx->getOption('topmenu_show_descriptions', null, true);
  61. }
  62. /**
  63. * Build the top menu
  64. *
  65. * @return void
  66. */
  67. public function render()
  68. {
  69. // First assign most variables so they could be used within menus
  70. $this->setPlaceholders();
  71. // Then process menu "containers"
  72. $mainNav = $this->modx->smarty->getTemplateVars('navb');
  73. if (empty($mainNav)) {
  74. $this->buildMenu(
  75. $this->modx->getOption('main_nav_parent', null, 'topnav', true),
  76. 'navb'
  77. );
  78. }
  79. $userNav = $this->modx->smarty->getTemplateVars('userNav');
  80. if (empty($userNav)) {
  81. $this->buildMenu(
  82. $this->modx->getOption('user_nav_parent', null, 'usernav', true),
  83. 'userNav'
  84. );
  85. }
  86. }
  87. /**
  88. * Set a bunch of placeholders to be used within Smarty templates
  89. *
  90. * @return void
  91. */
  92. public function setPlaceholders()
  93. {
  94. $username = '';
  95. if ($this->modx->getOption('manager_use_fullname') == true) {
  96. $userProfile = $this->modx->user->getOne('Profile');
  97. $username = $userProfile->get('fullname');
  98. }
  99. if (empty($username)) {
  100. $username = $this->modx->getLoginUserName();
  101. }
  102. $placeholders = array(
  103. 'username' => $username,
  104. 'userImage' => $this->getUserImage(),
  105. );
  106. $this->controller->setPlaceholders($placeholders);
  107. }
  108. /**
  109. * Retrieve/compute the user picture profile
  110. *
  111. * @return string The HTML output
  112. */
  113. public function getUserImage()
  114. {
  115. // Default to FontAwesome
  116. $output = '<i class="icon icon-user icon-large"></i>&nbsp;';
  117. $img = $this->modx->user->getPhoto(128, 128);
  118. if (!empty($img)) {
  119. $output = '<img src="' . $img . '" />';
  120. }
  121. return $output;
  122. }
  123. /**
  124. * Build the requested menu "container" and set it as a placeholder
  125. *
  126. * @param string $name The container name (topnav, usernav)
  127. * @param string $placeholder The placeholder to display the built menu to
  128. *
  129. * @return void
  130. */
  131. public function buildMenu($name, $placeholder)
  132. {
  133. if (!$placeholder) {
  134. $placeholder = $name;
  135. }
  136. // Grab the menus to process
  137. $menus = $this->getCache($name);
  138. // Iterate
  139. foreach ($menus as $menu) {
  140. $this->childrenCt = 0;
  141. if (!$this->hasPermission($menu['permissions'])) {
  142. continue;
  143. }
  144. $description = '';
  145. if ($this->showDescriptions && !empty($menu['description'])) {
  146. $description = '<span class="description">'.$menu['description'].'</span>'."\n";
  147. }
  148. $label = $menu['text'];
  149. $title = ' title="' . $menu['description'] .'"';
  150. $icon = false;
  151. if (!empty($menu['icon'])) {
  152. $icon = true;
  153. // Use the icon as label
  154. $label = $menu['icon'];
  155. // Reset the description (which is set as text in $title)
  156. $description = '';
  157. }
  158. $top = (!empty($menu['children'])) ? ' class="top"' : '';
  159. $menuTpl = '<li id="limenu-'.$menu['id'].'"'.$top.'>'."\n";
  160. if (!empty($menu['action'])) {
  161. if ($menu['namespace'] != 'core') {
  162. // Handle the namespace
  163. $menu['action'] .= '&namespace='.$menu['namespace'];
  164. }
  165. if (!$icon) {
  166. // No icon, no title property
  167. $title = '';
  168. }
  169. $onclick = (!empty($menu['handler'])) ? ' onclick="'.str_replace('"','\'',$menu['handler']).'"' : '';
  170. $menuTpl .= '<a href="?a='.$menu['action'].$menu['params'].'"'.( $top ? ' class="top-link"': '' ).$onclick.$title.'>'.$label.$description.'</a>'."\n";
  171. } elseif (!empty($menu['handler'])) {
  172. $menuTpl .= '<a href="javascript:;" onclick="'.str_replace('"','\'',$menu['handler']).'">'.$label.'</a>'."\n";
  173. } else {
  174. $menuTpl .= '<a href="javascript:;">'.$label.'</a>'."\n";
  175. }
  176. if (!empty($menu['children'])) {
  177. $menuTpl .= '<ul class="modx-subnav">'."\n";
  178. $this->processSubMenus($menuTpl, $menu['children']);
  179. $menuTpl .= '</ul>'."\n";
  180. }
  181. $menuTpl .= '</li>'."\n";
  182. /* if has no permissable children, and is not clickable, hide top menu item */
  183. if (!empty($this->childrenCt) || !empty($menu['action']) || !empty($menu['handler'])) {
  184. $this->output .= $menuTpl;
  185. }
  186. $this->order++;
  187. }
  188. //$this->cleanEmptySubMenus();
  189. $this->controller->setPlaceholder($placeholder, $this->output);
  190. $this->resetCounters();
  191. }
  192. /**
  193. * Retrieve the menus for the given "container"
  194. *
  195. * @param string $name
  196. *
  197. * @return array
  198. */
  199. protected function getCache($name)
  200. {
  201. $key = $this->getCacheKey($name);
  202. $menus = $this->modx->cacheManager->get($key, array(
  203. xPDO::OPT_CACHE_KEY => $this->modx->getOption('cache_menu_key', null, 'menu'),
  204. xPDO::OPT_CACHE_HANDLER => $this->modx->getOption(
  205. 'cache_menu_handler',
  206. null,
  207. $this->modx->getOption(xPDO::OPT_CACHE_HANDLER)
  208. ),
  209. xPDO::OPT_CACHE_FORMAT => (integer) $this->modx->getOption(
  210. 'cache_menu_format',
  211. null,
  212. $this->modx->getOption(xPDO::OPT_CACHE_FORMAT, null, xPDOCacheManager::CACHE_PHP)
  213. ),
  214. ));
  215. if ($menus == null || !is_array($menus)) {
  216. /** @var modMenu $menu */
  217. $menu = $this->modx->newObject('modMenu');
  218. $menus = $menu->rebuildCache($name);
  219. unset($menu);
  220. }
  221. return $menus;
  222. }
  223. /**
  224. * Compute the cache key for the given menu "container"
  225. *
  226. * @param string $name
  227. *
  228. * @return string
  229. */
  230. protected function getCacheKey($name)
  231. {
  232. return "menus/{$name}/" . $this->modx->getOption(
  233. 'manager_language',
  234. null,
  235. $this->modx->getOption('cultureKey', null, 'en')
  236. );
  237. }
  238. /**
  239. * Reset menu HTML output & indexes counters
  240. *
  241. * @return void
  242. */
  243. protected function resetCounters()
  244. {
  245. $this->output = '';
  246. $this->order = 0;
  247. $this->childrenCt = 0;
  248. }
  249. /**
  250. * Check if the current user is allowed to view the menu record
  251. *
  252. * @param string $perms
  253. *
  254. * @return bool
  255. */
  256. public function hasPermission($perms)
  257. {
  258. if (empty($perms)) {
  259. return true;
  260. }
  261. $permissions = array();
  262. $exploded = explode(',', $perms);
  263. foreach ($exploded as $permission) {
  264. $permissions[trim($permission)] = true;
  265. }
  266. return $this->modx->hasPermission($permissions);
  267. }
  268. /**
  269. * Process the given sub menus
  270. *
  271. * @param string $output The existing menu HTML "output"
  272. * @param array $menus The sub menus to process
  273. *
  274. * @return void
  275. */
  276. public function processSubMenus(&$output, array $menus = array())
  277. {
  278. //$output .= '<ul class="modx-subnav">'."\n";
  279. foreach ($menus as $menu) {
  280. if (!$this->hasPermission($menu['permissions'])) {
  281. continue;
  282. }
  283. $sub = (!empty($menu['children'])) ? ' class="sub"' : '';
  284. $smTpl = '<li id="'.$menu['id'].'"'.$sub.'>'."\n";
  285. $description = '';
  286. if ($this->showDescriptions && !empty($menu['description'])) {
  287. $description = '<span class="description">'.$menu['description'].'</span>'."\n";
  288. }
  289. $attributes = '';
  290. if (!empty($menu['action'])) {
  291. if ($menu['namespace'] != 'core') {
  292. $menu['action'] .= '&namespace='.$menu['namespace'];
  293. }
  294. $attributes = ' href="?a='.$menu['action'].$menu['params'].'"';
  295. }
  296. if (!empty($menu['handler'])) {
  297. $attributes .= ' onclick="{literal} '.str_replace('"','\'',$menu['handler']).'{/literal} "';
  298. }
  299. $smTpl .= '<a'.$attributes.'>'.$menu['text'].$description.'</a>'."\n";
  300. if (!empty($menu['children'])) {
  301. $smTpl .= '<ul class="modx-subsubnav">'."\n";
  302. $this->processSubMenus($smTpl, $menu['children']);
  303. $smTpl .= '</ul>'."\n";
  304. }
  305. $smTpl .= '</li>';
  306. $output .= $smTpl;
  307. $this->childrenCt++;
  308. }
  309. //$output .= '</ul>'."\n";
  310. }
  311. /**
  312. * Clean "orphan" sub menus
  313. *
  314. * @return void
  315. */
  316. public function cleanEmptySubMenus()
  317. {
  318. $emptySub = '<ul class="modx-subsubnav">'."\n".'</ul>'."\n";
  319. $this->output = str_replace($emptySub, '', $this->output);
  320. }
  321. }
  322. // Set Smarty placeholder to display search bar, if appropriate
  323. $this->setPlaceholder('_search', $modx->hasPermission('search'));
  324. $menu = new TopMenu($this);
  325. $menu->render();