SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Navigation.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16. use Kohana;
  17.  
  18. use karmabunny\pdb\Exceptions\QueryException;
  19.  
  20.  
  21. /**
  22. * Provides navigation functions
  23. **/
  24. class Navigation
  25. {
  26. /** @var PageNode|null */
  27. static private $root_node = null;
  28.  
  29. /** @var TreeNodeMatcher|null */
  30. static private $page_node_matcher = null;
  31.  
  32. /** @var int */
  33. static private $pmm2_nav_id = 0;
  34.  
  35.  
  36. /**
  37.   * Sets a matcher that should be used to find the current page in the tree
  38.   *
  39.   * @param TreeNodeMatcher $page_node_matcher The matcher to use
  40.   **/
  41. static public function setPageNodeMatcher($page_node_matcher)
  42. {
  43. self::$page_node_matcher = $page_node_matcher;
  44. }
  45.  
  46. /**
  47.   * Gets the matcher that is used to get the current page
  48.   *
  49.   * @return TreeNodeMatcher The matcher being used.
  50.   **/
  51. static public function getPageNodeMatcher()
  52. {
  53. return self::$page_node_matcher;
  54. }
  55.  
  56.  
  57. /**
  58.   * Loads the page tree from the database
  59.   *
  60.   * @param int $subsite_id The subsite to load pages from. If not provided, the current subsite is assumed.
  61.   * @param bool $is_admin If the user is in the admin, certain restrictions are not enforced.
  62.   * @param bool $set_root Set the Navigation::$root_node paramater, as used by all the other methods.
  63.   * Default is to set this parameter, but if you want the tree loaded twice for some reason, set to false.
  64.   * @return PageNode The root node of the tree
  65.   **/
  66. static public function loadPageTree($subsite_id = null, $is_admin = false, $set_root = true)
  67. {
  68. if ($subsite_id == null) {
  69. $subsite_id = SubsiteSelector::$content_id;
  70. }
  71.  
  72. if (Kohana::config('cache.enabled') and $is_admin == false) {
  73. $cache = Cache::instance();
  74. $from_cache = $cache->get("nav:{$subsite_id}");
  75. if ($from_cache) {
  76. if ($set_root) self::$root_node = $from_cache;
  77. return $from_cache;
  78. }
  79. }
  80.  
  81. $where = "pages.subsite_id = ?";
  82. if ($is_admin == false) {
  83. $where .= " AND pages.active = 1 AND revs.status = 'live'";
  84. }
  85.  
  86. $q = "SELECT pages.id, pages.parent_id, pages.slug, pages.name, pages.menu_group, pages.show_in_nav,
  87. pages.alt_nav_title, pages.admin_perm_type, banners.filename AS banner,
  88. gallery_thumbs.filename AS gallery_thumb, revs.controller_entrance, revs.controller_argument,
  89. pages.date_modified, revs.redirect
  90. FROM ~pages AS pages
  91. LEFT JOIN ~files AS banners ON pages.banner = banners.id
  92. LEFT JOIN ~files AS gallery_thumbs ON pages.gallery_thumb = gallery_thumbs.id
  93. INNER JOIN ~page_revisions AS revs ON revs.page_id = pages.id
  94. WHERE {$where}
  95. GROUP BY pages.id
  96. ORDER BY pages.parent_id, pages.record_order";
  97. try {
  98. $result = Pdb::q($q, [$subsite_id], 'arr');
  99. } catch (QueryException $ex) {
  100. // Assume DB has no tables
  101. $result = [];
  102. }
  103.  
  104. $root_node = new Pagenode(array(
  105. 'id' => 0,
  106. 'parent_id' => 0,
  107. 'slug' => '',
  108. 'name' => '',
  109. 'controller_entrance' => '',
  110. 'controller_argument' => '',
  111. 'menu_group' => '',
  112. ));
  113.  
  114. // Create nodes
  115. $needprocess = array();
  116. foreach ($result as $row) {
  117. $needprocess[(int)$row['id']] = new Pagenode($row);
  118. }
  119.  
  120. // Save all nodes in a cache
  121. $nodecache = $needprocess;
  122. $nodecache[0] = $root_node;
  123.  
  124. // Process the nodes
  125. // This may iterate if low-numbered nodes are children of high-numbered ones
  126. do {
  127. $num_processed = 0;
  128. foreach ($needprocess as $id => $node) {
  129. $parent = @$nodecache[(int)$node['parent_id']];
  130.  
  131. if ($parent) {
  132. $parent->children[] = $node;
  133. $node->parent = $parent;
  134. unset($needprocess[$id]);
  135. $num_processed++;
  136. }
  137. }
  138. if ($num_processed == 0) break;
  139.  
  140. } while (count($needprocess));
  141.  
  142. unset($nodecache);
  143.  
  144. if (Kohana::config('cache.enabled') and $is_admin == false) {
  145. $cache->set("nav:{$subsite_id}", $root_node);
  146. }
  147.  
  148. if ($set_root) {
  149. self::$root_node = $root_node;
  150. }
  151.  
  152. return $root_node;
  153. }
  154.  
  155. /**
  156.   * Gets the root node of the currently loaded navigation tree
  157.   **/
  158. static public function getRootNode()
  159. {
  160. if (! self::$root_node) self::loadPageTree();
  161. return self::$root_node;
  162. }
  163.  
  164. /**
  165.   * Alias for Navigation::getRootNode()
  166.   **/
  167. static public function rootNode()
  168. {
  169. return self::getRootNode();
  170. }
  171.  
  172.  
  173. /**
  174.   * Nuke the navigation from the cache.
  175.   * Assumes the user is logged into the admin
  176.   **/
  177. static public function clearCache()
  178. {
  179. $subsite_id = $_SESSION['admin']['active_subsite'];
  180.  
  181. if (Kohana::config('cache.enabled')) {
  182. $cache = Cache::instance();
  183. $cache->delete("nav:{$subsite_id}");
  184. }
  185. }
  186.  
  187. /**
  188.   * Draws a pmm2 menu, with the current page highlighted in the menu
  189.   *
  190.   * @tag api
  191.   * @tag designer-api
  192.   **/
  193. static public function pmm2()
  194. {
  195. if (! self::$root_node) self::loadPageTree();
  196.  
  197. self::$root_node->filterChildren(new TreenodeInMenuMatcher());
  198.  
  199. $pmm_max_depth = (int) Kohana::config('sprout.pmm2_depth');
  200. $pmm_nav_limit = (int) Kohana::config('sprout.nav_limit');
  201.  
  202. if ($pmm_nav_limit == 0) $pmm_nav_limit = 999;
  203. if ($pmm_max_depth == 0) $pmm_max_depth = 2;
  204.  
  205.  
  206. echo '<ul class="p7PMM">';
  207.  
  208. // Home page
  209. if (Kohana::config('sprout.nav_home')) {
  210. $nav_id = ++self::$pmm2_nav_id;
  211. $home_url = Url::base();
  212. if (Url::current() == 'home_page') {
  213. echo "<li id=\"nav{$nav_id}\" class=\"on\"><a href=\"{$home_url}\" class=\"main\">Home</a></li>";
  214. } else {
  215. echo "<li id=\"nav{$nav_id}\"><a href=\"{$home_url}\" class=\"main\">Home</a></li>";
  216. }
  217. }
  218.  
  219. // All the other pages
  220. foreach (self::$root_node->children as $page) {
  221. self::pmm2Drawnode ($page, 1, $pmm_max_depth, $pmm_nav_limit);
  222. }
  223.  
  224. echo '</ul>', PHP_EOL;
  225.  
  226. self::$root_node->removeFilter();
  227. }
  228.  
  229.  
  230. /**
  231.   * Draws a single item, and its sub-items
  232.   *
  233.   * @param PageNode $node The node to draw
  234.   * @param int $depth The current depth of the tree
  235.   **/
  236. static private function pmm2Drawnode($node, $depth, $pmm_max_depth, $pmm_nav_limit)
  237. {
  238. $anc = array();
  239. if (self::$page_node_matcher != null) {
  240. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  241. if ($page_node) {
  242. $anc = $page_node->findAncestors();
  243. }
  244. }
  245.  
  246. if ($depth == 1) {
  247. // Top level items are WEIRD!
  248. $nav_id = ++self::$pmm2_nav_id;
  249. if ($nav_id > $pmm_nav_limit) return;
  250. if (in_array($node, $anc, true)) {
  251. echo "<li id=\"nav{$nav_id}\" class=\"on\">";
  252. } else {
  253. echo "<li id=\"nav{$nav_id}\">";
  254. }
  255. echo "<a href=\"{$node->getFriendlyUrl()}\" class=\"main\">", Enc::html($node->getNavigationName()), "</a>";
  256.  
  257. } else {
  258. // All other levels
  259. if (in_array($node, $anc, true)) {
  260. echo "<li class=\"on\"><a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  261. } else {
  262. echo "<li><a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  263. }
  264. }
  265.  
  266. // Draw its children
  267. if ($depth < $pmm_max_depth and count($node->children) > 0) {
  268. echo '<div><ul>';
  269. foreach ($node->children as $node) {
  270. self::pmm2Drawnode($node, $depth + 1, $pmm_max_depth, $pmm_nav_limit);
  271. }
  272. echo '</ul></div>';
  273. }
  274.  
  275. echo '</li>';
  276. }
  277.  
  278.  
  279. /**
  280.   * Draws a superfish menu, with the current page highlighted in the menu
  281.   **/
  282. static public function superfish()
  283. {
  284. if (! self::$root_node) self::loadPageTree();
  285.  
  286. self::$root_node->filterChildren(new TreenodeInMenuMatcher());
  287. self::$pmm2_nav_id = 0;
  288.  
  289. $pmm_max_depth = (int) Kohana::config('sprout.pmm2_depth');
  290. $pmm_nav_limit = (int) Kohana::config('sprout.nav_limit');
  291.  
  292. if ($pmm_nav_limit == 0) $pmm_nav_limit = 999;
  293. if ($pmm_max_depth == 0) $pmm_max_depth = 2;
  294.  
  295.  
  296. echo '<ul id="frankenmenu-list" class="-clearfix">';
  297.  
  298. // Home page
  299. if (Kohana::config('sprout.nav_home')) {
  300. $pmm_nav_limit--;
  301. $home_url = Url::base();
  302. if (Url::current() == 'home_page') {
  303. echo "<li class=\"on\"><a href=\"{$home_url}\">Home</a></li>";
  304. } else {
  305. echo "<li><a href=\"{$home_url}\">Home</a></li>";
  306. }
  307. }
  308.  
  309. // All the other pages
  310. foreach (self::$root_node->children as $page) {
  311. $pmm_nav_limit--;
  312. self::superfishDrawnode ($page, 1, $pmm_max_depth);
  313. if ($pmm_nav_limit == 0) break;
  314. }
  315.  
  316. echo '</ul>', PHP_EOL;
  317.  
  318. self::$root_node->removeFilter();
  319. }
  320.  
  321.  
  322. /**
  323.   * Private node drawing function for SuperFish menu.
  324.   **/
  325. static private function superfishDrawnode($node, $depth, $pmm_max_depth)
  326. {
  327. $anc = array();
  328. if (self::$page_node_matcher != null) {
  329. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  330. if ($page_node) {
  331. $anc = $page_node->findAncestors();
  332. }
  333. }
  334.  
  335. // All other levels
  336. if (in_array($node, $anc, true)) {
  337. echo "<li class=\"on\"><a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  338. } else {
  339. echo "<li><a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  340. }
  341.  
  342. // Draw its children
  343. if ($depth < $pmm_max_depth and count($node->children) > 0) {
  344. echo '<ul>';
  345. foreach ($node->children as $node) {
  346. self::superfishDrawnode($node, $depth + 1, $pmm_max_depth);
  347. }
  348. echo '</ul>';
  349. }
  350.  
  351. echo '</li>';
  352. }
  353.  
  354.  
  355. /**
  356.   * Draws a current-reveal menu.
  357.   * This is a menu where the top level items all get shown, but the children
  358.   * only get shown for active items.
  359.   *
  360.   * Uses the same HTML as the pmm2 menu, just because that HTML has plenty of
  361.   * classes and ids, and I already had the code for that menu :)
  362.   *
  363.   * See http://www.rymill.com.au/ for an example of this type of menu.
  364.   *
  365.   * @tag api
  366.   * @tag designer-api
  367.   **/
  368. static public function currentReveal()
  369. {
  370. if (! self::$root_node) self::loadPageTree();
  371.  
  372. self::$root_node->filterChildren(new TreenodeInMenuMatcher());
  373.  
  374. if (Kohana::config('sprout.nav_limit') == 0) {
  375. Kohana::configSet('sprout.nav_limit', 9999);
  376. }
  377.  
  378. echo "<ul>\n";
  379.  
  380. // Home page
  381. if (Kohana::config('sprout.nav_home')) {
  382. $nav_id = ++self::$pmm2_nav_id;
  383. $home_url = Url::base();
  384. if (Url::current() == 'home_page') {
  385. echo "<li id=\"nav{$nav_id}\" class=\"on\"><a href=\"{$home_url}\" class=\"main\">Home</a></li>";
  386. } else {
  387. echo "<li id=\"nav{$nav_id}\"><a href=\"{$home_url}\" class=\"main\">Home</a></li>";
  388. }
  389. }
  390.  
  391. // All the other pages
  392. foreach (self::$root_node->children as $page) {
  393. self::currentRevealDrawnode($page, 1);
  394. }
  395. echo "</ul>\n";
  396.  
  397. self::$root_node->removeFilter();
  398. }
  399.  
  400.  
  401. /**
  402.   * Draws a single item, and its sub-items
  403.   *
  404.   * @param PageNode $node The node to draw
  405.   * @param int $depth The current depth of the tree
  406.   **/
  407. static private function currentRevealDrawnode($node, $depth)
  408. {
  409. $anc = array();
  410. if (self::$page_node_matcher != null) {
  411. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  412. if ($page_node) {
  413. $anc = $page_node->findAncestors();
  414. }
  415. }
  416.  
  417. if ($depth == 1) {
  418. // Top level items are WEIRD!
  419. $nav_id = ++self::$pmm2_nav_id;
  420. if ($nav_id > Kohana::config('sprout.nav_limit')) return;
  421. if (in_array($node, $anc, true)) {
  422. echo "<li id=\"nav{$nav_id}\" class=\"on\">";
  423. } else {
  424. echo "<li id=\"nav{$nav_id}\">";
  425. }
  426. echo "<a href=\"{$node->getFriendlyUrl()}\" class=\"main\">", Enc::html($node->getNavigationName()), "</a>";
  427.  
  428. } else {
  429. // All other levels
  430. if (in_array($node, $anc, true)) {
  431. echo "<li class=\"on\"><a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  432. } else {
  433. echo "<li><a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  434. }
  435. }
  436.  
  437. $max_depth = (int) Kohana::config('sprout.current_reveal_depth');
  438. if ($max_depth == 0) $max_depth = 2;
  439.  
  440. // Draw its children
  441. if (in_array($node, $anc, true) and $depth < $max_depth and count($node->children) > 0) {
  442. echo "\n<div><ul>\n";
  443. foreach ($node->children as $node) {
  444. self::currentRevealDrawnode($node, $depth + 1);
  445. }
  446. echo "</ul></div>\n";
  447. }
  448.  
  449. echo "</li>\n";
  450. }
  451.  
  452.  
  453. /**
  454.   * Draws a UL of children for a zippmenu
  455.   **/
  456. static public function zippmenuChildren($page_url)
  457. {
  458. if (! self::$root_node) self::loadPageTree();
  459.  
  460. self::$root_node->filterChildren(new TreenodeInMenuMatcher());
  461.  
  462. $page = self::$root_node->findNode(new TreenodePathMatcher($page_url));
  463.  
  464. if ($page == null) return;
  465. if (count($page->children) == 0) return;
  466.  
  467.  
  468. // If there is a current page, note down the ancestors
  469. $anc = array();
  470. if (self::$page_node_matcher != null) {
  471. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  472. if ($page_node) {
  473. $anc = $page_node->findAncestors();
  474. }
  475. }
  476.  
  477. // Dump all children
  478. echo "<ul class=\"zippmenu-sub\">\n";
  479. foreach ($page->children as $child) {
  480. if (in_array($child, $anc, true)) {
  481. echo "<li class=\"on\"><a href=\"{$child->getFriendlyUrl()}\">", Enc::html($child->getNavigationName()), "</a>";
  482. } else {
  483. echo "<li><a href=\"{$child->getFriendlyUrl()}\">", Enc::html($child->getNavigationName()), "</a>";
  484. }
  485. }
  486. echo "</ul>\n";
  487.  
  488. self::$root_node->removeFilter();
  489. }
  490.  
  491.  
  492. /**
  493.   * Renders breadcrumbs for the current page
  494.   *
  495.   * @param string $seperator_front The separator to use. Defaults to ' >> '
  496.   * @param array $post_crumbs Additional crumbs to add after the navigation ones. Should be in the format url => label
  497.   * @param string $seperator_back The separator for the closing tag, if needed.
  498.   * @return string HTML
  499.   **/
  500. static public function breadcrumb($seperator_front = ' &raquo; ', $post_crumbs = null, $seperator_back = '')
  501. {
  502. $crumbs = self::getCrumbs($post_crumbs);
  503. return self::renderCrumbs($crumbs, $seperator_front, $seperator_back);
  504. }
  505.  
  506.  
  507. /**
  508.   * @param array|null $post_crumbs
  509.   * @return array [ url, label ]
  510.   */
  511. static public function getCrumbs($post_crumbs = null)
  512. {
  513. if (! self::$root_node) self::loadPageTree();
  514.  
  515. // Load a page node from the page tree.
  516. if (self::$page_node_matcher == null) {
  517. return [];
  518. }
  519.  
  520. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  521. if ($page_node == null) {
  522. return [];
  523. }
  524.  
  525. // Generate the breadcrumbs. Will be generated in reverse order.
  526. $crumbs = array();
  527. $node = $page_node;
  528. while ($node['id'] != 0) {
  529. $crumbs[$node->getFriendlyUrl()] = $node->getNavigationName();
  530. $node = $node->parent;
  531. }
  532.  
  533. $home_url = Url::base();
  534. $crumbs[$home_url] = 'Home';
  535.  
  536. // Reverse the order of the breadcrumbs
  537. $crumbs = array_reverse($crumbs);
  538.  
  539. // Add in any extra crumbs
  540. if (is_array($post_crumbs)) {
  541. foreach ($post_crumbs as $url => $label) {
  542. if (!is_string($url)) $url = '';
  543. $crumbs[$url] = $label;
  544. }
  545. }
  546.  
  547. return $crumbs;
  548. }
  549.  
  550.  
  551. /**
  552.   * Renders a custom breadcrumb based on an array of crumbs
  553.   *
  554.   * @param array $crumbs Crumbs for the breadcrumb. Should be in the format url => label
  555.   * @param string $seperator_front The separator to use. Defaults to ' >> '
  556.   * @param string $seperator_back The separator for the closing tag, if needed.
  557.   * @return string HTML
  558.   */
  559. static public function renderCrumbs(array $crumbs, $seperator_front = ' &raquo; ', $seperator_back = '')
  560. {
  561. foreach ($crumbs as $url => $label) {
  562. if (!is_string($url)) $url = '';
  563. $bc[] = '<a href="' . Enc::html($url) . '">' . Enc::html($label) . '</a>';
  564. }
  565.  
  566. $c = array_pop($bc);
  567. $bc[] = '<span>' . strip_tags($c) . '</span>';
  568.  
  569. if (!empty($seperator_front) and !empty($seperator_back)) {
  570. foreach ($bc as &$crumb) {
  571. $crumb = $seperator_front . $crumb . $seperator_back;
  572. }
  573. return implode('', $bc);
  574. }
  575.  
  576. return implode($seperator_front, $bc);
  577. }
  578.  
  579.  
  580. /**
  581.   * Renders a custom breadcrumb based on an array of crumbs.
  582.   * This includes a home tag.
  583.   *
  584.   * @param array $crumbs Crumbs for the breadcrumb. Should be in the format url => label
  585.   * @param string $seperator_front The separator to use. Defaults to ' >> '
  586.   * @param string $seperator_back The separator for the closing tag, if needed.
  587.   * @return string HTML
  588.   **/
  589. static public function customBreadcrumb(array $crumbs, $seperator_front = ' &raquo; ', $seperator_back = '')
  590. {
  591. $crumbs = [ 'SITE/' => 'Home'] + $crumbs;
  592. return self::renderCrumbs($crumbs, $seperator_front, $seperator_back);
  593. }
  594.  
  595.  
  596. /**
  597.   * Generates a menu which does not have dropdowns, but just has the top-level items
  598.   *
  599.   * @tag api
  600.   * @tag designer-api
  601.   **/
  602. static public function nonDropdown()
  603. {
  604. if (! self::$root_node) self::loadPageTree();
  605.  
  606. self::$root_node->filterChildren(new TreenodeInMenuMatcher());
  607.  
  608. if (Kohana::config('sprout.nav_limit') == 0) {
  609. Kohana::configSet('sprout.nav_limit', 9999);
  610. }
  611.  
  612. // Selected page detection
  613. $anc = array();
  614. if (self::$page_node_matcher != null) {
  615. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  616. if ($page_node) {
  617. $anc = $page_node->findAncestors();
  618. }
  619. }
  620.  
  621. echo "<ul>";
  622. $i = 0;
  623.  
  624. // Show home page
  625. if (Kohana::config('sprout.nav_home')) {
  626. $i++;
  627. $home_url = Url::base();
  628. if (Url::current() == 'home_page') {
  629. echo "<li class=\"nav{$i} on\"><a href=\"{$home_url}\">Home</a></li>";
  630. } else {
  631. echo "<li class=\"nav{$i}\"><a href=\"{$home_url}\">Home</a></li>";
  632. }
  633. }
  634.  
  635. // Show items
  636. foreach (self::$root_node->children as $page) {
  637. $i++;
  638. if ($i > Kohana::config('sprout.nav_limit')) break;
  639. if ($page === @$anc[0]) {
  640. echo "<li class=\"nav{$i} on\"><a href=\"{$page->getFriendlyUrl()}\">", Enc::html($page->getNavigationName()), "</a></li>";
  641. } else {
  642. echo "<li class=\"nav{$i}\"><a href=\"{$page->getFriendlyUrl()}\">", Enc::html($page->getNavigationName()), "</a></li>";
  643. }
  644. }
  645. echo "</ul>";
  646.  
  647. self::$root_node->removeFilter();
  648. }
  649.  
  650.  
  651. /**
  652.   * Builds the title that gets put into TITLE tags in the head of the page
  653.   * Just glues the site title onto the end of the page title
  654.   *
  655.   * @param string $page_title The title of the page
  656.   **/
  657. static public function buildBrowserTitle($page_title)
  658. {
  659. $format = Kohana::config('sprout.browser_title');
  660. if ($format == '') $format = '%1$s | %2$s';
  661.  
  662. return sprintf($format, $page_title, Kohana::config('sprout.site_title'));
  663. }
  664.  
  665.  
  666. /**
  667.   * Returns an array of all pages.
  668.   **/
  669. static public function getAllPages($parent = null, $indent = 0)
  670. {
  671. if (! self::$root_node) self::loadPageTree();
  672.  
  673. if ($parent == null) {
  674. $parent = self::$root_node;
  675. }
  676.  
  677. $pages = array();
  678. foreach ($parent->children as $page) {
  679. // [0] => id, [1] => name, [2] = url
  680. $pages[] = array(
  681. $page['id'],
  682. str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $indent) . $page->getNavigationName(),
  683. $page->getFriendlyUrl(),
  684. );
  685.  
  686. $children = self::getAllPages($page, $indent + 1);
  687. $pages = array_merge($pages, $children);
  688. }
  689.  
  690. return $pages;
  691. }
  692.  
  693.  
  694. /**
  695.   * Returns the filename to use for a banner image, or NULL if no appropriate banner was found
  696.   **/
  697. static public function banner()
  698. {
  699. if (! self::$root_node) self::loadPageTree();
  700.  
  701. if (! self::$page_node_matcher) return null;
  702.  
  703. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  704. if (! $page_node) return null;
  705.  
  706. return $page_node->getBanner();
  707. }
  708.  
  709.  
  710. /**
  711.   * Returns the name of this section, or the site name if no reasonable section can be determined.
  712.   **/
  713. static public function sectionName()
  714. {
  715. if (! self::$root_node) self::loadPageTree();
  716. if (! self::$page_node_matcher) return Kohana::config('sprout.site_title');
  717.  
  718. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  719. if (! $page_node) return Kohana::config('sprout.site_title');
  720.  
  721. $ancestors = $page_node->findAncestors();
  722. if (! $ancestors[0]) return Kohana::config('sprout.site_title');
  723.  
  724. return $ancestors[0]->getNavigationName();
  725. }
  726.  
  727.  
  728. /**
  729.   * Finds the node in the page tree which matched the specified {@see TreenodeMatcher}.
  730.   *
  731.   * @return Treenode|null null if no node is found, or if the matcher does not exist
  732.   */
  733. static public function matchedNode()
  734. {
  735. if (! self::$root_node) self::loadPageTree();
  736. if (! self::$page_node_matcher) return null;
  737.  
  738. return self::$root_node->findNode(self::$page_node_matcher);
  739. }
  740.  
  741. /**
  742.   * Alias for {@see Navigation::matchedNode}()
  743.   **/
  744. static public function getMatchedNode()
  745. {
  746. return self::matchedNode();
  747. }
  748.  
  749.  
  750. /**
  751.   * Returns a simple menu
  752.   * @return array [id, friendly-url, menu-name, childs array]
  753.   **/
  754. public static function simpleMenu()
  755. {
  756. if (! self::$root_node) self::loadPageTree();
  757.  
  758. self::$root_node->filterChildren(new TreenodeInMenuMatcher());
  759. self::$pmm2_nav_id = 0;
  760.  
  761. $pmm_max_depth = (int) Kohana::config('sprout.pmm2_depth');
  762. $pmm_nav_limit = (int) Kohana::config('sprout.nav_limit');
  763.  
  764. if ($pmm_nav_limit == 0) $pmm_nav_limit = 999;
  765. if ($pmm_max_depth == 0) $pmm_max_depth = 2;
  766.  
  767.  
  768. echo '<ul id="frankenmenu-list" class="-clearfix">';
  769.  
  770. // Home page
  771. if (Kohana::config('sprout.nav_home')) {
  772. $pmm_nav_limit--;
  773. $home_url = Url::base();
  774. if (Url::current() == 'home_page' || Url::current() == null) {
  775. echo "<li class=\"menu-item menu-item-depth1 menu-home-page menu-current-item\"><a href=\"{$home_url}\">Home</a></li>";
  776. } else {
  777. echo "<li class=\"menu-item menu-item-depth1 menu-home-page\"><a href=\"{$home_url}\">Home</a></li>";
  778. }
  779. }
  780.  
  781. // All the other pages
  782. foreach (self::$root_node->children as $page) {
  783. $pmm_nav_limit--;
  784. self::simpleMenuDrawnode ($page, 1, $pmm_max_depth);
  785. if ($pmm_nav_limit == 0) break;
  786. }
  787.  
  788. echo '</ul>', PHP_EOL;
  789.  
  790. self::$root_node->removeFilter();
  791. }
  792.  
  793. /**
  794.   * Private node drawing function for SuperFish menu.
  795.   **/
  796. static private function simpleMenuDrawnode($node, $depth, $pmm_max_depth)
  797. {
  798. $anc = array();
  799. $page_node = null;
  800.  
  801. if (self::$page_node_matcher != null) {
  802. $page_node = self::$root_node->findNode(self::$page_node_matcher);
  803. if ($page_node) {
  804. $anc = $page_node->findAncestors();
  805. }
  806. }
  807.  
  808. // Determine if this node has children
  809. $has_children = ($depth < $pmm_max_depth and count($node->children) > 0 ? true : false);
  810.  
  811. // Determine classes to be added to child
  812. $item_classes = "";
  813. if($has_children) $item_classes .= " menu-item-has-children";
  814.  
  815. // If the page is the current item
  816. if (Url::current() === $node->getFriendlyUrlNoprefix()) {
  817. $item_classes .= ' menu-current-item';
  818. } else if (in_array($node, $anc, true)) {
  819. $item_classes .= " menu-current-item-ancestor";
  820. }
  821.  
  822.  
  823. // All other levels
  824. echo "<li class=\"menu-item menu-item-depth{$depth}{$item_classes}\">";
  825. echo "<a href=\"{$node->getFriendlyUrl()}\">", Enc::html($node->getNavigationName()), "</a>";
  826.  
  827. // Draw its children
  828. if ($has_children) {
  829. echo "<ul class=\"sub-menu sub-menu-depth{$depth}\">";
  830. foreach ($node->children as $node) {
  831. self::simpleMenuDrawnode($node, $depth + 1, $pmm_max_depth);
  832. }
  833. echo '</ul>';
  834. }
  835.  
  836. echo '</li>';
  837. }
  838.  
  839. }
  840.