Menu.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_View
  17. * @subpackage Helper
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_View_Helper_Navigation_HelperAbstract
  24. */
  25. require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
  26. /**
  27. * Helper for rendering menus from navigation containers
  28. *
  29. * @category Zend
  30. * @package Zend_View
  31. * @subpackage Helper
  32. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_View_Helper_Navigation_Menu
  36. extends Zend_View_Helper_Navigation_HelperAbstract
  37. {
  38. /**
  39. * CSS class to use for the ul element
  40. *
  41. * @var string
  42. */
  43. protected $_ulClass = 'navigation';
  44. /**
  45. * Unique identifier (id) for the ul element
  46. *
  47. * @var string
  48. */
  49. protected $_ulId = null;
  50. /**
  51. * CSS class to use for the active elements
  52. *
  53. * @var string
  54. */
  55. protected $_activeClass = 'active';
  56. /**
  57. * CSS class to use for the parent li element
  58. *
  59. * @var string
  60. */
  61. protected $_parentClass = 'menu-parent';
  62. /**
  63. * Whether parent li elements should be rendered with parent class
  64. *
  65. * @var bool
  66. */
  67. protected $_renderParentClass = false;
  68. /**
  69. * Whether only active branch should be rendered
  70. *
  71. * @var bool
  72. */
  73. protected $_onlyActiveBranch = false;
  74. /**
  75. * Whether parents should be rendered when only rendering active branch
  76. *
  77. * @var bool
  78. */
  79. protected $_renderParents = true;
  80. /**
  81. * Partial view script to use for rendering menu
  82. *
  83. * @var string|array
  84. */
  85. protected $_partial = null;
  86. /**
  87. * Expand all sibling nodes of active branch nodes
  88. *
  89. * @var bool
  90. */
  91. protected $_expandSiblingNodesOfActiveBranch = false;
  92. /**
  93. * Adds CSS class from page to li element
  94. *
  95. * @var bool
  96. */
  97. protected $_addPageClassToLi = false;
  98. /**
  99. * Inner indentation string
  100. *
  101. * @var string
  102. */
  103. protected $_innerIndent = ' ';
  104. /**
  105. * View helper entry point:
  106. * Retrieves helper and optionally sets container to operate on
  107. *
  108. * @param Zend_Navigation_Container $container [optional] container to
  109. * operate on
  110. * @return Zend_View_Helper_Navigation_Menu fluent interface,
  111. * returns self
  112. */
  113. public function menu(Zend_Navigation_Container $container = null)
  114. {
  115. if (null !== $container) {
  116. $this->setContainer($container);
  117. }
  118. return $this;
  119. }
  120. // Accessors:
  121. /**
  122. * Sets CSS class to use for the first 'ul' element when rendering
  123. *
  124. * @param string $ulClass CSS class to set
  125. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  126. */
  127. public function setUlClass($ulClass)
  128. {
  129. if (is_string($ulClass)) {
  130. $this->_ulClass = $ulClass;
  131. }
  132. return $this;
  133. }
  134. /**
  135. * Returns CSS class to use for the first 'ul' element when rendering
  136. *
  137. * @return string CSS class
  138. */
  139. public function getUlClass()
  140. {
  141. return $this->_ulClass;
  142. }
  143. /**
  144. * Sets unique identifier (id) to use for the first 'ul' element when
  145. * rendering
  146. *
  147. * @param string|null $ulId Unique identifier (id) to set
  148. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  149. */
  150. public function setUlId($ulId)
  151. {
  152. if (is_string($ulId)) {
  153. $this->_ulId = $ulId;
  154. }
  155. return $this;
  156. }
  157. /**
  158. * Returns unique identifier (id) to use for the first 'ul' element when
  159. * rendering
  160. *
  161. * @return string|null Unique identifier (id); Default is 'null'
  162. */
  163. public function getUlId()
  164. {
  165. return $this->_ulId;
  166. }
  167. /**
  168. * Sets CSS class to use for the active elements when rendering
  169. *
  170. * @param string $activeClass CSS class to set
  171. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  172. */
  173. public function setActiveClass($activeClass)
  174. {
  175. if (is_string($activeClass)) {
  176. $this->_activeClass = $activeClass;
  177. }
  178. return $this;
  179. }
  180. /**
  181. * Returns CSS class to use for the active elements when rendering
  182. *
  183. * @return string CSS class
  184. */
  185. public function getActiveClass()
  186. {
  187. return $this->_activeClass;
  188. }
  189. /**
  190. * Sets CSS class to use for the parent li elements when rendering
  191. *
  192. * @param string $parentClass CSS class to set to parents
  193. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  194. */
  195. public function setParentClass($parentClass)
  196. {
  197. if (is_string($parentClass)) {
  198. $this->_parentClass = $parentClass;
  199. }
  200. return $this;
  201. }
  202. /**
  203. * Returns CSS class to use for the parent lie elements when rendering
  204. *
  205. * @return string CSS class
  206. */
  207. public function getParentClass()
  208. {
  209. return $this->_parentClass;
  210. }
  211. /**
  212. * Enables/disables rendering of parent class to the li element
  213. *
  214. * @param bool $flag [optional] render with parent
  215. * class. Default is true.
  216. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  217. */
  218. public function setRenderParentClass($flag = true)
  219. {
  220. $this->_renderParentClass = (bool) $flag;
  221. return $this;
  222. }
  223. /**
  224. * Returns flag indicating whether parent class should be rendered to the li
  225. * element
  226. *
  227. * @return bool whether parent class should be rendered
  228. */
  229. public function getRenderParentClass()
  230. {
  231. return $this->_renderParentClass;
  232. }
  233. /**
  234. * Sets a flag indicating whether only active branch should be rendered
  235. *
  236. * @param bool $flag [optional] render only active
  237. * branch. Default is true.
  238. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  239. */
  240. public function setOnlyActiveBranch($flag = true)
  241. {
  242. $this->_onlyActiveBranch = (bool) $flag;
  243. return $this;
  244. }
  245. /**
  246. * Returns a flag indicating whether only active branch should be rendered
  247. *
  248. * By default, this value is false, meaning the entire menu will be
  249. * be rendered.
  250. *
  251. * @return bool whether only active branch should be rendered
  252. */
  253. public function getOnlyActiveBranch()
  254. {
  255. return $this->_onlyActiveBranch;
  256. }
  257. /**
  258. * Sets a flag indicating whether to expand all sibling nodes of the active branch
  259. *
  260. * @param bool $flag [optional] expand all siblings of
  261. * nodes in the active branch. Default is true.
  262. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  263. */
  264. public function setExpandSiblingNodesOfActiveBranch($flag = true)
  265. {
  266. $this->_expandSiblingNodesOfActiveBranch = (bool) $flag;
  267. return $this;
  268. }
  269. /**
  270. * Returns a flag indicating whether to expand all sibling nodes of the active branch
  271. *
  272. * By default, this value is false, meaning the entire menu will be
  273. * be rendered.
  274. *
  275. * @return bool whether siblings of nodes in the active branch should be expanded
  276. */
  277. public function getExpandSiblingNodesOfActiveBranch()
  278. {
  279. return $this->_expandSiblingNodesOfActiveBranch;
  280. }
  281. /**
  282. * Enables/disables rendering of parents when only rendering active branch
  283. *
  284. * See {@link setOnlyActiveBranch()} for more information.
  285. *
  286. * @param bool $flag [optional] render parents when
  287. * rendering active branch.
  288. * Default is true.
  289. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  290. */
  291. public function setRenderParents($flag = true)
  292. {
  293. $this->_renderParents = (bool) $flag;
  294. return $this;
  295. }
  296. /**
  297. * Returns flag indicating whether parents should be rendered when rendering
  298. * only the active branch
  299. *
  300. * By default, this value is true.
  301. *
  302. * @return bool whether parents should be rendered
  303. */
  304. public function getRenderParents()
  305. {
  306. return $this->_renderParents;
  307. }
  308. /**
  309. * Sets which partial view script to use for rendering menu
  310. *
  311. * @param string|array $partial partial view script or null. If
  312. * an array is given, it is
  313. * expected to contain two values;
  314. * the partial view script to use,
  315. * and the module where the script
  316. * can be found.
  317. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  318. */
  319. public function setPartial($partial)
  320. {
  321. if (null === $partial || is_string($partial) || is_array($partial)) {
  322. $this->_partial = $partial;
  323. }
  324. return $this;
  325. }
  326. /**
  327. * Returns partial view script to use for rendering menu
  328. *
  329. * @return string|array|null
  330. */
  331. public function getPartial()
  332. {
  333. return $this->_partial;
  334. }
  335. /**
  336. * Adds CSS class from page to li element
  337. *
  338. * Before:
  339. * <code>
  340. * <li>
  341. * <a href="#" class="foo">Bar</a>
  342. * </li>
  343. * </code>
  344. *
  345. * After:
  346. * <code>
  347. * <li class="foo">
  348. * <a href="#">Bar</a>
  349. * </li>
  350. * </code>
  351. *
  352. * @param bool $flag [optional] adds CSS class from
  353. * page to li element
  354. *
  355. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  356. */
  357. public function addPageClassToLi($flag = true)
  358. {
  359. $this->_addPageClassToLi = (bool) $flag;
  360. return $this;
  361. }
  362. /**
  363. * Returns a flag indicating whether the CSS class from page to be added to
  364. * li element
  365. *
  366. * @return bool
  367. */
  368. public function getAddPageClassToLi()
  369. {
  370. return $this->_addPageClassToLi;
  371. }
  372. /**
  373. * Set the inner indentation string for using in {@link render()}, optionally
  374. * a number of spaces to indent with
  375. *
  376. * @param string|int $indent indentation string or
  377. * number of spaces
  378. * @return Zend_View_Helper_Navigation_HelperAbstract fluent interface,
  379. * returns self
  380. */
  381. public function setInnerIndent($indent)
  382. {
  383. $this->_innerIndent = $this->_getWhitespace($indent);
  384. return $this;
  385. }
  386. /**
  387. * Returns inner indentation (format output is respected)
  388. *
  389. * @see getFormatOutput()
  390. *
  391. * @return string indentation string or an empty string
  392. */
  393. public function getInnerIndent()
  394. {
  395. if (false === $this->getFormatOutput()) {
  396. return '';
  397. }
  398. return $this->_innerIndent;
  399. }
  400. // Public methods:
  401. /**
  402. * Returns an HTML string containing an 'a' element for the given page if
  403. * the page's href is not empty, and a 'span' element if it is empty
  404. *
  405. * Overrides {@link Zend_View_Helper_Navigation_Abstract::htmlify()}.
  406. *
  407. * @param Zend_Navigation_Page $page page to generate HTML for
  408. * @return string HTML string for the given page
  409. */
  410. public function htmlify(Zend_Navigation_Page $page)
  411. {
  412. // get label and title for translating
  413. $label = $page->getLabel();
  414. $title = $page->getTitle();
  415. // translate label and title?
  416. if ($this->getUseTranslator() && $t = $this->getTranslator()) {
  417. if (is_string($label) && !empty($label)) {
  418. $label = $t->translate($label);
  419. }
  420. if (is_string($title) && !empty($title)) {
  421. $title = $t->translate($title);
  422. }
  423. }
  424. // get attribs for element
  425. $attribs = array(
  426. 'id' => $page->getId(),
  427. 'title' => $title,
  428. );
  429. if (false === $this->getAddPageClassToLi()) {
  430. $attribs['class'] = $page->getClass();
  431. }
  432. // does page have a href?
  433. if ($href = $page->getHref()) {
  434. $element = 'a';
  435. $attribs['href'] = $href;
  436. $attribs['target'] = $page->getTarget();
  437. $attribs['accesskey'] = $page->getAccessKey();
  438. } else {
  439. $element = 'span';
  440. }
  441. // Add custom HTML attributes
  442. $attribs = array_merge($attribs, $page->getCustomHtmlAttribs());
  443. return '<' . $element . $this->_htmlAttribs($attribs) . '>'
  444. . $this->view->escape($label)
  445. . '</' . $element . '>';
  446. }
  447. /**
  448. * Normalizes given render options
  449. *
  450. * @param array $options [optional] options to normalize
  451. * @return array normalized options
  452. */
  453. protected function _normalizeOptions(array $options = array())
  454. {
  455. // Ident
  456. if (isset($options['indent'])) {
  457. $options['indent'] = $this->_getWhitespace($options['indent']);
  458. } else {
  459. $options['indent'] = $this->getIndent();
  460. }
  461. // Inner ident
  462. if (isset($options['innerIndent'])) {
  463. $options['innerIndent'] =
  464. $this->_getWhitespace($options['innerIndent']);
  465. } else {
  466. $options['innerIndent'] = $this->getInnerIndent();
  467. }
  468. // UL class
  469. if (isset($options['ulClass']) && $options['ulClass'] !== null) {
  470. $options['ulClass'] = (string) $options['ulClass'];
  471. } else {
  472. $options['ulClass'] = $this->getUlClass();
  473. }
  474. // UL id
  475. if (isset($options['ulId']) && $options['ulId'] !== null) {
  476. $options['ulId'] = (string) $options['ulId'];
  477. } else {
  478. $options['ulId'] = $this->getUlId();
  479. }
  480. // Active class
  481. if (isset($options['activeClass']) && $options['activeClass'] !== null
  482. ) {
  483. $options['activeClass'] = (string) $options['activeClass'];
  484. } else {
  485. $options['activeClass'] = $this->getActiveClass();
  486. }
  487. // Parent class
  488. if (isset($options['parentClass']) && $options['parentClass'] !== null) {
  489. $options['parentClass'] = (string) $options['parentClass'];
  490. } else {
  491. $options['parentClass'] = $this->getParentClass();
  492. }
  493. // Minimum depth
  494. if (array_key_exists('minDepth', $options)) {
  495. if (null !== $options['minDepth']) {
  496. $options['minDepth'] = (int) $options['minDepth'];
  497. }
  498. } else {
  499. $options['minDepth'] = $this->getMinDepth();
  500. }
  501. if ($options['minDepth'] < 0 || $options['minDepth'] === null) {
  502. $options['minDepth'] = 0;
  503. }
  504. // Maximum depth
  505. if (array_key_exists('maxDepth', $options)) {
  506. if (null !== $options['maxDepth']) {
  507. $options['maxDepth'] = (int) $options['maxDepth'];
  508. }
  509. } else {
  510. $options['maxDepth'] = $this->getMaxDepth();
  511. }
  512. // Only active branch
  513. if (!isset($options['onlyActiveBranch'])) {
  514. $options['onlyActiveBranch'] = $this->getOnlyActiveBranch();
  515. }
  516. // Expand sibling nodes of active branch
  517. if (!isset($options['expandSiblingNodesOfActiveBranch'])) {
  518. $options['expandSiblingNodesOfActiveBranch'] = $this->getExpandSiblingNodesOfActiveBranch();
  519. }
  520. // Render parents?
  521. if (!isset($options['renderParents'])) {
  522. $options['renderParents'] = $this->getRenderParents();
  523. }
  524. // Render parent class?
  525. if (!isset($options['renderParentClass'])) {
  526. $options['renderParentClass'] = $this->getRenderParentClass();
  527. }
  528. // Add page CSS class to LI element
  529. if (!isset($options['addPageClassToLi'])) {
  530. $options['addPageClassToLi'] = $this->getAddPageClassToLi();
  531. }
  532. return $options;
  533. }
  534. // Render methods:
  535. /**
  536. * Renders the deepest active menu within [$minDepth, $maxDeth], (called
  537. * from {@link renderMenu()})
  538. *
  539. * @param Zend_Navigation_Container $container container to render
  540. * @param string $ulClass CSS class for first UL
  541. * @param string $indent initial indentation
  542. * @param string $innerIndent inner indentation
  543. * @param int|null $minDepth minimum depth
  544. * @param int|null $maxDepth maximum depth
  545. * @param string|null $ulId unique identifier (id)
  546. * for first UL
  547. * @param bool $addPageClassToLi adds CSS class from
  548. * page to li element
  549. * @param string|null $activeClass CSS class for active
  550. * element
  551. * @param string $parentClass CSS class for parent
  552. * li's
  553. * @param bool $renderParentClass Render parent class?
  554. * @return string rendered menu (HTML)
  555. */
  556. protected function _renderDeepestMenu(Zend_Navigation_Container $container,
  557. $ulClass,
  558. $indent,
  559. $innerIndent,
  560. $minDepth,
  561. $maxDepth,
  562. $ulId,
  563. $addPageClassToLi,
  564. $activeClass,
  565. $parentClass,
  566. $renderParentClass)
  567. {
  568. if (!$active = $this->findActive($container, $minDepth - 1, $maxDepth)) {
  569. return '';
  570. }
  571. // special case if active page is one below minDepth
  572. if ($active['depth'] < $minDepth) {
  573. if (!$active['page']->hasPages()) {
  574. return '';
  575. }
  576. } else if (!$active['page']->hasPages()) {
  577. // found pages has no children; render siblings
  578. $active['page'] = $active['page']->getParent();
  579. } else if (is_int($maxDepth) && $active['depth'] + 1 > $maxDepth) {
  580. // children are below max depth; render siblings
  581. $active['page'] = $active['page']->getParent();
  582. }
  583. $attribs = array(
  584. 'class' => $ulClass,
  585. 'id' => $ulId,
  586. );
  587. // We don't need a prefix for the menu ID (backup)
  588. $skipValue = $this->_skipPrefixForId;
  589. $this->skipPrefixForId();
  590. $html = $indent . '<ul'
  591. . $this->_htmlAttribs($attribs)
  592. . '>'
  593. . $this->getEOL();
  594. // Reset prefix for IDs
  595. $this->_skipPrefixForId = $skipValue;
  596. foreach ($active['page'] as $subPage) {
  597. if (!$this->accept($subPage)) {
  598. continue;
  599. }
  600. $liClass = '';
  601. if ($subPage->isActive(true) && $addPageClassToLi) {
  602. $liClass = $this->_htmlAttribs(
  603. array('class' => $activeClass . ' ' . $subPage->getClass())
  604. );
  605. } else if ($subPage->isActive(true)) {
  606. $liClass = $this->_htmlAttribs(array('class' => $activeClass));
  607. } else if ($addPageClassToLi) {
  608. $liClass = $this->_htmlAttribs(
  609. array('class' => $subPage->getClass())
  610. );
  611. }
  612. $html .= $indent . $innerIndent . '<li' . $liClass . '>' . $this->getEOL();
  613. $html .= $indent . str_repeat($innerIndent, 2) . $this->htmlify($subPage)
  614. . $this->getEOL();
  615. $html .= $indent . $innerIndent . '</li>' . $this->getEOL();
  616. }
  617. $html .= $indent . '</ul>';
  618. return $html;
  619. }
  620. /**
  621. * Renders a normal menu (called from {@link renderMenu()})
  622. *
  623. * @param Zend_Navigation_Container $container container to render
  624. * @param string $ulClass CSS class for first UL
  625. * @param string $indent initial indentation
  626. * @param string $innerIndent inner indentation
  627. * @param int|null $minDepth minimum depth
  628. * @param int|null $maxDepth maximum depth
  629. * @param bool $onlyActive render only active branch?
  630. * @param bool $expandSibs render siblings of active
  631. * branch nodes?
  632. * @param string|null $ulId unique identifier (id)
  633. * for first UL
  634. * @param bool $addPageClassToLi adds CSS class from
  635. * page to li element
  636. * @param string|null $activeClass CSS class for active
  637. * element
  638. * @param string $parentClass CSS class for parent
  639. * li's
  640. * @param bool $renderParentClass Render parent class?
  641. * @return string rendered menu (HTML)
  642. */
  643. protected function _renderMenu(Zend_Navigation_Container $container,
  644. $ulClass,
  645. $indent,
  646. $innerIndent,
  647. $minDepth,
  648. $maxDepth,
  649. $onlyActive,
  650. $expandSibs,
  651. $ulId,
  652. $addPageClassToLi,
  653. $activeClass,
  654. $parentClass,
  655. $renderParentClass)
  656. {
  657. $html = '';
  658. // find deepest active
  659. if ($found = $this->findActive($container, $minDepth, $maxDepth)) {
  660. $foundPage = $found['page'];
  661. $foundDepth = $found['depth'];
  662. } else {
  663. $foundPage = null;
  664. }
  665. // create iterator
  666. $iterator = new RecursiveIteratorIterator($container,
  667. RecursiveIteratorIterator::SELF_FIRST);
  668. if (is_int($maxDepth)) {
  669. $iterator->setMaxDepth($maxDepth);
  670. }
  671. // iterate container
  672. $prevDepth = -1;
  673. foreach ($iterator as $page) {
  674. $depth = $iterator->getDepth();
  675. $isActive = $page->isActive(true);
  676. if ($depth < $minDepth || !$this->accept($page)) {
  677. // page is below minDepth or not accepted by acl/visibilty
  678. continue;
  679. } else if ($expandSibs && $depth > $minDepth) {
  680. // page is not active itself, but might be in the active branch
  681. $accept = false;
  682. if ($foundPage) {
  683. if ($foundPage->hasPage($page)) {
  684. // accept if page is a direct child of the active page
  685. $accept = true;
  686. } else if ($page->getParent()->isActive(true)) {
  687. // page is a sibling of the active branch...
  688. $accept = true;
  689. }
  690. }
  691. if (!$isActive && !$accept) {
  692. continue;
  693. }
  694. } else if ($onlyActive && !$isActive) {
  695. // page is not active itself, but might be in the active branch
  696. $accept = false;
  697. if ($foundPage) {
  698. if ($foundPage->hasPage($page)) {
  699. // accept if page is a direct child of the active page
  700. $accept = true;
  701. } else if ($foundPage->getParent()->hasPage($page)) {
  702. // page is a sibling of the active page...
  703. if (!$foundPage->hasPages() ||
  704. is_int($maxDepth) && $foundDepth + 1 > $maxDepth) {
  705. // accept if active page has no children, or the
  706. // children are too deep to be rendered
  707. $accept = true;
  708. }
  709. }
  710. }
  711. if (!$accept) {
  712. continue;
  713. }
  714. }
  715. // make sure indentation is correct
  716. $depth -= $minDepth;
  717. $myIndent = $indent . str_repeat($innerIndent, $depth * 2);
  718. if ($depth > $prevDepth) {
  719. $attribs = array();
  720. // start new ul tag
  721. if (0 == $depth) {
  722. $attribs = array(
  723. 'class' => $ulClass,
  724. 'id' => $ulId,
  725. );
  726. }
  727. // We don't need a prefix for the menu ID (backup)
  728. $skipValue = $this->_skipPrefixForId;
  729. $this->skipPrefixForId();
  730. $html .= $myIndent . '<ul'
  731. . $this->_htmlAttribs($attribs)
  732. . '>'
  733. . $this->getEOL();
  734. // Reset prefix for IDs
  735. $this->_skipPrefixForId = $skipValue;
  736. } else if ($prevDepth > $depth) {
  737. // close li/ul tags until we're at current depth
  738. for ($i = $prevDepth; $i > $depth; $i--) {
  739. $ind = $indent . str_repeat($innerIndent, $i * 2);
  740. $html .= $ind . $innerIndent . '</li>' . $this->getEOL();
  741. $html .= $ind . '</ul>' . $this->getEOL();
  742. }
  743. // close previous li tag
  744. $html .= $myIndent . $innerIndent . '</li>' . $this->getEOL();
  745. } else {
  746. // close previous li tag
  747. $html .= $myIndent . $innerIndent . '</li>' . $this->getEOL();
  748. }
  749. // render li tag and page
  750. $liClasses = array();
  751. // Is page active?
  752. if ($isActive) {
  753. $liClasses[] = $activeClass;
  754. }
  755. // Add CSS class from page to LI?
  756. if ($addPageClassToLi) {
  757. $liClasses[] = $page->getClass();
  758. }
  759. // Add CSS class for parents to LI?
  760. if ($renderParentClass && $page->hasChildren()) {
  761. // Check max depth
  762. if ((is_int($maxDepth) && ($depth + 1 < $maxDepth))
  763. || !is_int($maxDepth)
  764. ) {
  765. $liClasses[] = $parentClass;
  766. }
  767. }
  768. $html .= $myIndent . $innerIndent . '<li'
  769. . $this->_htmlAttribs(array('class' => implode(' ', $liClasses)))
  770. . '>' . $this->getEOL()
  771. . $myIndent . str_repeat($innerIndent, 2)
  772. . $this->htmlify($page)
  773. . $this->getEOL();
  774. // store as previous depth for next iteration
  775. $prevDepth = $depth;
  776. }
  777. if ($html) {
  778. // done iterating container; close open ul/li tags
  779. for ($i = $prevDepth+1; $i > 0; $i--) {
  780. $myIndent = $indent . str_repeat($innerIndent . $innerIndent, $i - 1);
  781. $html .= $myIndent . $innerIndent . '</li>' . $this->getEOL()
  782. . $myIndent . '</ul>' . $this->getEOL();
  783. }
  784. $html = rtrim($html, $this->getEOL());
  785. }
  786. return $html;
  787. }
  788. /**
  789. * Renders helper
  790. *
  791. * Renders a HTML 'ul' for the given $container. If $container is not given,
  792. * the container registered in the helper will be used.
  793. *
  794. * Available $options:
  795. *
  796. *
  797. * @param Zend_Navigation_Container $container [optional] container to
  798. * create menu from. Default
  799. * is to use the container
  800. * retrieved from
  801. * {@link getContainer()}.
  802. * @param array $options [optional] options for
  803. * controlling rendering
  804. * @return string rendered menu
  805. */
  806. public function renderMenu(Zend_Navigation_Container $container = null,
  807. array $options = array())
  808. {
  809. if (null === $container) {
  810. $container = $this->getContainer();
  811. }
  812. $options = $this->_normalizeOptions($options);
  813. if ($options['onlyActiveBranch'] && !$options['renderParents']) {
  814. $html = $this->_renderDeepestMenu(
  815. $container,
  816. $options['ulClass'],
  817. $options['indent'],
  818. $options['innerIndent'],
  819. $options['minDepth'],
  820. $options['maxDepth'],
  821. $options['ulId'],
  822. $options['addPageClassToLi'],
  823. $options['activeClass'],
  824. $options['parentClass'],
  825. $options['renderParentClass']
  826. );
  827. } else {
  828. $html = $this->_renderMenu(
  829. $container,
  830. $options['ulClass'],
  831. $options['indent'],
  832. $options['innerIndent'],
  833. $options['minDepth'],
  834. $options['maxDepth'],
  835. $options['onlyActiveBranch'],
  836. $options['expandSiblingNodesOfActiveBranch'],
  837. $options['ulId'],
  838. $options['addPageClassToLi'],
  839. $options['activeClass'],
  840. $options['parentClass'],
  841. $options['renderParentClass']
  842. );
  843. }
  844. return $html;
  845. }
  846. /**
  847. * Renders the inner-most sub menu for the active page in the $container
  848. *
  849. * This is a convenience method which is equivalent to the following call:
  850. * <code>
  851. * renderMenu($container, array(
  852. * 'indent' => $indent,
  853. * 'ulClass' => $ulClass,
  854. * 'minDepth' => null,
  855. * 'maxDepth' => null,
  856. * 'onlyActiveBranch' => true,
  857. * 'renderParents' => false
  858. * ));
  859. * </code>
  860. *
  861. * @param Zend_Navigation_Container $container [optional] container to
  862. * render. Default is to render
  863. * the container registered in
  864. * the helper.
  865. * @param string|null $ulClass [optional] CSS class to
  866. * use for UL element. Default
  867. * is to use the value from
  868. * {@link getUlClass()}.
  869. * @param string|int $indent [optional] indentation as
  870. * a string or number of
  871. * spaces. Default is to use
  872. * the value retrieved from
  873. * {@link getIndent()}.
  874. * @param string|null $ulId [optional] Unique identifier
  875. * (id) use for UL element
  876. * @param bool $addPageClassToLi adds CSS class from
  877. * page to li element
  878. * @param string|int $innerIndent [optional] inner
  879. * indentation as a string
  880. * or number of spaces.
  881. * Default is to use the
  882. * {@link getInnerIndent()}.
  883. * @return string rendered content
  884. */
  885. public function renderSubMenu(Zend_Navigation_Container $container = null,
  886. $ulClass = null,
  887. $indent = null,
  888. $ulId = null,
  889. $addPageClassToLi = false,
  890. $innerIndent = null)
  891. {
  892. return $this->renderMenu($container, array(
  893. 'indent' => $indent,
  894. 'innerIndent' => $innerIndent,
  895. 'ulClass' => $ulClass,
  896. 'minDepth' => null,
  897. 'maxDepth' => null,
  898. 'onlyActiveBranch' => true,
  899. 'renderParents' => false,
  900. 'ulId' => $ulId,
  901. 'addPageClassToLi' => $addPageClassToLi,
  902. ));
  903. }
  904. /**
  905. * Renders the given $container by invoking the partial view helper
  906. *
  907. * The container will simply be passed on as a model to the view script
  908. * as-is, and will be available in the partial script as 'container', e.g.
  909. * <code>echo 'Number of pages: ', count($this->container);</code>.
  910. *
  911. * @param Zend_Navigation_Container $container [optional] container to
  912. * pass to view script. Default
  913. * is to use the container
  914. * registered in the helper.
  915. * @param string|array $partial [optional] partial view
  916. * script to use. Default is to
  917. * use the partial registered
  918. * in the helper. If an array
  919. * is given, it is expected to
  920. * contain two values; the
  921. * partial view script to use,
  922. * and the module where the
  923. * script can be found.
  924. * @return string helper output
  925. *
  926. * @throws Zend_View_Exception When no partial script is set
  927. */
  928. public function renderPartial(Zend_Navigation_Container $container = null,
  929. $partial = null)
  930. {
  931. if (null === $container) {
  932. $container = $this->getContainer();
  933. }
  934. if (null === $partial) {
  935. $partial = $this->getPartial();
  936. }
  937. if (empty($partial)) {
  938. require_once 'Zend/View/Exception.php';
  939. $e = new Zend_View_Exception(
  940. 'Unable to render menu: No partial view script provided'
  941. );
  942. $e->setView($this->view);
  943. throw $e;
  944. }
  945. $model = array(
  946. 'container' => $container
  947. );
  948. if (is_array($partial)) {
  949. if (count($partial) != 2) {
  950. require_once 'Zend/View/Exception.php';
  951. $e = new Zend_View_Exception(
  952. 'Unable to render menu: A view partial supplied as '
  953. . 'an array must contain two values: partial view '
  954. . 'script and module where script can be found'
  955. );
  956. $e->setView($this->view);
  957. throw $e;
  958. }
  959. return $this->view->partial($partial[0], $partial[1], $model);
  960. }
  961. return $this->view->partial($partial, null, $model);
  962. }
  963. // Zend_View_Helper_Navigation_Helper:
  964. /**
  965. * Renders menu
  966. *
  967. * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
  968. *
  969. * If a partial view is registered in the helper, the menu will be rendered
  970. * using the given partial script. If no partial is registered, the menu
  971. * will be rendered as an 'ul' element by the helper's internal method.
  972. *
  973. * @see renderPartial()
  974. * @see renderMenu()
  975. *
  976. * @param Zend_Navigation_Container $container [optional] container to
  977. * render. Default is to
  978. * render the container
  979. * registered in the helper.
  980. * @return string helper output
  981. */
  982. public function render(Zend_Navigation_Container $container = null)
  983. {
  984. if ($partial = $this->getPartial()) {
  985. return $this->renderPartial($container, $partial);
  986. } else {
  987. return $this->renderMenu($container);
  988. }
  989. }
  990. }