diff -r 5e7a0fedabdf -r 877f952ae2bd web/lib/Zend/View/Helper/Navigation/Menu.php --- a/web/lib/Zend/View/Helper/Navigation/Menu.php Thu Mar 21 17:31:31 2013 +0100 +++ b/web/lib/Zend/View/Helper/Navigation/Menu.php Thu Mar 21 19:50:53 2013 +0100 @@ -15,9 +15,9 @@ * @category Zend * @package Zend_View * @subpackage Helper - * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @version $Id: Menu.php 20096 2010-01-06 02:05:09Z bkarwin $ + * @version $Id: Menu.php 25239 2013-01-22 09:45:01Z frosch $ */ /** @@ -31,7 +31,7 @@ * @category Zend * @package Zend_View * @subpackage Helper - * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_View_Helper_Navigation_Menu @@ -45,6 +45,34 @@ protected $_ulClass = 'navigation'; /** + * Unique identifier (id) for the ul element + * + * @var string + */ + protected $_ulId = null; + + /** + * CSS class to use for the active elements + * + * @var string + */ + protected $_activeClass = 'active'; + + /** + * CSS class to use for the parent li element + * + * @var string + */ + protected $_parentClass = 'menu-parent'; + + /** + * Whether parent li elements should be rendered with parent class + * + * @var bool + */ + protected $_renderParentClass = false; + + /** * Whether only active branch should be rendered * * @var bool @@ -66,6 +94,27 @@ protected $_partial = null; /** + * Expand all sibling nodes of active branch nodes + * + * @var bool + */ + protected $_expandSiblingNodesOfActiveBranch = false; + + /** + * Adds CSS class from page to li element + * + * @var bool + */ + protected $_addPageClassToLi = false; + + /** + * Inner indentation string + * + * @var string + */ + protected $_innerIndent = ' '; + + /** * View helper entry point: * Retrieves helper and optionally sets container to operate on * @@ -111,6 +160,107 @@ } /** + * Sets unique identifier (id) to use for the first 'ul' element when + * rendering + * + * @param string|null $ulId Unique identifier (id) to set + * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self + */ + public function setUlId($ulId) + { + if (is_string($ulId)) { + $this->_ulId = $ulId; + } + + return $this; + } + + /** + * Returns unique identifier (id) to use for the first 'ul' element when + * rendering + * + * @return string|null Unique identifier (id); Default is 'null' + */ + public function getUlId() + { + return $this->_ulId; + } + + /** + * Sets CSS class to use for the active elements when rendering + * + * @param string $activeClass CSS class to set + * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self + */ + public function setActiveClass($activeClass) + { + if (is_string($activeClass)) { + $this->_activeClass = $activeClass; + } + + return $this; + } + + /** + * Returns CSS class to use for the active elements when rendering + * + * @return string CSS class + */ + public function getActiveClass() + { + return $this->_activeClass; + } + + /** + * Sets CSS class to use for the parent li elements when rendering + * + * @param string $parentClass CSS class to set to parents + * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self + */ + public function setParentClass($parentClass) + { + if (is_string($parentClass)) { + $this->_parentClass = $parentClass; + } + + return $this; + } + + /** + * Returns CSS class to use for the parent lie elements when rendering + * + * @return string CSS class + */ + public function getParentClass() + { + return $this->_parentClass; + } + + /** + * Enables/disables rendering of parent class to the li element + * + * @param bool $flag [optional] render with parent + * class. Default is true. + * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self + */ + public function setRenderParentClass($flag = true) + { + $this->_renderParentClass = (bool) $flag; + return $this; + } + + /** + * Returns flag indicating whether parent class should be rendered to the li + * element + * + * @return bool whether parent class should be rendered + */ + public function getRenderParentClass() + { + return $this->_renderParentClass; + } + + /** * Sets a flag indicating whether only active branch should be rendered * * @param bool $flag [optional] render only active @@ -135,8 +285,34 @@ { return $this->_onlyActiveBranch; } + + /** + * Sets a flag indicating whether to expand all sibling nodes of the active branch + * + * @param bool $flag [optional] expand all siblings of + * nodes in the active branch. Default is true. + * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self + */ + public function setExpandSiblingNodesOfActiveBranch($flag = true) + { + $this->_expandSiblingNodesOfActiveBranch = (bool) $flag; + return $this; + } /** + * Returns a flag indicating whether to expand all sibling nodes of the active branch + * + * By default, this value is false, meaning the entire menu will be + * be rendered. + * + * @return bool whether siblings of nodes in the active branch should be expanded + */ + public function getExpandSiblingNodesOfActiveBranch() + { + return $this->_expandSiblingNodesOfActiveBranch; + } + + /** * Enables/disables rendering of parents when only rendering active branch * * See {@link setOnlyActiveBranch()} for more information. @@ -195,6 +371,78 @@ return $this->_partial; } + /** + * Adds CSS class from page to li element + * + * Before: + * + *
  • + * Bar + *
  • + *
    + * + * After: + * + *
  • + * Bar + *
  • + *
    + * + * @param bool $flag [optional] adds CSS class from + * page to li element + * + * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self + */ + public function addPageClassToLi($flag = true) + { + $this->_addPageClassToLi = (bool) $flag; + + return $this; + } + + /** + * Returns a flag indicating whether the CSS class from page to be added to + * li element + * + * @return bool + */ + public function getAddPageClassToLi() + { + return $this->_addPageClassToLi; + } + + /** + * Set the inner indentation string for using in {@link render()}, optionally + * a number of spaces to indent with + * + * @param string|int $indent indentation string or + * number of spaces + * @return Zend_View_Helper_Navigation_HelperAbstract fluent interface, + * returns self + */ + public function setInnerIndent($indent) + { + $this->_innerIndent = $this->_getWhitespace($indent); + + return $this; + } + + /** + * Returns inner indentation (format output is respected) + * + * @see getFormatOutput() + * + * @return string indentation string or an empty string + */ + public function getInnerIndent() + { + if (false === $this->getFormatOutput()) { + return ''; + } + + return $this->_innerIndent; + } + // Public methods: /** @@ -226,18 +474,25 @@ $attribs = array( 'id' => $page->getId(), 'title' => $title, - 'class' => $page->getClass() ); + if (false === $this->getAddPageClassToLi()) { + $attribs['class'] = $page->getClass(); + } + // does page have a href? if ($href = $page->getHref()) { - $element = 'a'; - $attribs['href'] = $href; - $attribs['target'] = $page->getTarget(); + $element = 'a'; + $attribs['href'] = $href; + $attribs['target'] = $page->getTarget(); + $attribs['accesskey'] = $page->getAccessKey(); } else { $element = 'span'; } + // Add custom HTML attributes + $attribs = array_merge($attribs, $page->getCustomHtmlAttribs()); + return '<' . $element . $this->_htmlAttribs($attribs) . '>' . $this->view->escape($label) . ''; @@ -251,18 +506,51 @@ */ protected function _normalizeOptions(array $options = array()) { + // Ident if (isset($options['indent'])) { $options['indent'] = $this->_getWhitespace($options['indent']); } else { $options['indent'] = $this->getIndent(); } + // Inner ident + if (isset($options['innerIndent'])) { + $options['innerIndent'] = + $this->_getWhitespace($options['innerIndent']); + } else { + $options['innerIndent'] = $this->getInnerIndent(); + } + + // UL class if (isset($options['ulClass']) && $options['ulClass'] !== null) { $options['ulClass'] = (string) $options['ulClass']; } else { $options['ulClass'] = $this->getUlClass(); } + // UL id + if (isset($options['ulId']) && $options['ulId'] !== null) { + $options['ulId'] = (string) $options['ulId']; + } else { + $options['ulId'] = $this->getUlId(); + } + + // Active class + if (isset($options['activeClass']) && $options['activeClass'] !== null + ) { + $options['activeClass'] = (string) $options['activeClass']; + } else { + $options['activeClass'] = $this->getActiveClass(); + } + + // Parent class + if (isset($options['parentClass']) && $options['parentClass'] !== null) { + $options['parentClass'] = (string) $options['parentClass']; + } else { + $options['parentClass'] = $this->getParentClass(); + } + + // Minimum depth if (array_key_exists('minDepth', $options)) { if (null !== $options['minDepth']) { $options['minDepth'] = (int) $options['minDepth']; @@ -275,6 +563,7 @@ $options['minDepth'] = 0; } + // Maximum depth if (array_key_exists('maxDepth', $options)) { if (null !== $options['maxDepth']) { $options['maxDepth'] = (int) $options['maxDepth']; @@ -283,14 +572,31 @@ $options['maxDepth'] = $this->getMaxDepth(); } + // Only active branch if (!isset($options['onlyActiveBranch'])) { $options['onlyActiveBranch'] = $this->getOnlyActiveBranch(); } + // Expand sibling nodes of active branch + if (!isset($options['expandSiblingNodesOfActiveBranch'])) { + $options['expandSiblingNodesOfActiveBranch'] = $this->getExpandSiblingNodesOfActiveBranch(); + } + + // Render parents? if (!isset($options['renderParents'])) { $options['renderParents'] = $this->getRenderParents(); } + // Render parent class? + if (!isset($options['renderParentClass'])) { + $options['renderParentClass'] = $this->getRenderParentClass(); + } + + // Add page CSS class to LI element + if (!isset($options['addPageClassToLi'])) { + $options['addPageClassToLi'] = $this->getAddPageClassToLi(); + } + return $options; } @@ -300,19 +606,34 @@ * Renders the deepest active menu within [$minDepth, $maxDeth], (called * from {@link renderMenu()}) * - * @param Zend_Navigation_Container $container container to render - * @param array $active active page and depth - * @param string $ulClass CSS class for first UL - * @param string $indent initial indentation - * @param int|null $minDepth minimum depth - * @param int|null $maxDepth maximum depth - * @return string rendered menu + * @param Zend_Navigation_Container $container container to render + * @param string $ulClass CSS class for first UL + * @param string $indent initial indentation + * @param string $innerIndent inner indentation + * @param int|null $minDepth minimum depth + * @param int|null $maxDepth maximum depth + * @param string|null $ulId unique identifier (id) + * for first UL + * @param bool $addPageClassToLi adds CSS class from + * page to li element + * @param string|null $activeClass CSS class for active + * element + * @param string $parentClass CSS class for parent + * li's + * @param bool $renderParentClass Render parent class? + * @return string rendered menu (HTML) */ protected function _renderDeepestMenu(Zend_Navigation_Container $container, $ulClass, $indent, + $innerIndent, $minDepth, - $maxDepth) + $maxDepth, + $ulId, + $addPageClassToLi, + $activeClass, + $parentClass, + $renderParentClass) { if (!$active = $this->findActive($container, $minDepth - 1, $maxDepth)) { return ''; @@ -326,22 +647,49 @@ } else if (!$active['page']->hasPages()) { // found pages has no children; render siblings $active['page'] = $active['page']->getParent(); - } else if (is_int($maxDepth) && $active['depth'] +1 > $maxDepth) { + } else if (is_int($maxDepth) && $active['depth'] + 1 > $maxDepth) { // children are below max depth; render siblings $active['page'] = $active['page']->getParent(); } - $ulClass = $ulClass ? ' class="' . $ulClass . '"' : ''; - $html = $indent . '' . self::EOL; + $attribs = array( + 'class' => $ulClass, + 'id' => $ulId, + ); + + // We don't need a prefix for the menu ID (backup) + $skipValue = $this->_skipPrefixForId; + $this->skipPrefixForId(); + + $html = $indent . '_htmlAttribs($attribs) + . '>' + . $this->getEOL(); + + // Reset prefix for IDs + $this->_skipPrefixForId = $skipValue; foreach ($active['page'] as $subPage) { if (!$this->accept($subPage)) { continue; } - $liClass = $subPage->isActive(true) ? ' class="active"' : ''; - $html .= $indent . ' ' . self::EOL; - $html .= $indent . ' ' . $this->htmlify($subPage) . self::EOL; - $html .= $indent . ' ' . self::EOL; + + $liClass = ''; + if ($subPage->isActive(true) && $addPageClassToLi) { + $liClass = $this->_htmlAttribs( + array('class' => $activeClass . ' ' . $subPage->getClass()) + ); + } else if ($subPage->isActive(true)) { + $liClass = $this->_htmlAttribs(array('class' => $activeClass)); + } else if ($addPageClassToLi) { + $liClass = $this->_htmlAttribs( + array('class' => $subPage->getClass()) + ); + } + $html .= $indent . $innerIndent . '' . $this->getEOL(); + $html .= $indent . str_repeat($innerIndent, 2) . $this->htmlify($subPage) + . $this->getEOL(); + $html .= $indent . $innerIndent . '' . $this->getEOL(); } $html .= $indent . ''; @@ -352,20 +700,39 @@ /** * Renders a normal menu (called from {@link renderMenu()}) * - * @param Zend_Navigation_Container $container container to render - * @param string $ulClass CSS class for first UL - * @param string $indent initial indentation - * @param int|null $minDepth minimum depth - * @param int|null $maxDepth maximum depth - * @param bool $onlyActive render only active branch? - * @return string + * @param Zend_Navigation_Container $container container to render + * @param string $ulClass CSS class for first UL + * @param string $indent initial indentation + * @param string $innerIndent inner indentation + * @param int|null $minDepth minimum depth + * @param int|null $maxDepth maximum depth + * @param bool $onlyActive render only active branch? + * @param bool $expandSibs render siblings of active + * branch nodes? + * @param string|null $ulId unique identifier (id) + * for first UL + * @param bool $addPageClassToLi adds CSS class from + * page to li element + * @param string|null $activeClass CSS class for active + * element + * @param string $parentClass CSS class for parent + * li's + * @param bool $renderParentClass Render parent class? + * @return string rendered menu (HTML) */ protected function _renderMenu(Zend_Navigation_Container $container, $ulClass, $indent, + $innerIndent, $minDepth, $maxDepth, - $onlyActive) + $onlyActive, + $expandSibs, + $ulId, + $addPageClassToLi, + $activeClass, + $parentClass, + $renderParentClass) { $html = ''; @@ -392,6 +759,21 @@ if ($depth < $minDepth || !$this->accept($page)) { // page is below minDepth or not accepted by acl/visibilty continue; + } else if ($expandSibs && $depth > $minDepth) { + // page is not active itself, but might be in the active branch + $accept = false; + if ($foundPage) { + if ($foundPage->hasPage($page)) { + // accept if page is a direct child of the active page + $accept = true; + } else if ($page->getParent()->isActive(true)) { + // page is a sibling of the active branch... + $accept = true; + } + } + if (!$isActive && !$accept) { + continue; + } } else if ($onlyActive && !$isActive) { // page is not active itself, but might be in the active branch $accept = false; @@ -416,35 +798,71 @@ } // make sure indentation is correct - $depth -= $minDepth; - $myIndent = $indent . str_repeat(' ', $depth); + $depth -= $minDepth; + $myIndent = $indent . str_repeat($innerIndent, $depth * 2); if ($depth > $prevDepth) { + $attribs = array(); + // start new ul tag - if ($ulClass && $depth == 0) { - $ulClass = ' class="' . $ulClass . '"'; - } else { - $ulClass = ''; + if (0 == $depth) { + $attribs = array( + 'class' => $ulClass, + 'id' => $ulId, + ); } - $html .= $myIndent . '' . self::EOL; + + // We don't need a prefix for the menu ID (backup) + $skipValue = $this->_skipPrefixForId; + $this->skipPrefixForId(); + + $html .= $myIndent . '_htmlAttribs($attribs) + . '>' + . $this->getEOL(); + + // Reset prefix for IDs + $this->_skipPrefixForId = $skipValue; } else if ($prevDepth > $depth) { // close li/ul tags until we're at current depth for ($i = $prevDepth; $i > $depth; $i--) { - $ind = $indent . str_repeat(' ', $i); - $html .= $ind . ' ' . self::EOL; - $html .= $ind . '' . self::EOL; + $ind = $indent . str_repeat($innerIndent, $i * 2); + $html .= $ind . $innerIndent . '' . $this->getEOL(); + $html .= $ind . '' . $this->getEOL(); } // close previous li tag - $html .= $myIndent . ' ' . self::EOL; + $html .= $myIndent . $innerIndent . '' . $this->getEOL(); } else { // close previous li tag - $html .= $myIndent . ' ' . self::EOL; + $html .= $myIndent . $innerIndent . '' . $this->getEOL(); } // render li tag and page - $liClass = $isActive ? ' class="active"' : ''; - $html .= $myIndent . ' ' . self::EOL - . $myIndent . ' ' . $this->htmlify($page) . self::EOL; + $liClasses = array(); + // Is page active? + if ($isActive) { + $liClasses[] = $activeClass; + } + // Add CSS class from page to LI? + if ($addPageClassToLi) { + $liClasses[] = $page->getClass(); + } + // Add CSS class for parents to LI? + if ($renderParentClass && $page->hasChildren()) { + // Check max depth + if ((is_int($maxDepth) && ($depth + 1 < $maxDepth)) + || !is_int($maxDepth) + ) { + $liClasses[] = $parentClass; + } + } + + $html .= $myIndent . $innerIndent . '_htmlAttribs(array('class' => implode(' ', $liClasses))) + . '>' . $this->getEOL() + . $myIndent . str_repeat($innerIndent, 2) + . $this->htmlify($page) + . $this->getEOL(); // store as previous depth for next iteration $prevDepth = $depth; @@ -453,11 +871,11 @@ if ($html) { // done iterating container; close open ul/li tags for ($i = $prevDepth+1; $i > 0; $i--) { - $myIndent = $indent . str_repeat(' ', $i-1); - $html .= $myIndent . ' ' . self::EOL - . $myIndent . '' . self::EOL; + $myIndent = $indent . str_repeat($innerIndent . $innerIndent, $i - 1); + $html .= $myIndent . $innerIndent . '' . $this->getEOL() + . $myIndent . '' . $this->getEOL(); } - $html = rtrim($html, self::EOL); + $html = rtrim($html, $this->getEOL()); } return $html; @@ -491,18 +909,35 @@ $options = $this->_normalizeOptions($options); if ($options['onlyActiveBranch'] && !$options['renderParents']) { - $html = $this->_renderDeepestMenu($container, - $options['ulClass'], - $options['indent'], - $options['minDepth'], - $options['maxDepth']); + $html = $this->_renderDeepestMenu( + $container, + $options['ulClass'], + $options['indent'], + $options['innerIndent'], + $options['minDepth'], + $options['maxDepth'], + $options['ulId'], + $options['addPageClassToLi'], + $options['activeClass'], + $options['parentClass'], + $options['renderParentClass'] + ); } else { - $html = $this->_renderMenu($container, - $options['ulClass'], - $options['indent'], - $options['minDepth'], - $options['maxDepth'], - $options['onlyActiveBranch']); + $html = $this->_renderMenu( + $container, + $options['ulClass'], + $options['indent'], + $options['innerIndent'], + $options['minDepth'], + $options['maxDepth'], + $options['onlyActiveBranch'], + $options['expandSiblingNodesOfActiveBranch'], + $options['ulId'], + $options['addPageClassToLi'], + $options['activeClass'], + $options['parentClass'], + $options['renderParentClass'] + ); } return $html; @@ -527,7 +962,7 @@ * render. Default is to render * the container registered in * the helper. - * @param string $ulClass [optional] CSS class to + * @param string|null $ulClass [optional] CSS class to * use for UL element. Default * is to use the value from * {@link getUlClass()}. @@ -536,19 +971,34 @@ * spaces. Default is to use * the value retrieved from * {@link getIndent()}. - * @return string rendered content + * @param string|null $ulId [optional] Unique identifier + * (id) use for UL element + * @param bool $addPageClassToLi adds CSS class from + * page to li element + * @param string|int $innerIndent [optional] inner + * indentation as a string + * or number of spaces. + * Default is to use the + * {@link getInnerIndent()}. + * @return string rendered content */ public function renderSubMenu(Zend_Navigation_Container $container = null, $ulClass = null, - $indent = null) + $indent = null, + $ulId = null, + $addPageClassToLi = false, + $innerIndent = null) { return $this->renderMenu($container, array( 'indent' => $indent, + 'innerIndent' => $innerIndent, 'ulClass' => $ulClass, 'minDepth' => null, 'maxDepth' => null, 'onlyActiveBranch' => true, - 'renderParents' => false + 'renderParents' => false, + 'ulId' => $ulId, + 'addPageClassToLi' => $addPageClassToLi, )); } @@ -573,6 +1023,8 @@ * and the module where the * script can be found. * @return string helper output + * + * @throws Zend_View_Exception When no partial script is set */ public function renderPartial(Zend_Navigation_Container $container = null, $partial = null) @@ -602,8 +1054,8 @@ if (count($partial) != 2) { require_once 'Zend/View/Exception.php'; $e = new Zend_View_Exception( - 'Unable to render menu: A view partial supplied as ' - . 'an array must contain two values: partial view ' + 'Unable to render menu: A view partial supplied as ' + . 'an array must contain two values: partial view ' . 'script and module where script can be found' ); $e->setView($this->view);