File indexing completed on 2025-03-02 05:29:58

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_View
0017  * @subpackage Helper
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_View_Helper_Navigation_HelperAbstract
0025  */
0026 // require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
0027 
0028 /**
0029  * Helper for rendering menus from navigation containers
0030  *
0031  * @category   Zend
0032  * @package    Zend_View
0033  * @subpackage Helper
0034  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0035  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0036  */
0037 class Zend_View_Helper_Navigation_Menu
0038     extends Zend_View_Helper_Navigation_HelperAbstract
0039 {
0040     /**
0041      * CSS class to use for the ul element
0042      *
0043      * @var string
0044      */
0045     protected $_ulClass = 'navigation';
0046 
0047     /**
0048      * Unique identifier (id) for the ul element
0049      *
0050      * @var string
0051      */
0052     protected $_ulId = null;
0053 
0054     /**
0055      * CSS class to use for the active elements
0056      *
0057      * @var string
0058      */
0059     protected $_activeClass = 'active';
0060 
0061     /**
0062      * CSS class to use for the parent li element
0063      *
0064      * @var string
0065      */
0066     protected $_parentClass = 'menu-parent';
0067 
0068     /**
0069      * Whether parent li elements should be rendered with parent class
0070      *
0071      * @var bool
0072      */
0073     protected $_renderParentClass = false;
0074 
0075     /**
0076      * Whether only active branch should be rendered
0077      *
0078      * @var bool
0079      */
0080     protected $_onlyActiveBranch = false;
0081 
0082     /**
0083      * Whether parents should be rendered when only rendering active branch
0084      *
0085      * @var bool
0086      */
0087     protected $_renderParents = true;
0088 
0089     /**
0090      * Partial view script to use for rendering menu
0091      *
0092      * @var string|array
0093      */
0094     protected $_partial = null;
0095 
0096     /**
0097      * Expand all sibling nodes of active branch nodes
0098      *
0099      * @var bool
0100      */
0101     protected $_expandSiblingNodesOfActiveBranch = false;
0102 
0103     /**
0104      * Adds CSS class from page to li element
0105      *
0106      * @var bool
0107      */
0108     protected $_addPageClassToLi = false;
0109 
0110     /**
0111      * Inner indentation string
0112      *
0113      * @var string
0114      */
0115     protected $_innerIndent = '    ';
0116     
0117     /**
0118      * View helper entry point:
0119      * Retrieves helper and optionally sets container to operate on
0120      *
0121      * @param  Zend_Navigation_Container $container  [optional] container to
0122      *                                               operate on
0123      * @return Zend_View_Helper_Navigation_Menu      fluent interface,
0124      *                                               returns self
0125      */
0126     public function menu(Zend_Navigation_Container $container = null)
0127     {
0128         if (null !== $container) {
0129             $this->setContainer($container);
0130         }
0131 
0132         return $this;
0133     }
0134 
0135     // Accessors:
0136 
0137     /**
0138      * Sets CSS class to use for the first 'ul' element when rendering
0139      *
0140      * @param  string $ulClass                   CSS class to set
0141      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
0142      */
0143     public function setUlClass($ulClass)
0144     {
0145         if (is_string($ulClass)) {
0146             $this->_ulClass = $ulClass;
0147         }
0148 
0149         return $this;
0150     }
0151 
0152     /**
0153      * Returns CSS class to use for the first 'ul' element when rendering
0154      *
0155      * @return string  CSS class
0156      */
0157     public function getUlClass()
0158     {
0159         return $this->_ulClass;
0160     }
0161 
0162     /**
0163      * Sets unique identifier (id) to use for the first 'ul' element when
0164      * rendering
0165      *
0166      * @param  string|null  $ulId                Unique identifier (id) to set
0167      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
0168      */
0169     public function setUlId($ulId)
0170     {
0171         if (is_string($ulId)) {
0172             $this->_ulId = $ulId;
0173         }
0174 
0175         return $this;
0176     }
0177 
0178     /**
0179      * Returns unique identifier (id) to use for the first 'ul' element when
0180      * rendering
0181      *
0182      * @return string|null  Unique identifier (id); Default is 'null'
0183      */
0184     public function getUlId()
0185     {
0186         return $this->_ulId;
0187     }
0188 
0189     /**
0190      * Sets CSS class to use for the active elements when rendering
0191      *
0192      * @param string $activeClass               CSS class to set
0193      * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
0194      */
0195     public function setActiveClass($activeClass)
0196     {
0197         if (is_string($activeClass)) {
0198             $this->_activeClass = $activeClass;
0199         }
0200 
0201         return $this;
0202     }
0203 
0204     /**
0205      * Returns CSS class to use for the active elements when rendering
0206      *
0207      * @return string  CSS class
0208      */
0209     public function getActiveClass()
0210     {
0211         return $this->_activeClass;
0212     }
0213 
0214     /**
0215      * Sets CSS class to use for the parent li elements when rendering
0216      *
0217      * @param  string $parentClass              CSS class to set to parents
0218      * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
0219      */
0220     public function setParentClass($parentClass)
0221     {
0222         if (is_string($parentClass)) {
0223             $this->_parentClass = $parentClass;
0224         }
0225 
0226         return $this;
0227     }
0228 
0229     /**
0230      * Returns CSS class to use for the parent lie elements when rendering
0231      *
0232      * @return string CSS class
0233      */
0234     public function getParentClass()
0235     {
0236         return $this->_parentClass;
0237     }
0238 
0239     /**
0240      * Enables/disables rendering of parent class to the li element
0241      *
0242      * @param bool $flag                        [optional] render with parent
0243      *                                          class. Default is true.
0244      * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
0245      */
0246     public function setRenderParentClass($flag = true)
0247     {
0248         $this->_renderParentClass = (bool) $flag;
0249         return $this;
0250     }
0251 
0252     /**
0253      * Returns flag indicating whether parent class should be rendered to the li
0254      * element
0255      *
0256      * @return bool  whether parent class should be rendered
0257      */
0258     public function getRenderParentClass()
0259     {
0260         return $this->_renderParentClass;
0261     }
0262 
0263     /**
0264      * Sets a flag indicating whether only active branch should be rendered
0265      *
0266      * @param  bool $flag                        [optional] render only active
0267      *                                           branch. Default is true.
0268      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
0269      */
0270     public function setOnlyActiveBranch($flag = true)
0271     {
0272         $this->_onlyActiveBranch = (bool) $flag;
0273         return $this;
0274     }
0275 
0276     /**
0277      * Returns a flag indicating whether only active branch should be rendered
0278      *
0279      * By default, this value is false, meaning the entire menu will be
0280      * be rendered.
0281      *
0282      * @return bool  whether only active branch should be rendered
0283      */
0284     public function getOnlyActiveBranch()
0285     {
0286         return $this->_onlyActiveBranch;
0287     }
0288     
0289     /**
0290      * Sets a flag indicating whether to expand all sibling nodes of the active branch
0291      * 
0292      * @param  bool $flag                        [optional] expand all siblings of
0293      *                                           nodes in the active branch. Default is true.
0294      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
0295      */
0296     public function setExpandSiblingNodesOfActiveBranch($flag = true)
0297     {
0298         $this->_expandSiblingNodesOfActiveBranch = (bool) $flag;
0299         return $this;
0300     }
0301 
0302     /**
0303      * Returns a flag indicating whether to expand all sibling nodes of the active branch
0304      *
0305      * By default, this value is false, meaning the entire menu will be
0306      * be rendered.
0307      *
0308      * @return bool  whether siblings of nodes in the active branch should be expanded
0309      */
0310     public function getExpandSiblingNodesOfActiveBranch()
0311     {
0312         return $this->_expandSiblingNodesOfActiveBranch;
0313     }
0314     
0315     /**
0316      * Enables/disables rendering of parents when only rendering active branch
0317      *
0318      * See {@link setOnlyActiveBranch()} for more information.
0319      *
0320      * @param  bool $flag                        [optional] render parents when
0321      *                                           rendering active branch.
0322      *                                           Default is true.
0323      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
0324      */
0325     public function setRenderParents($flag = true)
0326     {
0327         $this->_renderParents = (bool) $flag;
0328         return $this;
0329     }
0330 
0331     /**
0332      * Returns flag indicating whether parents should be rendered when rendering
0333      * only the active branch
0334      *
0335      * By default, this value is true.
0336      *
0337      * @return bool  whether parents should be rendered
0338      */
0339     public function getRenderParents()
0340     {
0341         return $this->_renderParents;
0342     }
0343 
0344     /**
0345      * Sets which partial view script to use for rendering menu
0346      *
0347      * @param  string|array $partial             partial view script or null. If
0348      *                                           an array is given, it is
0349      *                                           expected to contain two values;
0350      *                                           the partial view script to use,
0351      *                                           and the module where the script
0352      *                                           can be found.
0353      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
0354      */
0355     public function setPartial($partial)
0356     {
0357         if (null === $partial || is_string($partial) || is_array($partial)) {
0358             $this->_partial = $partial;
0359         }
0360 
0361         return $this;
0362     }
0363 
0364     /**
0365      * Returns partial view script to use for rendering menu
0366      *
0367      * @return string|array|null
0368      */
0369     public function getPartial()
0370     {
0371         return $this->_partial;
0372     }
0373 
0374     /**
0375      * Adds CSS class from page to li element
0376      *
0377      * Before:
0378      * <code>
0379      * <li>
0380      *     <a href="#" class="foo">Bar</a>
0381      * </li>
0382      * </code>
0383      *
0384      * After:
0385      * <code>
0386      * <li class="foo">
0387      *     <a href="#">Bar</a>
0388      * </li>
0389      * </code>
0390      *
0391      * @param bool $flag                        [optional] adds CSS class from
0392      *                                          page to li element
0393      *
0394      * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
0395      */
0396     public function addPageClassToLi($flag = true)
0397     {
0398         $this->_addPageClassToLi = (bool) $flag;
0399 
0400         return $this;
0401     }
0402 
0403     /**
0404      * Returns a flag indicating whether the CSS class from page to be added to
0405      * li element
0406      *
0407      * @return bool
0408      */
0409     public function getAddPageClassToLi()
0410     {
0411         return $this->_addPageClassToLi;
0412     }
0413 
0414     /**
0415      * Set the inner indentation string for using in {@link render()}, optionally
0416      * a number of spaces to indent with
0417      *
0418      * @param  string|int $indent                          indentation string or
0419      *                                                     number of spaces
0420      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0421      *                                                     returns self
0422      */
0423     public function setInnerIndent($indent)
0424     {
0425         $this->_innerIndent = $this->_getWhitespace($indent);
0426 
0427         return $this;
0428     }
0429 
0430     /**
0431      * Returns inner indentation (format output is respected)
0432      *
0433      * @see getFormatOutput()
0434      *
0435      * @return string       indentation string or an empty string
0436      */
0437     public function getInnerIndent()
0438     {
0439         if (false === $this->getFormatOutput()) {
0440             return '';
0441         }
0442 
0443         return $this->_innerIndent;
0444     }
0445 
0446     // Public methods:
0447 
0448     /**
0449      * Returns an HTML string containing an 'a' element for the given page if
0450      * the page's href is not empty, and a 'span' element if it is empty
0451      *
0452      * Overrides {@link Zend_View_Helper_Navigation_Abstract::htmlify()}.
0453      *
0454      * @param  Zend_Navigation_Page $page  page to generate HTML for
0455      * @return string                      HTML string for the given page
0456      */
0457     public function htmlify(Zend_Navigation_Page $page)
0458     {
0459         // get label and title for translating
0460         $label = $page->getLabel();
0461         $title = $page->getTitle();
0462 
0463         // translate label and title?
0464         if ($this->getUseTranslator() && $t = $this->getTranslator()) {
0465             if (is_string($label) && !empty($label)) {
0466                 $label = $t->translate($label);
0467             }
0468             if (is_string($title) && !empty($title)) {
0469                 $title = $t->translate($title);
0470             }
0471         }
0472 
0473         // get attribs for element
0474         $attribs = array(
0475             'id'     => $page->getId(),
0476             'title'  => $title,
0477         );
0478 
0479         if (false === $this->getAddPageClassToLi()) {
0480             $attribs['class'] = $page->getClass();
0481         }
0482 
0483         // does page have a href?
0484         if ($href = $page->getHref()) {
0485             $element              = 'a';
0486             $attribs['href']      = $href;
0487             $attribs['target']    = $page->getTarget();
0488             $attribs['accesskey'] = $page->getAccessKey();
0489         } else {
0490             $element = 'span';
0491         }
0492 
0493         // Add custom HTML attributes
0494         $attribs = array_merge($attribs, $page->getCustomHtmlAttribs());
0495 
0496         return '<' . $element . $this->_htmlAttribs($attribs) . '>'
0497              . $this->view->escape($label)
0498              . '</' . $element . '>';
0499     }
0500 
0501     /**
0502      * Normalizes given render options
0503      *
0504      * @param  array $options  [optional] options to normalize
0505      * @return array           normalized options
0506      */
0507     protected function _normalizeOptions(array $options = array())
0508     {
0509         // Ident
0510         if (isset($options['indent'])) {
0511             $options['indent'] = $this->_getWhitespace($options['indent']);
0512         } else {
0513             $options['indent'] = $this->getIndent();
0514         }
0515 
0516         // Inner ident
0517         if (isset($options['innerIndent'])) {
0518             $options['innerIndent'] =
0519                 $this->_getWhitespace($options['innerIndent']);
0520         } else {
0521             $options['innerIndent'] = $this->getInnerIndent();
0522         }
0523 
0524         // UL class
0525         if (isset($options['ulClass']) && $options['ulClass'] !== null) {
0526             $options['ulClass'] = (string) $options['ulClass'];
0527         } else {
0528             $options['ulClass'] = $this->getUlClass();
0529         }
0530 
0531         // UL id
0532         if (isset($options['ulId']) && $options['ulId'] !== null) {
0533             $options['ulId'] = (string) $options['ulId'];
0534         } else {
0535             $options['ulId'] = $this->getUlId();
0536         }
0537 
0538         // Active class
0539         if (isset($options['activeClass']) && $options['activeClass'] !== null
0540         ) {
0541             $options['activeClass'] = (string) $options['activeClass'];
0542         } else {
0543             $options['activeClass'] = $this->getActiveClass();
0544         }
0545 
0546         // Parent class
0547         if (isset($options['parentClass']) && $options['parentClass'] !== null) {
0548             $options['parentClass'] = (string) $options['parentClass'];
0549         } else {
0550             $options['parentClass'] = $this->getParentClass();
0551         }
0552 
0553         // Minimum depth
0554         if (array_key_exists('minDepth', $options)) {
0555             if (null !== $options['minDepth']) {
0556                 $options['minDepth'] = (int) $options['minDepth'];
0557             }
0558         } else {
0559             $options['minDepth'] = $this->getMinDepth();
0560         }
0561 
0562         if ($options['minDepth'] < 0 || $options['minDepth'] === null) {
0563             $options['minDepth'] = 0;
0564         }
0565 
0566         // Maximum depth
0567         if (array_key_exists('maxDepth', $options)) {
0568             if (null !== $options['maxDepth']) {
0569                 $options['maxDepth'] = (int) $options['maxDepth'];
0570             }
0571         } else {
0572             $options['maxDepth'] = $this->getMaxDepth();
0573         }
0574 
0575         // Only active branch
0576         if (!isset($options['onlyActiveBranch'])) {
0577             $options['onlyActiveBranch'] = $this->getOnlyActiveBranch();
0578         }
0579 
0580         // Expand sibling nodes of active branch
0581         if (!isset($options['expandSiblingNodesOfActiveBranch'])) {
0582             $options['expandSiblingNodesOfActiveBranch'] = $this->getExpandSiblingNodesOfActiveBranch();
0583         }
0584 
0585         // Render parents?
0586         if (!isset($options['renderParents'])) {
0587             $options['renderParents'] = $this->getRenderParents();
0588         }
0589 
0590         // Render parent class?
0591         if (!isset($options['renderParentClass'])) {
0592             $options['renderParentClass'] = $this->getRenderParentClass();
0593         }
0594 
0595         // Add page CSS class to LI element
0596         if (!isset($options['addPageClassToLi'])) {
0597             $options['addPageClassToLi'] = $this->getAddPageClassToLi();
0598         }
0599 
0600         return $options;
0601     }
0602 
0603     // Render methods:
0604 
0605     /**
0606      * Renders the deepest active menu within [$minDepth, $maxDeth], (called
0607      * from {@link renderMenu()})
0608      *
0609      * @param  Zend_Navigation_Container $container     container to render
0610      * @param  string                    $ulClass       CSS class for first UL
0611      * @param  string                    $indent        initial indentation
0612      * @param  string                    $innerIndent   inner indentation
0613      * @param  int|null                  $minDepth      minimum depth
0614      * @param  int|null                  $maxDepth      maximum depth
0615      * @param  string|null               $ulId          unique identifier (id)
0616      *                                                  for first UL
0617      * @param  bool                      $addPageClassToLi  adds CSS class from
0618      *                                                      page to li element
0619      * @param  string|null               $activeClass       CSS class for active
0620      *                                                      element
0621      * @param  string                    $parentClass       CSS class for parent
0622      *                                                      li's
0623      * @param  bool                      $renderParentClass Render parent class?
0624      * @return string                                       rendered menu (HTML)
0625      */
0626     protected function _renderDeepestMenu(Zend_Navigation_Container $container,
0627                                           $ulClass,
0628                                           $indent,
0629                                           $innerIndent,
0630                                           $minDepth,
0631                                           $maxDepth,
0632                                           $ulId,
0633                                           $addPageClassToLi,
0634                                           $activeClass,
0635                                           $parentClass,
0636                                           $renderParentClass)
0637     {
0638         if (!$active = $this->findActive($container, $minDepth - 1, $maxDepth)) {
0639             return '';
0640         }
0641 
0642         // special case if active page is one below minDepth
0643         if ($active['depth'] < $minDepth) {
0644             if (!$active['page']->hasPages()) {
0645                 return '';
0646             }
0647         } else if (!$active['page']->hasPages()) {
0648             // found pages has no children; render siblings
0649             $active['page'] = $active['page']->getParent();
0650         } else if (is_int($maxDepth) && $active['depth'] + 1 > $maxDepth) {
0651             // children are below max depth; render siblings
0652             $active['page'] = $active['page']->getParent();
0653         }
0654 
0655         $attribs = array(
0656             'class' => $ulClass,
0657             'id'    => $ulId,
0658         );
0659 
0660         // We don't need a prefix for the menu ID (backup)
0661         $skipValue = $this->_skipPrefixForId;
0662         $this->skipPrefixForId();
0663 
0664         $html = $indent . '<ul'
0665                         . $this->_htmlAttribs($attribs)
0666                         . '>'
0667                         . $this->getEOL();
0668 
0669         // Reset prefix for IDs
0670         $this->_skipPrefixForId = $skipValue;
0671 
0672         foreach ($active['page'] as $subPage) {
0673             if (!$this->accept($subPage)) {
0674                 continue;
0675             }
0676 
0677             $liClass = '';
0678             if ($subPage->isActive(true) && $addPageClassToLi) {
0679                 $liClass = $this->_htmlAttribs(
0680                     array('class' => $activeClass . ' ' . $subPage->getClass())
0681                 );
0682             } else if ($subPage->isActive(true)) {
0683                 $liClass = $this->_htmlAttribs(array('class' => $activeClass));
0684             } else if ($addPageClassToLi) {
0685                 $liClass = $this->_htmlAttribs(
0686                     array('class' => $subPage->getClass())
0687                 );
0688             }
0689             $html .= $indent . $innerIndent . '<li' . $liClass . '>' . $this->getEOL();
0690             $html .= $indent . str_repeat($innerIndent, 2) . $this->htmlify($subPage)
0691                                                            . $this->getEOL();
0692             $html .= $indent . $innerIndent . '</li>' . $this->getEOL();
0693         }
0694 
0695         $html .= $indent . '</ul>';
0696 
0697         return $html;
0698     }
0699 
0700     /**
0701      * Renders a normal menu (called from {@link renderMenu()})
0702      *
0703      * @param  Zend_Navigation_Container $container     container to render
0704      * @param  string                    $ulClass       CSS class for first UL
0705      * @param  string                    $indent        initial indentation
0706      * @param  string                    $innerIndent   inner indentation
0707      * @param  int|null                  $minDepth      minimum depth
0708      * @param  int|null                  $maxDepth      maximum depth
0709      * @param  bool                      $onlyActive    render only active branch?
0710      * @param  bool                      $expandSibs    render siblings of active
0711      *                                                  branch nodes?
0712      * @param  string|null               $ulId          unique identifier (id)
0713      *                                                  for first UL
0714      * @param  bool                      $addPageClassToLi  adds CSS class from
0715      *                                                      page to li element
0716      * @param  string|null               $activeClass       CSS class for active
0717      *                                                      element
0718      * @param  string                    $parentClass       CSS class for parent
0719      *                                                      li's
0720      * @param  bool                      $renderParentClass Render parent class?
0721      * @return string                                       rendered menu (HTML)
0722      */
0723     protected function _renderMenu(Zend_Navigation_Container $container,
0724                                    $ulClass,
0725                                    $indent,
0726                                    $innerIndent,
0727                                    $minDepth,
0728                                    $maxDepth,
0729                                    $onlyActive,
0730                                    $expandSibs,
0731                                    $ulId,
0732                                    $addPageClassToLi,
0733                                    $activeClass,
0734                                    $parentClass,
0735                                    $renderParentClass)
0736     {
0737         $html = '';
0738 
0739         // find deepest active
0740         if ($found = $this->findActive($container, $minDepth, $maxDepth)) {
0741             $foundPage = $found['page'];
0742             $foundDepth = $found['depth'];
0743         } else {
0744             $foundPage = null;
0745         }
0746 
0747         // create iterator
0748         $iterator = new RecursiveIteratorIterator($container,
0749                             RecursiveIteratorIterator::SELF_FIRST);
0750         if (is_int($maxDepth)) {
0751             $iterator->setMaxDepth($maxDepth);
0752         }
0753 
0754         // iterate container
0755         $prevDepth = -1;
0756         foreach ($iterator as $page) {
0757             $depth = $iterator->getDepth();
0758             $isActive = $page->isActive(true);
0759             if ($depth < $minDepth || !$this->accept($page)) {
0760                 // page is below minDepth or not accepted by acl/visibilty
0761                 continue;
0762             } else if ($expandSibs && $depth > $minDepth) {
0763                 // page is not active itself, but might be in the active branch
0764                 $accept = false;
0765                 if ($foundPage) {
0766                     if ($foundPage->hasPage($page)) {
0767                         // accept if page is a direct child of the active page
0768                         $accept = true;
0769                     } else if ($page->getParent()->isActive(true)) {
0770                         // page is a sibling of the active branch...
0771                         $accept = true;
0772                     }
0773                 }
0774                 if (!$isActive && !$accept) {
0775                     continue;
0776                 }
0777             } else if ($onlyActive && !$isActive) {
0778                 // page is not active itself, but might be in the active branch
0779                 $accept = false;
0780                 if ($foundPage) {
0781                     if ($foundPage->hasPage($page)) {
0782                         // accept if page is a direct child of the active page
0783                         $accept = true;
0784                     } else if ($foundPage->getParent()->hasPage($page)) {
0785                         // page is a sibling of the active page...
0786                         if (!$foundPage->hasPages() ||
0787                             is_int($maxDepth) && $foundDepth + 1 > $maxDepth) {
0788                             // accept if active page has no children, or the
0789                             // children are too deep to be rendered
0790                             $accept = true;
0791                         }
0792                     }
0793                 }
0794 
0795                 if (!$accept) {
0796                     continue;
0797                 }
0798             }
0799 
0800             // make sure indentation is correct
0801             $depth   -= $minDepth;
0802             $myIndent = $indent . str_repeat($innerIndent, $depth * 2);
0803 
0804             if ($depth > $prevDepth) {
0805                 $attribs = array();
0806 
0807                 // start new ul tag
0808                 if (0 == $depth) {
0809                     $attribs = array(
0810                         'class' => $ulClass,
0811                         'id'    => $ulId,
0812                     );
0813                 }
0814 
0815                 // We don't need a prefix for the menu ID (backup)
0816                 $skipValue = $this->_skipPrefixForId;
0817                 $this->skipPrefixForId();
0818 
0819                 $html .= $myIndent . '<ul'
0820                                    . $this->_htmlAttribs($attribs)
0821                                    . '>'
0822                                    . $this->getEOL();
0823 
0824                 // Reset prefix for IDs
0825                 $this->_skipPrefixForId = $skipValue;
0826             } else if ($prevDepth > $depth) {
0827                 // close li/ul tags until we're at current depth
0828                 for ($i = $prevDepth; $i > $depth; $i--) {
0829                     $ind   = $indent . str_repeat($innerIndent, $i * 2);
0830                     $html .= $ind . $innerIndent . '</li>' . $this->getEOL();
0831                     $html .= $ind . '</ul>' . $this->getEOL();
0832                 }
0833                 // close previous li tag
0834                 $html .= $myIndent . $innerIndent . '</li>' . $this->getEOL();
0835             } else {
0836                 // close previous li tag
0837                 $html .= $myIndent . $innerIndent . '</li>' . $this->getEOL();
0838             }
0839 
0840             // render li tag and page
0841             $liClasses = array();
0842             // Is page active?
0843             if ($isActive) {
0844                 $liClasses[] = $activeClass;
0845             }
0846             // Add CSS class from page to LI?
0847             if ($addPageClassToLi) {
0848                 $liClasses[] = $page->getClass();
0849             }
0850             // Add CSS class for parents to LI?
0851             if ($renderParentClass && $page->hasChildren()) {
0852                 // Check max depth
0853                 if ((is_int($maxDepth) && ($depth + 1 < $maxDepth))
0854                     || !is_int($maxDepth)
0855                 ) {
0856                     $liClasses[] = $parentClass;
0857                 }
0858             }
0859 
0860             $html .= $myIndent . $innerIndent . '<li'
0861                    . $this->_htmlAttribs(array('class' => implode(' ', $liClasses)))
0862                    . '>' . $this->getEOL()
0863                    . $myIndent . str_repeat($innerIndent, 2)
0864                    . $this->htmlify($page)
0865                    . $this->getEOL();
0866 
0867             // store as previous depth for next iteration
0868             $prevDepth = $depth;
0869         }
0870 
0871         if ($html) {
0872             // done iterating container; close open ul/li tags
0873             for ($i = $prevDepth+1; $i > 0; $i--) {
0874                 $myIndent = $indent . str_repeat($innerIndent . $innerIndent, $i - 1);
0875                 $html    .= $myIndent . $innerIndent . '</li>' . $this->getEOL()
0876                          . $myIndent . '</ul>' . $this->getEOL();
0877             }
0878             $html = rtrim($html, $this->getEOL());
0879         }
0880 
0881         return $html;
0882     }
0883 
0884     /**
0885      * Renders helper
0886      *
0887      * Renders a HTML 'ul' for the given $container. If $container is not given,
0888      * the container registered in the helper will be used.
0889      *
0890      * Available $options:
0891      *
0892      *
0893      * @param  Zend_Navigation_Container $container  [optional] container to
0894      *                                               create menu from. Default
0895      *                                               is to use the container
0896      *                                               retrieved from
0897      *                                               {@link getContainer()}.
0898      * @param  array                     $options    [optional] options for
0899      *                                               controlling rendering
0900      * @return string                                rendered menu
0901      */
0902     public function renderMenu(Zend_Navigation_Container $container = null,
0903                                array $options = array())
0904     {
0905         if (null === $container) {
0906             $container = $this->getContainer();
0907         }
0908 
0909         $options = $this->_normalizeOptions($options);
0910 
0911         if ($options['onlyActiveBranch'] && !$options['renderParents']) {
0912             $html = $this->_renderDeepestMenu(
0913                 $container,
0914                 $options['ulClass'],
0915                 $options['indent'],
0916                 $options['innerIndent'],
0917                 $options['minDepth'],
0918                 $options['maxDepth'],
0919                 $options['ulId'],
0920                 $options['addPageClassToLi'],
0921                 $options['activeClass'],
0922                 $options['parentClass'],
0923                 $options['renderParentClass']
0924             );
0925         } else {
0926             $html = $this->_renderMenu(
0927                 $container,
0928                 $options['ulClass'],
0929                 $options['indent'],
0930                 $options['innerIndent'],
0931                 $options['minDepth'],
0932                 $options['maxDepth'],
0933                 $options['onlyActiveBranch'],
0934                 $options['expandSiblingNodesOfActiveBranch'],
0935                 $options['ulId'],
0936                 $options['addPageClassToLi'],
0937                 $options['activeClass'],
0938                 $options['parentClass'],
0939                 $options['renderParentClass']
0940             );
0941         }
0942 
0943         return $html;
0944     }
0945 
0946     /**
0947      * Renders the inner-most sub menu for the active page in the $container
0948      *
0949      * This is a convenience method which is equivalent to the following call:
0950      * <code>
0951      * renderMenu($container, array(
0952      *     'indent'           => $indent,
0953      *     'ulClass'          => $ulClass,
0954      *     'minDepth'         => null,
0955      *     'maxDepth'         => null,
0956      *     'onlyActiveBranch' => true,
0957      *     'renderParents'    => false
0958      * ));
0959      * </code>
0960      *
0961      * @param  Zend_Navigation_Container $container  [optional] container to
0962      *                                               render. Default is to render
0963      *                                               the container registered in
0964      *                                               the helper.
0965      * @param  string|null               $ulClass    [optional] CSS class to
0966      *                                               use for UL element. Default
0967      *                                               is to use the value from
0968      *                                               {@link getUlClass()}.
0969      * @param  string|int                $indent     [optional] indentation as
0970      *                                               a string or number of
0971      *                                               spaces. Default is to use
0972      *                                               the value retrieved from
0973      *                                               {@link getIndent()}.
0974      * @param  string|null               $ulId       [optional] Unique identifier
0975      *                                               (id) use for UL element
0976      * @param  bool                      $addPageClassToLi  adds CSS class from
0977      *                                                      page to li element
0978      * @param  string|int                $innerIndent   [optional] inner
0979      *                                                  indentation as a string
0980      *                                                  or number of spaces.
0981      *                                                  Default is to use the
0982      *                                                  {@link getInnerIndent()}.
0983      * @return string                                   rendered content
0984      */
0985     public function renderSubMenu(Zend_Navigation_Container $container = null,
0986                                   $ulClass = null,
0987                                   $indent = null,
0988                                   $ulId   = null,
0989                                   $addPageClassToLi = false,
0990                                   $innerIndent = null)
0991     {
0992         return $this->renderMenu($container, array(
0993             'indent'           => $indent,
0994             'innerIndent'      => $innerIndent,
0995             'ulClass'          => $ulClass,
0996             'minDepth'         => null,
0997             'maxDepth'         => null,
0998             'onlyActiveBranch' => true,
0999             'renderParents'    => false,
1000             'ulId'             => $ulId,
1001             'addPageClassToLi' => $addPageClassToLi,
1002         ));
1003     }
1004 
1005     /**
1006      * Renders the given $container by invoking the partial view helper
1007      *
1008      * The container will simply be passed on as a model to the view script
1009      * as-is, and will be available in the partial script as 'container', e.g.
1010      * <code>echo 'Number of pages: ', count($this->container);</code>.
1011      *
1012      * @param  Zend_Navigation_Container $container  [optional] container to
1013      *                                               pass to view script. Default
1014      *                                               is to use the container
1015      *                                               registered in the helper.
1016      * @param  string|array             $partial     [optional] partial view
1017      *                                               script to use. Default is to
1018      *                                               use the partial registered
1019      *                                               in the helper. If an array
1020      *                                               is given, it is expected to
1021      *                                               contain two values; the
1022      *                                               partial view script to use,
1023      *                                               and the module where the
1024      *                                               script can be found.
1025      * @return string                                helper output
1026      *
1027      * @throws Zend_View_Exception   When no partial script is set
1028      */
1029     public function renderPartial(Zend_Navigation_Container $container = null,
1030                                   $partial = null)
1031     {
1032         if (null === $container) {
1033             $container = $this->getContainer();
1034         }
1035 
1036         if (null === $partial) {
1037             $partial = $this->getPartial();
1038         }
1039 
1040         if (empty($partial)) {
1041             // require_once 'Zend/View/Exception.php';
1042             $e = new Zend_View_Exception(
1043                 'Unable to render menu: No partial view script provided'
1044             );
1045             $e->setView($this->view);
1046             throw $e;
1047         }
1048 
1049         $model = array(
1050             'container' => $container
1051         );
1052 
1053         if (is_array($partial)) {
1054             if (count($partial) != 2) {
1055                 // require_once 'Zend/View/Exception.php';
1056                 $e = new Zend_View_Exception(
1057                     'Unable to render menu: A view partial supplied as '
1058                     .  'an array must contain two values: partial view '
1059                     .  'script and module where script can be found'
1060                 );
1061                 $e->setView($this->view);
1062                 throw $e;
1063             }
1064 
1065             return $this->view->partial($partial[0], $partial[1], $model);
1066         }
1067 
1068         return $this->view->partial($partial, null, $model);
1069     }
1070 
1071     // Zend_View_Helper_Navigation_Helper:
1072 
1073     /**
1074      * Renders menu
1075      *
1076      * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
1077      *
1078      * If a partial view is registered in the helper, the menu will be rendered
1079      * using the given partial script. If no partial is registered, the menu
1080      * will be rendered as an 'ul' element by the helper's internal method.
1081      *
1082      * @see renderPartial()
1083      * @see renderMenu()
1084      *
1085      * @param  Zend_Navigation_Container $container  [optional] container to
1086      *                                               render. Default is to
1087      *                                               render the container
1088      *                                               registered in the helper.
1089      * @return string                                helper output
1090      */
1091     public function render(Zend_Navigation_Container $container = null)
1092     {
1093         if ($partial = $this->getPartial()) {
1094             return $this->renderPartial($container, $partial);
1095         } else {
1096             return $this->renderMenu($container);
1097         }
1098     }
1099 }