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