Issues

ZF-6941: Zend_View_Navigation_Menu expand sibling of active branch

Issue Type: Improvement Created: 2009-06-06T04:07:28.000+0000 Last Updated: 2013-01-22T10:44:12.000+0000 Status: Resolved Fix version(s): - 1.12.0 (27/Aug/12)

Reporter: Patrice De Saint Steban (lesgrumels) Assignee: Frank Brückner (frosch) Tags: - Zend_Navigation

  • Zend_View
  • FixForZF1.12
  • state:patch-ready-for-review
  • zf-caretaker-adamlundrigan
  • zf-crteam-priority
  • zf-crteam-review

Related issues: - ZF-8317

Attachments: - Zend-View-Helper-Navigation-Menu_expandBranch.patch

Description

I want an option to display the menu with all sibling of the active branch and not just the parents branch.

For the exemple in the view helpers documentation display the menu like this :

  • Foo Server ** FAQ ** Editions ** System Requirement
  • Foo Studio
  • Investor Relations
  • News
  • My Account
  • Forums

The code changed in the Menu helper, I had a new option parameter : "expandBranch"

<pre class="highlight">
    /**
     * Whether only active branch should be rendered
     *
     * @var bool
     */
    protected $_expandBranch = false;

    /**
     * Sets a flag indicating whether the first depth and expand the active branch
     *
     * @param  bool $flag                        [optional] render the first depth and the active branch
     *                                                      . Default is true.
     * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
     */
    public function setExpandBranch($flag = true)
    {
        $this->_expandBranch = (bool) $flag;
        return $this;
    }

    /**
     * Returns a flag indicating whether the first depth and the active branch should be rendered
     *
     * By default, this value is false, meaning the entire menu will be
     * be rendered.
     *
     * @return bool  whether only active branch should be rendered
     */
    public function getExpandBranch()
    {
        return $this->_expandBranch;
    }

    // Render methods:

    /**
     * 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?
     * @param  bool                      $expandBranch  render sibling of the active branch
     * @return string
     */
    protected function _renderMenu(Zend_Navigation_Container $container,
                                   $ulClass,
                                   $indent,
                                   $minDepth,
                                   $maxDepth,
                                   $onlyActive,
                                   $expandBranch)
    {
        $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;
        foreach ($iterator as $page) {
            $depth = $iterator->getDepth();
            $isActive = $page->isActive(true);
            if ($depth < $minDepth || !$this->accept($page)) {
                // page is below minDepth or not accepted by acl/visibilty
                continue;
            } else if ($expandBranch && $depth > $minDepth) { // If expand branch
                // 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;
                if ($foundPage) {
                    if ($foundPage->hasPage($page)) {
                        // accept if page is a direct child of the active page
                        $accept = true;
                    } else if ($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;
    }

    /**
     * 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['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'],
                                       $options['expandBranch']);
        }

        return $html;
    }

I just had this condition before the activeBranch condition :

<pre class="highlight">
          if ($expandBranch && $depth > $minDepth) { // If expand branch
                // 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;
                }         
            } 

Comments

Posted by Robin Skoglund (robinsk) on 2009-06-13T04:35:54.000+0000

Thanks for the detailed report. I see how this feature might be useful.

Have you signed a CLA? Would you care to implement the expandBranch feature and write tests and docs for it? You probably have a better overview of exactly what needs to be tested etc.

Posted by Patrice De Saint Steban (lesgrumels) on 2009-06-15T14:45:21.000+0000

Hello,

Yes I have signed a CLA. I can do it, I will send a patch as soon as possible.

Thanks.

Posted by Benjamin-Timm Broich (chameleon) on 2009-06-22T23:54:07.000+0000

Hope this improvement will find his way into the framework asap. ;)

Posted by Patrice De Saint Steban (lesgrumels) on 2009-06-23T07:49:15.000+0000

Hello,

This is the patch to add this improvement. There are the modification of the menu helper, tests, and documentation. I hope it's can be added in the next release.

Thanks.

Posted by Benjamin-Timm Broich (chameleon) on 2009-08-31T07:32:27.000+0000

Is there a new patch for the current ZF version (1.9.2) or is there another (new) way to solve what this improvements makes?

Posted by Eduard Zintz (ezintz) on 2009-09-01T02:54:48.000+0000

Has this patch already found his way to the framework? If not, when does the patch will be included?

Posted by Patrice De Saint Steban (lesgrumels) on 2009-09-01T03:16:27.000+0000

I don't found the path in changelog of new framework release. I don't know when the patch will be included.

I don't test the path on the ZF 1.9.2 version, but I think it will work with the version. There aren't many modification in the navigation helpers (has I see in the changelog). I will test soon with the last version.

For my project, I extend the menu helper to add the path code.

Thanks

Posted by Nikolay Goldschmidt (ccppprogrammer) on 2010-04-26T04:49:49.000+0000

Good job! Works like a charm on latest Zend Framework 1.9.3. I'm also vote for adding this patch to zf. Thanks!

Posted by Adam Lundrigan (adamlundrigan) on 2011-11-01T15:56:56.000+0000

Cleaned up patch so that it will apply cleanly against 1.11.11. Also renamed option/methods to better describe their purpose.

I've applied and tested the patch, with no ill-side-effects noted. All unit tests in Navigation and View_Helper groups pass after applying patch. The fix does, however, extend the public API of Zend_View_Helper_Navigation_Menu, so we will need to get approval from [~Matthew] before committing to SVN.

Posted by Frank Brückner (frosch) on 2011-11-01T16:14:05.000+0000

Hi Adam, I have tested your patch. Looks good and all unit tests are working.

Thanks for the clean up!

Posted by Adam Lundrigan (adamlundrigan) on 2012-05-31T12:31:54.000+0000

Fixed in trunk (1.12.0): r24839

Have you found an issue?

See the Overview section for more details.

Copyright

© 2006-2016 by Zend, a Rogue Wave Company. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.

Contacts