2
0

Menu.php 36 KB

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