_renderParentSiblings; } /** * Enables/disables rendering of parent's siblings when only rendering active branch * * See {@link setOnlyActiveBranch()} for more information. * * @param bool $flag [optional] render parent's siblings when * rendering active branch. * Default is false. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self */ public function setRenderParents($flag = false) { $this->_renderParentSiblings = (bool) $flag; return $this; } /** * Normalizes given render options * * @param array $options [optional] options to normalize * @return array normalized options */ protected function _normalizeOptions(array $options = array()) { $options = parent::_normalizeOptions($options); if (!isset($options['renderParentSiblings'])) { $options['renderParentSiblings'] = $this->getRenderParentSiblings(); } return $options; } /** * Renders helper * * Renders a HTML 'ul' for the given $container. If $container is not given, * the container registered in the helper will be used. * * Available $options: * * * @param Zend_Navigation_Container $container [optional] container to * create menu from. Default * is to use the container * retrieved from * {@link getContainer()}. * @param array $options [optional] options for * controlling rendering * @return string rendered menu */ public function renderMenu(Zend_Navigation_Container $container = null, array $options = array()) { if (null === $container) { $container = $this->getContainer(); } $options = $this->_normalizeOptions($options); if($options['renderParentSiblings']) { $html = $this->_renderMenuWithParentSiblings($container, $options['ulClass'], $options['indent'], $options['minDepth'], $options['maxDepth'], $options['onlyActiveBranch'], $options['renderParentSiblings']); } else if ($options['onlyActiveBranch'] && !$options['renderParents']) { $html = $this->_renderDeepestMenu($container, $options['ulClass'], $options['indent'], $options['minDepth'], $options['maxDepth']); } else { $html = $this->_renderMenu($container, $options['ulClass'], $options['indent'], $options['minDepth'], $options['maxDepth'], $options['onlyActiveBranch']); } return $html; } /** * Renders a menu with all the siblings * * @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? * @param bool $renderParentSiblings render all parent siblings * @return string */ protected function _renderMenuWithParentSiblings(Zend_Navigation_Container $container, $ulClass, $indent, $minDepth, $maxDepth, $onlyActive, $renderParentSiblings) { $html = ''; // find deepest active if ($found = $this->findActive($container, $minDepth, $maxDepth)) { $foundPage = $found['page']; $foundDepth = $found['depth']; } else { $foundPage = null; } // create iterator $iterator = new RecursiveIteratorIterator($container, RecursiveIteratorIterator::SELF_FIRST); if (is_int($maxDepth)) { $iterator->setMaxDepth($maxDepth); } // iterate container $prevDepth = -1; $siblings = array(); if($foundPage) { foreach ($iterator as $page) { $depth = $iterator->getDepth(); if($depth >= $minDepth) { if($page->hasPage($foundPage, true) || $page == $foundPage) { if(!is_null($page->getParent())) { foreach ($page->getParent()->getPages() as $sibling) { $siblings[] = $sibling; } } } } } } foreach ($iterator as $page) { $depth = $iterator->getDepth(); /* @var $page Zend_Navigation_Page */ $isActive = $page->isActive(true); if(in_array($page, $siblings) && $this->accept($page, true) && $depth >= $minDepth) { $accept = true; } else if ($depth < $minDepth || !$this->accept($page, true)) { // page is below minDepth or not accepted by acl/visibilty continue; } else if ($onlyActive && !$isActive) { // 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 (!is_null($foundPage->getParent()) && $foundPage->getParent()->hasPage($page)) { // page is a sibling of the active page... if (!$foundPage->hasPages() || is_int($maxDepth) && $foundDepth + 1 > $maxDepth) { // accept if active page has no children, or the // children are too deep to be rendered $accept = true; } } } if (!$accept) { continue; } } // make sure indentation is correct $depth -= $minDepth; $myIndent = $indent . str_repeat(' ', $depth); if ($depth > $prevDepth) { // start new ul tag if ($ulClass && $depth == 0) { $ulClass = ' class="' . $ulClass . '"'; } else { $ulClass = ''; } $html .= $myIndent . '' . self::EOL; } 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; } // close previous li tag $html .= $myIndent . ' ' . self::EOL; } else { // close previous li tag $html .= $myIndent . ' ' . self::EOL; } // render li tag and page $liClass = $isActive ? ' class="active"' : ''; $html .= $myIndent . ' ' . self::EOL . $myIndent . ' ' . $this->htmlify($page) . self::EOL; // store as previous depth for next iteration $prevDepth = $depth; } 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; } $html = rtrim($html, self::EOL); } return $html; } /** * OVERLOAD - invisible pages should be found back !! * * Finds the deepest active page in the given container * * @param Zend_Navigation_Container $container container to search * @param int|null $minDepth [optional] minimum depth * required for page to be * valid. Default is to use * {@link getMinDepth()}. A * null value means no minimum * depth required. * @param int|null $minDepth [optional] maximum depth * a page can have to be * valid. Default is to use * {@link getMaxDepth()}. A * null value means no maximum * depth required. * @return array an associative array with * the values 'depth' and * 'page', or an empty array * if not found */ public function findActive(Zend_Navigation_Container $container, $minDepth = null, $maxDepth = -1) { if (!is_int($minDepth)) { $minDepth = $this->getMinDepth(); } if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) { $maxDepth = $this->getMaxDepth(); } $found = null; $foundDepth = -1; $iterator = new RecursiveIteratorIterator($container, RecursiveIteratorIterator::CHILD_FIRST); foreach ($iterator as $page) { $currDepth = $iterator->getDepth(); // if ($currDepth < $minDepth || !$this->accept($page)) { // hidden pages should be found if ($currDepth < $minDepth) { // page is not accepted continue; } if ($page->isActive(false) && $currDepth > $foundDepth) { // found an active page at a deeper level than before $found = $page; $foundDepth = $currDepth; } } if (is_int($maxDepth) && $foundDepth > $maxDepth) { while ($foundDepth > $maxDepth) { if (--$foundDepth < $minDepth) { $found = null; break; } $found = $found->getParent(); if (!$found instanceof Zend_Navigation_Page) { $found = null; break; } } } if ($found) { return array('page' => $found, 'depth' => $foundDepth); } else { return array(); } } }