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_Helper
0025  */
0026 // require_once 'Zend/View/Helper/Navigation/Helper.php';
0027 
0028 /**
0029  * @see Zend_View_Helper_HtmlElement
0030  */
0031 // require_once 'Zend/View/Helper/HtmlElement.php';
0032 
0033 /**
0034  * Base class for navigational helpers
0035  *
0036  * @category   Zend
0037  * @package    Zend_View
0038  * @subpackage Helper
0039  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0040  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0041  */
0042 abstract class Zend_View_Helper_Navigation_HelperAbstract
0043     extends Zend_View_Helper_HtmlElement
0044     implements Zend_View_Helper_Navigation_Helper
0045 {
0046     /**
0047      * Container to operate on by default
0048      *
0049      * @var Zend_Navigation_Container
0050      */
0051     protected $_container;
0052 
0053     /**
0054      * The minimum depth a page must have to be included when rendering
0055      *
0056      * @var int
0057      */
0058     protected $_minDepth;
0059 
0060     /**
0061      * The maximum depth a page can have to be included when rendering
0062      *
0063      * @var int
0064      */
0065     protected $_maxDepth;
0066 
0067     /**
0068      * Indentation string
0069      *
0070      * @var string
0071      */
0072     protected $_indent = '';
0073 
0074     /**
0075      * Whether HTML/XML output should be formatted
0076      *
0077      * @var bool
0078      */
0079     protected $_formatOutput = true;
0080 
0081     /**
0082      * Prefix for IDs when they are normalized
0083      *
0084      * @var string|null
0085      */
0086     protected $_prefixForId = null;
0087 
0088     /**
0089      * Skip current prefix for IDs when they are normalized (flag)
0090      *
0091      * @var bool
0092      */
0093     protected $_skipPrefixForId = false;
0094 
0095     /**
0096      * Translator
0097      *
0098      * @var Zend_Translate_Adapter
0099      */
0100     protected $_translator;
0101 
0102     /**
0103      * ACL to use when iterating pages
0104      *
0105      * @var Zend_Acl
0106      */
0107     protected $_acl;
0108 
0109     /**
0110      * Wheter invisible items should be rendered by this helper
0111      *
0112      * @var bool
0113      */
0114     protected $_renderInvisible = false;
0115 
0116     /**
0117      * ACL role to use when iterating pages
0118      *
0119      * @var string|Zend_Acl_Role_Interface
0120      */
0121     protected $_role;
0122 
0123     /**
0124      * Whether translator should be used for page labels and titles
0125      *
0126      * @var bool
0127      */
0128     protected $_useTranslator = true;
0129 
0130     /**
0131      * Whether ACL should be used for filtering out pages
0132      *
0133      * @var bool
0134      */
0135     protected $_useAcl = true;
0136 
0137     /**
0138      * Default ACL to use when iterating pages if not explicitly set in the
0139      * instance by calling {@link setAcl()}
0140      *
0141      * @var Zend_Acl
0142      */
0143     protected static $_defaultAcl;
0144 
0145     /**
0146      * Default ACL role to use when iterating pages if not explicitly set in the
0147      * instance by calling {@link setRole()}
0148      *
0149      * @var string|Zend_Acl_Role_Interface
0150      */
0151     protected static $_defaultRole;
0152 
0153     // Accessors:
0154 
0155     /**
0156      * Sets navigation container the helper operates on by default
0157      *
0158      * Implements {@link Zend_View_Helper_Navigation_Interface::setContainer()}.
0159      *
0160      * @param  Zend_Navigation_Container $container        [optional] container
0161      *                                                     to operate on.
0162      *                                                     Default is null,
0163      *                                                     meaning container
0164      *                                                     will be reset.
0165      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0166      *                                                     returns self
0167      */
0168     public function setContainer(Zend_Navigation_Container $container = null)
0169     {
0170         $this->_container = $container;
0171         return $this;
0172     }
0173 
0174     /**
0175      * Returns the navigation container helper operates on by default
0176      *
0177      * Implements {@link Zend_View_Helper_Navigation_Interface::getContainer()}.
0178      *
0179      * If a helper is not explicitly set in this helper instance by calling
0180      * {@link setContainer()} or by passing it through the helper entry point,
0181      * this method will look in {@link Zend_Registry} for a container by using
0182      * the key 'Zend_Navigation'.
0183      *
0184      * If no container is set, and nothing is found in Zend_Registry, a new
0185      * container will be instantiated and stored in the helper.
0186      *
0187      * @return Zend_Navigation_Container  navigation container
0188      */
0189     public function getContainer()
0190     {
0191         if (null === $this->_container) {
0192             // try to fetch from registry first
0193             // require_once 'Zend/Registry.php';
0194             if (Zend_Registry::isRegistered('Zend_Navigation')) {
0195                 $nav = Zend_Registry::get('Zend_Navigation');
0196                 if ($nav instanceof Zend_Navigation_Container) {
0197                     return $this->_container = $nav;
0198                 }
0199             }
0200 
0201             // nothing found in registry, create new container
0202             // require_once 'Zend/Navigation.php';
0203             $this->_container = new Zend_Navigation();
0204         }
0205 
0206         return $this->_container;
0207     }
0208 
0209     /**
0210      * Sets the minimum depth a page must have to be included when rendering
0211      *
0212      * @param  int $minDepth                               [optional] minimum
0213      *                                                     depth. Default is
0214      *                                                     null, which sets
0215      *                                                     no minimum depth.
0216      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0217      *                                                     returns self
0218      */
0219     public function setMinDepth($minDepth = null)
0220     {
0221         if (null === $minDepth || is_int($minDepth)) {
0222             $this->_minDepth = $minDepth;
0223         } else {
0224             $this->_minDepth = (int) $minDepth;
0225         }
0226         return $this;
0227     }
0228 
0229     /**
0230      * Returns minimum depth a page must have to be included when rendering
0231      *
0232      * @return int|null  minimum depth or null
0233      */
0234     public function getMinDepth()
0235     {
0236         if (!is_int($this->_minDepth) || $this->_minDepth < 0) {
0237             return 0;
0238         }
0239         return $this->_minDepth;
0240     }
0241 
0242     /**
0243      * Sets the maximum depth a page can have to be included when rendering
0244      *
0245      * @param  int $maxDepth                               [optional] maximum
0246      *                                                     depth. Default is
0247      *                                                     null, which sets no
0248      *                                                     maximum depth.
0249      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0250      *                                                     returns self
0251      */
0252     public function setMaxDepth($maxDepth = null)
0253     {
0254         if (null === $maxDepth || is_int($maxDepth)) {
0255             $this->_maxDepth = $maxDepth;
0256         } else {
0257             $this->_maxDepth = (int) $maxDepth;
0258         }
0259         return $this;
0260     }
0261 
0262     /**
0263      * Returns maximum depth a page can have to be included when rendering
0264      *
0265      * @return int|null  maximum depth or null
0266      */
0267     public function getMaxDepth()
0268     {
0269         return $this->_maxDepth;
0270     }
0271 
0272     /**
0273      * Set the indentation string for using in {@link render()}, optionally a
0274      * number of spaces to indent with
0275      *
0276      * @param  string|int $indent                          indentation string or
0277      *                                                     number of spaces
0278      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0279      *                                                     returns self
0280      */
0281     public function setIndent($indent)
0282     {
0283         $this->_indent = $this->_getWhitespace($indent);
0284         return $this;
0285     }
0286 
0287     /**
0288      * Returns indentation (format output is respected)
0289      *
0290      * @return string   indentation string or an empty string
0291      */
0292     public function getIndent()
0293     {
0294         if (false === $this->getFormatOutput()) {
0295             return '';
0296         }
0297 
0298         return $this->_indent;
0299     }
0300 
0301     /**
0302      * Returns the EOL character (format output is respected)
0303      *
0304      * @see self::EOL
0305      * @see getFormatOutput()
0306      *
0307      * @return string       standard EOL charater or an empty string
0308      */
0309     public function getEOL()
0310     {
0311         if (false === $this->getFormatOutput()) {
0312             return '';
0313         }
0314 
0315         return self::EOL;
0316     }
0317 
0318     /**
0319      * Sets whether HTML/XML output should be formatted
0320      *
0321      * @param  bool $formatOutput                   [optional] whether output
0322      *                                              should be formatted. Default
0323      *                                              is true.
0324      *
0325      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
0326      *                                              self
0327      */
0328     public function setFormatOutput($formatOutput = true)
0329     {
0330         $this->_formatOutput = (bool)$formatOutput;
0331 
0332         return $this;
0333     }
0334 
0335     /**
0336      * Returns whether HTML/XML output should be formatted
0337      *
0338      * @return bool  whether HTML/XML output should be formatted
0339      */
0340     public function getFormatOutput()
0341     {
0342         return $this->_formatOutput;
0343     }
0344 
0345     /**
0346      * Sets prefix for IDs when they are normalized
0347      *
0348      * @param   string $prefix                              Prefix for IDs
0349      * @return  Zend_View_Helper_Navigation_HelperAbstract  fluent interface, returns self
0350      */
0351     public function setPrefixForId($prefix)
0352     {
0353         if (is_string($prefix)) {
0354             $this->_prefixForId = trim($prefix);
0355         }
0356 
0357         return $this;
0358     }
0359 
0360     /**
0361      * Returns prefix for IDs when they are normalized
0362      *
0363      * @return string   Prefix for
0364      */
0365     public function getPrefixForId()
0366     {
0367         if (null === $this->_prefixForId) {
0368             $prefix             = get_class($this);
0369             $this->_prefixForId = strtolower(
0370                     trim(substr($prefix, strrpos($prefix, '_')), '_')
0371                 ) . '-';
0372         }
0373 
0374         return $this->_prefixForId;
0375     }
0376 
0377     /**
0378      * Skip the current prefix for IDs when they are normalized
0379      *
0380      * @param  bool $flag
0381      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface, returns self
0382      */
0383     public function skipPrefixForId($flag = true)
0384     {
0385         $this->_skipPrefixForId = (bool) $flag;
0386         return $this;
0387     }
0388 
0389     /**
0390      * Sets translator to use in helper
0391      *
0392      * Implements {@link Zend_View_Helper_Navigation_Helper::setTranslator()}.
0393      *
0394      * @param  mixed $translator                           [optional] translator.
0395      *                                                     Expects an object of
0396      *                                                     type
0397      *                                                     {@link Zend_Translate_Adapter}
0398      *                                                     or {@link Zend_Translate},
0399      *                                                     or null. Default is
0400      *                                                     null, which sets no
0401      *                                                     translator.
0402      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0403      *                                                     returns self
0404      */
0405     public function setTranslator($translator = null)
0406     {
0407         if (null == $translator ||
0408             $translator instanceof Zend_Translate_Adapter) {
0409             $this->_translator = $translator;
0410         } elseif ($translator instanceof Zend_Translate) {
0411             $this->_translator = $translator->getAdapter();
0412         }
0413 
0414         return $this;
0415     }
0416 
0417     /**
0418      * Returns translator used in helper
0419      *
0420      * Implements {@link Zend_View_Helper_Navigation_Helper::getTranslator()}.
0421      *
0422      * @return Zend_Translate_Adapter|null  translator or null
0423      */
0424     public function getTranslator()
0425     {
0426         if (null === $this->_translator) {
0427             // require_once 'Zend/Registry.php';
0428             if (Zend_Registry::isRegistered('Zend_Translate')) {
0429                 $this->setTranslator(Zend_Registry::get('Zend_Translate'));
0430             }
0431         }
0432 
0433         return $this->_translator;
0434     }
0435 
0436     /**
0437      * Sets ACL to use when iterating pages
0438      *
0439      * Implements {@link Zend_View_Helper_Navigation_Helper::setAcl()}.
0440      *
0441      * @param  Zend_Acl $acl                               [optional] ACL object.
0442      *                                                     Default is null.
0443      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0444      *                                                     returns self
0445      */
0446     public function setAcl(Zend_Acl $acl = null)
0447     {
0448         $this->_acl = $acl;
0449         return $this;
0450     }
0451 
0452     /**
0453      * Returns ACL or null if it isn't set using {@link setAcl()} or
0454      * {@link setDefaultAcl()}
0455      *
0456      * Implements {@link Zend_View_Helper_Navigation_Helper::getAcl()}.
0457      *
0458      * @return Zend_Acl|null  ACL object or null
0459      */
0460     public function getAcl()
0461     {
0462         if ($this->_acl === null && self::$_defaultAcl !== null) {
0463             return self::$_defaultAcl;
0464         }
0465 
0466         return $this->_acl;
0467     }
0468 
0469     /**
0470      * Sets ACL role(s) to use when iterating pages
0471      *
0472      * Implements {@link Zend_View_Helper_Navigation_Helper::setRole()}.
0473      *
0474      * @param  mixed $role                                 [optional] role to
0475      *                                                     set. Expects a string,
0476      *                                                     an instance of type
0477      *                                                     {@link Zend_Acl_Role_Interface},
0478      *                                                     or null. Default is
0479      *                                                     null, which will set
0480      *                                                     no role.
0481      * @throws Zend_View_Exception                         if $role is invalid
0482      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0483      *                                                     returns self
0484      */
0485     public function setRole($role = null)
0486     {
0487         if (null === $role || is_string($role) ||
0488             $role instanceof Zend_Acl_Role_Interface) {
0489             $this->_role = $role;
0490         } else {
0491             // require_once 'Zend/View/Exception.php';
0492             $e = new Zend_View_Exception(sprintf(
0493                 '$role must be a string, null, or an instance of '
0494                 .  'Zend_Acl_Role_Interface; %s given',
0495                 gettype($role)
0496             ));
0497             $e->setView($this->view);
0498             throw $e;
0499         }
0500 
0501         return $this;
0502     }
0503 
0504     /**
0505      * Returns ACL role to use when iterating pages, or null if it isn't set
0506      * using {@link setRole()} or {@link setDefaultRole()}
0507      *
0508      * Implements {@link Zend_View_Helper_Navigation_Helper::getRole()}.
0509      *
0510      * @return string|Zend_Acl_Role_Interface|null  role or null
0511      */
0512     public function getRole()
0513     {
0514         if ($this->_role === null && self::$_defaultRole !== null) {
0515             return self::$_defaultRole;
0516         }
0517 
0518         return $this->_role;
0519     }
0520 
0521     /**
0522      * Sets whether ACL should be used
0523      *
0524      * Implements {@link Zend_View_Helper_Navigation_Helper::setUseAcl()}.
0525      *
0526      * @param  bool $useAcl                                [optional] whether ACL
0527      *                                                     should be used.
0528      *                                                     Default is true.
0529      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0530      *                                                     returns self
0531      */
0532     public function setUseAcl($useAcl = true)
0533     {
0534         $this->_useAcl = (bool) $useAcl;
0535         return $this;
0536     }
0537 
0538     /**
0539      * Returns whether ACL should be used
0540      *
0541      * Implements {@link Zend_View_Helper_Navigation_Helper::getUseAcl()}.
0542      *
0543      * @return bool  whether ACL should be used
0544      */
0545     public function getUseAcl()
0546     {
0547         return $this->_useAcl;
0548     }
0549 
0550     /**
0551      * Return renderInvisible flag
0552      *
0553      * @return bool
0554      */
0555     public function getRenderInvisible()
0556     {
0557         return $this->_renderInvisible;
0558     }
0559 
0560     /**
0561      * Render invisible items?
0562      *
0563      * @param  bool $renderInvisible                       [optional] boolean flag
0564      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface
0565      *                                                     returns self
0566      */
0567     public function setRenderInvisible($renderInvisible = true)
0568     {
0569         $this->_renderInvisible = (bool) $renderInvisible;
0570         return $this;
0571     }
0572 
0573     /**
0574      * Sets whether translator should be used
0575      *
0576      * Implements {@link Zend_View_Helper_Navigation_Helper::setUseTranslator()}.
0577      *
0578      * @param  bool $useTranslator                         [optional] whether
0579      *                                                     translator should be
0580      *                                                     used. Default is true.
0581      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
0582      *                                                     returns self
0583      */
0584     public function setUseTranslator($useTranslator = true)
0585     {
0586         $this->_useTranslator = (bool) $useTranslator;
0587         return $this;
0588     }
0589 
0590     /**
0591      * Returns whether translator should be used
0592      *
0593      * Implements {@link Zend_View_Helper_Navigation_Helper::getUseTranslator()}.
0594      *
0595      * @return bool  whether translator should be used
0596      */
0597     public function getUseTranslator()
0598     {
0599         return $this->_useTranslator;
0600     }
0601 
0602     // Magic overloads:
0603 
0604     /**
0605      * Magic overload: Proxy calls to the navigation container
0606      *
0607      * @param  string $method             method name in container
0608      * @param  array  $arguments          [optional] arguments to pass
0609      * @return mixed                      returns what the container returns
0610      * @throws Zend_Navigation_Exception  if method does not exist in container
0611      */
0612     public function __call($method, array $arguments = array())
0613     {
0614         return call_user_func_array(
0615                 array($this->getContainer(), $method),
0616                 $arguments);
0617     }
0618 
0619     /**
0620      * Magic overload: Proxy to {@link render()}.
0621      *
0622      * This method will trigger an E_USER_ERROR if rendering the helper causes
0623      * an exception to be thrown.
0624      *
0625      * Implements {@link Zend_View_Helper_Navigation_Helper::__toString()}.
0626      *
0627      * @return string
0628      */
0629     public function __toString()
0630     {
0631         try {
0632             return $this->render();
0633         } catch (Exception $e) {
0634             $msg = get_class($e) . ': ' . $e->getMessage();
0635             trigger_error($msg, E_USER_ERROR);
0636             return '';
0637         }
0638     }
0639 
0640     // Public methods:
0641 
0642     /**
0643      * Finds the deepest active page in the given container
0644      *
0645      * @param  Zend_Navigation_Container $container  container to search
0646      * @param  int|null                  $minDepth   [optional] minimum depth
0647      *                                               required for page to be
0648      *                                               valid. Default is to use
0649      *                                               {@link getMinDepth()}. A
0650      *                                               null value means no minimum
0651      *                                               depth required.
0652      * @param  int|null                  $minDepth   [optional] maximum depth
0653      *                                               a page can have to be
0654      *                                               valid. Default is to use
0655      *                                               {@link getMaxDepth()}. A
0656      *                                               null value means no maximum
0657      *                                               depth required.
0658      * @return array                                 an associative array with
0659      *                                               the values 'depth' and
0660      *                                               'page', or an empty array
0661      *                                               if not found
0662      */
0663     public function findActive(Zend_Navigation_Container $container,
0664                                $minDepth = null,
0665                                $maxDepth = -1)
0666     {
0667         if (!is_int($minDepth)) {
0668             $minDepth = $this->getMinDepth();
0669         }
0670         if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) {
0671             $maxDepth = $this->getMaxDepth();
0672         }
0673 
0674         $found  = null;
0675         $foundDepth = -1;
0676         $iterator = new RecursiveIteratorIterator($container,
0677                 RecursiveIteratorIterator::CHILD_FIRST);
0678 
0679         foreach ($iterator as $page) {
0680             $currDepth = $iterator->getDepth();
0681             if ($currDepth < $minDepth || !$this->accept($page)) {
0682                 // page is not accepted
0683                 continue;
0684             }
0685 
0686             if ($page->isActive(false) && $currDepth > $foundDepth) {
0687                 // found an active page at a deeper level than before
0688                 $found = $page;
0689                 $foundDepth = $currDepth;
0690             }
0691         }
0692 
0693         if (is_int($maxDepth) && $foundDepth > $maxDepth) {
0694             while ($foundDepth > $maxDepth) {
0695                 if (--$foundDepth < $minDepth) {
0696                     $found = null;
0697                     break;
0698                 }
0699 
0700                 $found = $found->getParent();
0701                 if (!$found instanceof Zend_Navigation_Page) {
0702                     $found = null;
0703                     break;
0704                 }
0705             }
0706         }
0707 
0708         if ($found) {
0709             return array('page' => $found, 'depth' => $foundDepth);
0710         } else {
0711             return array();
0712         }
0713     }
0714 
0715     /**
0716      * Checks if the helper has a container
0717      *
0718      * Implements {@link Zend_View_Helper_Navigation_Helper::hasContainer()}.
0719      *
0720      * @return bool  whether the helper has a container or not
0721      */
0722     public function hasContainer()
0723     {
0724         return null !== $this->_container;
0725     }
0726 
0727     /**
0728      * Checks if the helper has an ACL instance
0729      *
0730      * Implements {@link Zend_View_Helper_Navigation_Helper::hasAcl()}.
0731      *
0732      * @return bool  whether the helper has a an ACL instance or not
0733      */
0734     public function hasAcl()
0735     {
0736         return null !== $this->_acl;
0737     }
0738 
0739     /**
0740      * Checks if the helper has an ACL role
0741      *
0742      * Implements {@link Zend_View_Helper_Navigation_Helper::hasRole()}.
0743      *
0744      * @return bool  whether the helper has a an ACL role or not
0745      */
0746     public function hasRole()
0747     {
0748         return null !== $this->_role;
0749     }
0750 
0751     /**
0752      * Checks if the helper has a translator
0753      *
0754      * Implements {@link Zend_View_Helper_Navigation_Helper::hasTranslator()}.
0755      *
0756      * @return bool  whether the helper has a translator or not
0757      */
0758     public function hasTranslator()
0759     {
0760         return null !== $this->_translator;
0761     }
0762 
0763     /**
0764      * Returns an HTML string containing an 'a' element for the given page
0765      *
0766      * @param  Zend_Navigation_Page $page  page to generate HTML for
0767      * @return string                      HTML string for the given page
0768      */
0769     public function htmlify(Zend_Navigation_Page $page)
0770     {
0771         // get label and title for translating
0772         $label = $page->getLabel();
0773         $title = $page->getTitle();
0774 
0775         if ($this->getUseTranslator() && $t = $this->getTranslator()) {
0776             if (is_string($label) && !empty($label)) {
0777                 $label = $t->translate($label);
0778             }
0779             if (is_string($title) && !empty($title)) {
0780                 $title = $t->translate($title);
0781             }
0782         }
0783 
0784         // get attribs for anchor element
0785         $attribs = array_merge(
0786             array(
0787                 'id'     => $page->getId(),
0788                 'title'  => $title,
0789                 'class'  => $page->getClass(),
0790                 'href'   => $page->getHref(),
0791                 'target' => $page->getTarget()
0792             ),
0793             $page->getCustomHtmlAttribs()
0794         );
0795 
0796         return '<a' . $this->_htmlAttribs($attribs) . '>'
0797              . $this->view->escape($label)
0798              . '</a>';
0799     }
0800 
0801     // Iterator filter methods:
0802 
0803     /**
0804      * Determines whether a page should be accepted when iterating
0805      *
0806      * Rules:
0807      * - If a page is not visible it is not accepted, unless RenderInvisible has
0808      *   been set to true.
0809      * - If helper has no ACL, page is accepted
0810      * - If helper has ACL, but no role, page is not accepted
0811      * - If helper has ACL and role:
0812      *  - Page is accepted if it has no resource or privilege
0813      *  - Page is accepted if ACL allows page's resource or privilege
0814      * - If page is accepted by the rules above and $recursive is true, the page
0815      *   will not be accepted if it is the descendant of a non-accepted page.
0816      *
0817      * @param  Zend_Navigation_Page $page      page to check
0818      * @param  bool                $recursive  [optional] if true, page will not
0819      *                                         be accepted if it is the
0820      *                                         descendant of a page that is not
0821      *                                         accepted. Default is true.
0822      * @return bool                            whether page should be accepted
0823      */
0824     public function accept(Zend_Navigation_Page $page, $recursive = true)
0825     {
0826         // accept by default
0827         $accept = true;
0828 
0829         if (!$page->isVisible(false) && !$this->getRenderInvisible()) {
0830             // don't accept invisible pages
0831             $accept = false;
0832         } elseif ($this->getUseAcl() && !$this->_acceptAcl($page)) {
0833             // acl is not amused
0834             $accept = false;
0835         }
0836 
0837         if ($accept && $recursive) {
0838             $parent = $page->getParent();
0839             if ($parent instanceof Zend_Navigation_Page) {
0840                 $accept = $this->accept($parent, true);
0841             }
0842         }
0843 
0844         return $accept;
0845     }
0846 
0847     /**
0848      * Determines whether a page should be accepted by ACL when iterating
0849      *
0850      * Rules:
0851      * - If helper has no ACL, page is accepted
0852      * - If page has a resource or privilege defined, page is accepted
0853      *   if the ACL allows access to it using the helper's role
0854      * - If page has no resource or privilege, page is accepted
0855      *
0856      * @param  Zend_Navigation_Page $page  page to check
0857      * @return bool                        whether page is accepted by ACL
0858      */
0859     protected function _acceptAcl(Zend_Navigation_Page $page)
0860     {
0861         if (!$acl = $this->getAcl()) {
0862             // no acl registered means don't use acl
0863             return true;
0864         }
0865 
0866         $role = $this->getRole();
0867         $resource = $page->getResource();
0868         $privilege = $page->getPrivilege();
0869 
0870         if ($resource || $privilege) {
0871             // determine using helper role and page resource/privilege
0872             return $acl->isAllowed($role, $resource, $privilege);
0873         }
0874 
0875         return true;
0876     }
0877 
0878     // Util methods:
0879 
0880     /**
0881      * Retrieve whitespace representation of $indent
0882      *
0883      * @param  int|string $indent
0884      * @return string
0885      */
0886     protected function _getWhitespace($indent)
0887     {
0888         if (is_int($indent)) {
0889             $indent = str_repeat(' ', $indent);
0890         }
0891 
0892         return (string) $indent;
0893     }
0894 
0895     /**
0896      * Converts an associative array to a string of tag attributes.
0897      *
0898      * Overloads {@link Zend_View_Helper_HtmlElement::_htmlAttribs()}.
0899      *
0900      * @param  array $attribs  an array where each key-value pair is converted
0901      *                         to an attribute name and value
0902      * @return string          an attribute string
0903      */
0904     protected function _htmlAttribs($attribs)
0905     {
0906         // filter out null values and empty string values
0907         foreach ($attribs as $key => $value) {
0908             if ($value === null || (is_string($value) && !strlen($value))) {
0909                 unset($attribs[$key]);
0910             }
0911         }
0912 
0913         return parent::_htmlAttribs($attribs);
0914     }
0915 
0916     /**
0917      * Normalize an ID
0918      *
0919      * Extends {@link Zend_View_Helper_HtmlElement::_normalizeId()}.
0920      *
0921      * @param  string $value    ID
0922      * @return string           Normalized ID
0923      */
0924     protected function _normalizeId($value)
0925     {        
0926         if (false === $this->_skipPrefixForId) {
0927             $prefix = $this->getPrefixForId();
0928 
0929             if (strlen($prefix)) {
0930                 return $prefix . $value;
0931             }
0932         }
0933 
0934         return parent::_normalizeId($value);
0935     }
0936 
0937     // Static methods:
0938 
0939     /**
0940      * Sets default ACL to use if another ACL is not explicitly set
0941      *
0942      * @param  Zend_Acl $acl  [optional] ACL object. Default is null, which
0943      *                        sets no ACL object.
0944      * @return void
0945      */
0946     public static function setDefaultAcl(Zend_Acl $acl = null)
0947     {
0948         self::$_defaultAcl = $acl;
0949     }
0950 
0951     /**
0952      * Sets default ACL role(s) to use when iterating pages if not explicitly
0953      * set later with {@link setRole()}
0954      *
0955      * @param  mixed $role               [optional] role to set. Expects null,
0956      *                                   string, or an instance of
0957      *                                   {@link Zend_Acl_Role_Interface}.
0958      *                                   Default is null, which sets no default
0959      *                                   role.
0960      * @throws Zend_View_Exception       if role is invalid
0961      * @return void
0962      */
0963     public static function setDefaultRole($role = null)
0964     {
0965         if (null === $role ||
0966             is_string($role) ||
0967             $role instanceof Zend_Acl_Role_Interface) {
0968             self::$_defaultRole = $role;
0969         } else {
0970             // require_once 'Zend/View/Exception.php';
0971             throw new Zend_View_Exception(
0972                 '$role must be null|string|Zend_Acl_Role_Interface'
0973             );
0974         }
0975     }
0976 }