web/lib/Zend/View/Helper/Navigation/HelperAbstract.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_View
       
    17  * @subpackage Helper
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: HelperAbstract.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 /**
       
    24  * @see Zend_View_Helper_Navigation_Helper
       
    25  */
       
    26 require_once 'Zend/View/Helper/Navigation/Helper.php';
       
    27 
       
    28 /**
       
    29  * @see Zend_View_Helper_HtmlElement
       
    30  */
       
    31 require_once 'Zend/View/Helper/HtmlElement.php';
       
    32 
       
    33 /**
       
    34  * Base class for navigational helpers
       
    35  *
       
    36  * @category   Zend
       
    37  * @package    Zend_View
       
    38  * @subpackage Helper
       
    39  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    40  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    41  */
       
    42 abstract class Zend_View_Helper_Navigation_HelperAbstract
       
    43     extends Zend_View_Helper_HtmlElement
       
    44     implements Zend_View_Helper_Navigation_Helper
       
    45 {
       
    46     /**
       
    47      * Container to operate on by default
       
    48      *
       
    49      * @var Zend_Navigation_Container
       
    50      */
       
    51     protected $_container;
       
    52 
       
    53     /**
       
    54      * The minimum depth a page must have to be included when rendering
       
    55      *
       
    56      * @var int
       
    57      */
       
    58     protected $_minDepth;
       
    59 
       
    60     /**
       
    61      * The maximum depth a page can have to be included when rendering
       
    62      *
       
    63      * @var int
       
    64      */
       
    65     protected $_maxDepth;
       
    66 
       
    67     /**
       
    68      * Indentation string
       
    69      *
       
    70      * @var string
       
    71      */
       
    72     protected $_indent = '';
       
    73 
       
    74     /**
       
    75      * Translator
       
    76      *
       
    77      * @var Zend_Translate_Adapter
       
    78      */
       
    79     protected $_translator;
       
    80 
       
    81     /**
       
    82      * ACL to use when iterating pages
       
    83      *
       
    84      * @var Zend_Acl
       
    85      */
       
    86     protected $_acl;
       
    87 
       
    88     /**
       
    89      * Wheter invisible items should be rendered by this helper
       
    90      *
       
    91      * @var bool
       
    92      */
       
    93     protected $_renderInvisible = false;
       
    94 
       
    95     /**
       
    96      * ACL role to use when iterating pages
       
    97      *
       
    98      * @var string|Zend_Acl_Role_Interface
       
    99      */
       
   100     protected $_role;
       
   101 
       
   102     /**
       
   103      * Whether translator should be used for page labels and titles
       
   104      *
       
   105      * @var bool
       
   106      */
       
   107     protected $_useTranslator = true;
       
   108 
       
   109     /**
       
   110      * Whether ACL should be used for filtering out pages
       
   111      *
       
   112      * @var bool
       
   113      */
       
   114     protected $_useAcl = true;
       
   115 
       
   116     /**
       
   117      * Default ACL to use when iterating pages if not explicitly set in the
       
   118      * instance by calling {@link setAcl()}
       
   119      *
       
   120      * @var Zend_Acl
       
   121      */
       
   122     protected static $_defaultAcl;
       
   123 
       
   124     /**
       
   125      * Default ACL role to use when iterating pages if not explicitly set in the
       
   126      * instance by calling {@link setRole()}
       
   127      *
       
   128      * @var string|Zend_Acl_Role_Interface
       
   129      */
       
   130     protected static $_defaultRole;
       
   131 
       
   132     // Accessors:
       
   133 
       
   134     /**
       
   135      * Sets navigation container the helper operates on by default
       
   136      *
       
   137      * Implements {@link Zend_View_Helper_Navigation_Interface::setContainer()}.
       
   138      *
       
   139      * @param  Zend_Navigation_Container $container        [optional] container
       
   140      *                                                     to operate on.
       
   141      *                                                     Default is null,
       
   142      *                                                     meaning container
       
   143      *                                                     will be reset.
       
   144      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   145      *                                                     returns self
       
   146      */
       
   147     public function setContainer(Zend_Navigation_Container $container = null)
       
   148     {
       
   149         $this->_container = $container;
       
   150         return $this;
       
   151     }
       
   152 
       
   153     /**
       
   154      * Returns the navigation container helper operates on by default
       
   155      *
       
   156      * Implements {@link Zend_View_Helper_Navigation_Interface::getContainer()}.
       
   157      *
       
   158      * If a helper is not explicitly set in this helper instance by calling
       
   159      * {@link setContainer()} or by passing it through the helper entry point,
       
   160      * this method will look in {@link Zend_Registry} for a container by using
       
   161      * the key 'Zend_Navigation'.
       
   162      *
       
   163      * If no container is set, and nothing is found in Zend_Registry, a new
       
   164      * container will be instantiated and stored in the helper.
       
   165      *
       
   166      * @return Zend_Navigation_Container  navigation container
       
   167      */
       
   168     public function getContainer()
       
   169     {
       
   170         if (null === $this->_container) {
       
   171             // try to fetch from registry first
       
   172             require_once 'Zend/Registry.php';
       
   173             if (Zend_Registry::isRegistered('Zend_Navigation')) {
       
   174                 $nav = Zend_Registry::get('Zend_Navigation');
       
   175                 if ($nav instanceof Zend_Navigation_Container) {
       
   176                     return $this->_container = $nav;
       
   177                 }
       
   178             }
       
   179 
       
   180             // nothing found in registry, create new container
       
   181             require_once 'Zend/Navigation.php';
       
   182             $this->_container = new Zend_Navigation();
       
   183         }
       
   184 
       
   185         return $this->_container;
       
   186     }
       
   187 
       
   188     /**
       
   189      * Sets the minimum depth a page must have to be included when rendering
       
   190      *
       
   191      * @param  int $minDepth                               [optional] minimum
       
   192      *                                                     depth. Default is
       
   193      *                                                     null, which sets
       
   194      *                                                     no minimum depth.
       
   195      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   196      *                                                     returns self
       
   197      */
       
   198     public function setMinDepth($minDepth = null)
       
   199     {
       
   200         if (null === $minDepth || is_int($minDepth)) {
       
   201             $this->_minDepth = $minDepth;
       
   202         } else {
       
   203             $this->_minDepth = (int) $minDepth;
       
   204         }
       
   205         return $this;
       
   206     }
       
   207 
       
   208     /**
       
   209      * Returns minimum depth a page must have to be included when rendering
       
   210      *
       
   211      * @return int|null  minimum depth or null
       
   212      */
       
   213     public function getMinDepth()
       
   214     {
       
   215         if (!is_int($this->_minDepth) || $this->_minDepth < 0) {
       
   216             return 0;
       
   217         }
       
   218         return $this->_minDepth;
       
   219     }
       
   220 
       
   221     /**
       
   222      * Sets the maximum depth a page can have to be included when rendering
       
   223      *
       
   224      * @param  int $maxDepth                               [optional] maximum
       
   225      *                                                     depth. Default is
       
   226      *                                                     null, which sets no
       
   227      *                                                     maximum depth.
       
   228      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   229      *                                                     returns self
       
   230      */
       
   231     public function setMaxDepth($maxDepth = null)
       
   232     {
       
   233         if (null === $maxDepth || is_int($maxDepth)) {
       
   234             $this->_maxDepth = $maxDepth;
       
   235         } else {
       
   236             $this->_maxDepth = (int) $maxDepth;
       
   237         }
       
   238         return $this;
       
   239     }
       
   240 
       
   241     /**
       
   242      * Returns maximum depth a page can have to be included when rendering
       
   243      *
       
   244      * @return int|null  maximum depth or null
       
   245      */
       
   246     public function getMaxDepth()
       
   247     {
       
   248         return $this->_maxDepth;
       
   249     }
       
   250 
       
   251     /**
       
   252      * Set the indentation string for using in {@link render()}, optionally a
       
   253      * number of spaces to indent with
       
   254      *
       
   255      * @param  string|int $indent                          indentation string or
       
   256      *                                                     number of spaces
       
   257      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   258      *                                                     returns self
       
   259      */
       
   260     public function setIndent($indent)
       
   261     {
       
   262         $this->_indent = $this->_getWhitespace($indent);
       
   263         return $this;
       
   264     }
       
   265 
       
   266     /**
       
   267      * Returns indentation
       
   268      *
       
   269      * @return string
       
   270      */
       
   271     public function getIndent()
       
   272     {
       
   273         return $this->_indent;
       
   274     }
       
   275 
       
   276     /**
       
   277      * Sets translator to use in helper
       
   278      *
       
   279      * Implements {@link Zend_View_Helper_Navigation_Helper::setTranslator()}.
       
   280      *
       
   281      * @param  mixed $translator                           [optional] translator.
       
   282      *                                                     Expects an object of
       
   283      *                                                     type
       
   284      *                                                     {@link Zend_Translate_Adapter}
       
   285      *                                                     or {@link Zend_Translate},
       
   286      *                                                     or null. Default is
       
   287      *                                                     null, which sets no
       
   288      *                                                     translator.
       
   289      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   290      *                                                     returns self
       
   291      */
       
   292     public function setTranslator($translator = null)
       
   293     {
       
   294         if (null == $translator ||
       
   295             $translator instanceof Zend_Translate_Adapter) {
       
   296             $this->_translator = $translator;
       
   297         } elseif ($translator instanceof Zend_Translate) {
       
   298             $this->_translator = $translator->getAdapter();
       
   299         }
       
   300 
       
   301         return $this;
       
   302     }
       
   303 
       
   304     /**
       
   305      * Returns translator used in helper
       
   306      *
       
   307      * Implements {@link Zend_View_Helper_Navigation_Helper::getTranslator()}.
       
   308      *
       
   309      * @return Zend_Translate_Adapter|null  translator or null
       
   310      */
       
   311     public function getTranslator()
       
   312     {
       
   313         if (null === $this->_translator) {
       
   314             require_once 'Zend/Registry.php';
       
   315             if (Zend_Registry::isRegistered('Zend_Translate')) {
       
   316                 $this->setTranslator(Zend_Registry::get('Zend_Translate'));
       
   317             }
       
   318         }
       
   319 
       
   320         return $this->_translator;
       
   321     }
       
   322 
       
   323     /**
       
   324      * Sets ACL to use when iterating pages
       
   325      *
       
   326      * Implements {@link Zend_View_Helper_Navigation_Helper::setAcl()}.
       
   327      *
       
   328      * @param  Zend_Acl $acl                               [optional] ACL object.
       
   329      *                                                     Default is null.
       
   330      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   331      *                                                     returns self
       
   332      */
       
   333     public function setAcl(Zend_Acl $acl = null)
       
   334     {
       
   335         $this->_acl = $acl;
       
   336         return $this;
       
   337     }
       
   338 
       
   339     /**
       
   340      * Returns ACL or null if it isn't set using {@link setAcl()} or
       
   341      * {@link setDefaultAcl()}
       
   342      *
       
   343      * Implements {@link Zend_View_Helper_Navigation_Helper::getAcl()}.
       
   344      *
       
   345      * @return Zend_Acl|null  ACL object or null
       
   346      */
       
   347     public function getAcl()
       
   348     {
       
   349         if ($this->_acl === null && self::$_defaultAcl !== null) {
       
   350             return self::$_defaultAcl;
       
   351         }
       
   352 
       
   353         return $this->_acl;
       
   354     }
       
   355 
       
   356     /**
       
   357      * Sets ACL role(s) to use when iterating pages
       
   358      *
       
   359      * Implements {@link Zend_View_Helper_Navigation_Helper::setRole()}.
       
   360      *
       
   361      * @param  mixed $role                                 [optional] role to
       
   362      *                                                     set. Expects a string,
       
   363      *                                                     an instance of type
       
   364      *                                                     {@link Zend_Acl_Role_Interface},
       
   365      *                                                     or null. Default is
       
   366      *                                                     null, which will set
       
   367      *                                                     no role.
       
   368      * @throws Zend_View_Exception                         if $role is invalid
       
   369      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   370      *                                                     returns self
       
   371      */
       
   372     public function setRole($role = null)
       
   373     {
       
   374         if (null === $role || is_string($role) ||
       
   375             $role instanceof Zend_Acl_Role_Interface) {
       
   376             $this->_role = $role;
       
   377         } else {
       
   378             require_once 'Zend/View/Exception.php';
       
   379             $e = new Zend_View_Exception(sprintf(
       
   380                 '$role must be a string, null, or an instance of ' 
       
   381                 .  'Zend_Acl_Role_Interface; %s given',
       
   382                 gettype($role)
       
   383             ));
       
   384             $e->setView($this->view);
       
   385             throw $e;
       
   386         }
       
   387 
       
   388         return $this;
       
   389     }
       
   390 
       
   391     /**
       
   392      * Returns ACL role to use when iterating pages, or null if it isn't set
       
   393      * using {@link setRole()} or {@link setDefaultRole()}
       
   394      *
       
   395      * Implements {@link Zend_View_Helper_Navigation_Helper::getRole()}.
       
   396      *
       
   397      * @return string|Zend_Acl_Role_Interface|null  role or null
       
   398      */
       
   399     public function getRole()
       
   400     {
       
   401         if ($this->_role === null && self::$_defaultRole !== null) {
       
   402             return self::$_defaultRole;
       
   403         }
       
   404 
       
   405         return $this->_role;
       
   406     }
       
   407 
       
   408     /**
       
   409      * Sets whether ACL should be used
       
   410      *
       
   411      * Implements {@link Zend_View_Helper_Navigation_Helper::setUseAcl()}.
       
   412      *
       
   413      * @param  bool $useAcl                                [optional] whether ACL
       
   414      *                                                     should be used.
       
   415      *                                                     Default is true.
       
   416      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   417      *                                                     returns self
       
   418      */
       
   419     public function setUseAcl($useAcl = true)
       
   420     {
       
   421         $this->_useAcl = (bool) $useAcl;
       
   422         return $this;
       
   423     }
       
   424 
       
   425     /**
       
   426      * Returns whether ACL should be used
       
   427      *
       
   428      * Implements {@link Zend_View_Helper_Navigation_Helper::getUseAcl()}.
       
   429      *
       
   430      * @return bool  whether ACL should be used
       
   431      */
       
   432     public function getUseAcl()
       
   433     {
       
   434         return $this->_useAcl;
       
   435     }
       
   436 
       
   437     /**
       
   438      * Return renderInvisible flag
       
   439      *
       
   440      * @return bool
       
   441      */
       
   442     public function getRenderInvisible()
       
   443     {
       
   444         return $this->_renderInvisible;
       
   445     }
       
   446 
       
   447     /**
       
   448      * Render invisible items?
       
   449      *
       
   450      * @param  bool $renderInvisible                       [optional] boolean flag
       
   451      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface
       
   452      *                                                     returns self
       
   453      */
       
   454     public function setRenderInvisible($renderInvisible = true)
       
   455     {
       
   456         $this->_renderInvisible = (bool) $renderInvisible;
       
   457         return $this;
       
   458     }
       
   459 
       
   460     /**
       
   461      * Sets whether translator should be used
       
   462      *
       
   463      * Implements {@link Zend_View_Helper_Navigation_Helper::setUseTranslator()}.
       
   464      *
       
   465      * @param  bool $useTranslator                         [optional] whether
       
   466      *                                                     translator should be
       
   467      *                                                     used. Default is true.
       
   468      * @return Zend_View_Helper_Navigation_HelperAbstract  fluent interface,
       
   469      *                                                     returns self
       
   470      */
       
   471     public function setUseTranslator($useTranslator = true)
       
   472     {
       
   473         $this->_useTranslator = (bool) $useTranslator;
       
   474         return $this;
       
   475     }
       
   476 
       
   477     /**
       
   478      * Returns whether translator should be used
       
   479      *
       
   480      * Implements {@link Zend_View_Helper_Navigation_Helper::getUseTranslator()}.
       
   481      *
       
   482      * @return bool  whether translator should be used
       
   483      */
       
   484     public function getUseTranslator()
       
   485     {
       
   486         return $this->_useTranslator;
       
   487     }
       
   488 
       
   489     // Magic overloads:
       
   490 
       
   491     /**
       
   492      * Magic overload: Proxy calls to the navigation container
       
   493      *
       
   494      * @param  string $method             method name in container
       
   495      * @param  array  $arguments          [optional] arguments to pass
       
   496      * @return mixed                      returns what the container returns
       
   497      * @throws Zend_Navigation_Exception  if method does not exist in container
       
   498      */
       
   499     public function __call($method, array $arguments = array())
       
   500     {
       
   501         return call_user_func_array(
       
   502                 array($this->getContainer(), $method),
       
   503                 $arguments);
       
   504     }
       
   505 
       
   506     /**
       
   507      * Magic overload: Proxy to {@link render()}.
       
   508      *
       
   509      * This method will trigger an E_USER_ERROR if rendering the helper causes
       
   510      * an exception to be thrown.
       
   511      *
       
   512      * Implements {@link Zend_View_Helper_Navigation_Helper::__toString()}.
       
   513      *
       
   514      * @return string
       
   515      */
       
   516     public function __toString()
       
   517     {
       
   518         try {
       
   519             return $this->render();
       
   520         } catch (Exception $e) {
       
   521             $msg = get_class($e) . ': ' . $e->getMessage();
       
   522             trigger_error($msg, E_USER_ERROR);
       
   523             return '';
       
   524         }
       
   525     }
       
   526 
       
   527     // Public methods:
       
   528 
       
   529     /**
       
   530      * Finds the deepest active page in the given container
       
   531      *
       
   532      * @param  Zend_Navigation_Container $container  container to search
       
   533      * @param  int|null                  $minDepth   [optional] minimum depth
       
   534      *                                               required for page to be
       
   535      *                                               valid. Default is to use
       
   536      *                                               {@link getMinDepth()}. A
       
   537      *                                               null value means no minimum
       
   538      *                                               depth required.
       
   539      * @param  int|null                  $minDepth   [optional] maximum depth
       
   540      *                                               a page can have to be
       
   541      *                                               valid. Default is to use
       
   542      *                                               {@link getMaxDepth()}. A
       
   543      *                                               null value means no maximum
       
   544      *                                               depth required.
       
   545      * @return array                                 an associative array with
       
   546      *                                               the values 'depth' and
       
   547      *                                               'page', or an empty array
       
   548      *                                               if not found
       
   549      */
       
   550     public function findActive(Zend_Navigation_Container $container,
       
   551                                $minDepth = null,
       
   552                                $maxDepth = -1)
       
   553     {
       
   554         if (!is_int($minDepth)) {
       
   555             $minDepth = $this->getMinDepth();
       
   556         }
       
   557         if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) {
       
   558             $maxDepth = $this->getMaxDepth();
       
   559         }
       
   560 
       
   561         $found  = null;
       
   562         $foundDepth = -1;
       
   563         $iterator = new RecursiveIteratorIterator($container,
       
   564                 RecursiveIteratorIterator::CHILD_FIRST);
       
   565 
       
   566         foreach ($iterator as $page) {
       
   567             $currDepth = $iterator->getDepth();
       
   568             if ($currDepth < $minDepth || !$this->accept($page)) {
       
   569                 // page is not accepted
       
   570                 continue;
       
   571             }
       
   572 
       
   573             if ($page->isActive(false) && $currDepth > $foundDepth) {
       
   574                 // found an active page at a deeper level than before
       
   575                 $found = $page;
       
   576                 $foundDepth = $currDepth;
       
   577             }
       
   578         }
       
   579 
       
   580         if (is_int($maxDepth) && $foundDepth > $maxDepth) {
       
   581             while ($foundDepth > $maxDepth) {
       
   582                 if (--$foundDepth < $minDepth) {
       
   583                     $found = null;
       
   584                     break;
       
   585                 }
       
   586 
       
   587                 $found = $found->getParent();
       
   588                 if (!$found instanceof Zend_Navigation_Page) {
       
   589                     $found = null;
       
   590                     break;
       
   591                 }
       
   592             }
       
   593         }
       
   594 
       
   595         if ($found) {
       
   596             return array('page' => $found, 'depth' => $foundDepth);
       
   597         } else {
       
   598             return array();
       
   599         }
       
   600     }
       
   601 
       
   602     /**
       
   603      * Checks if the helper has a container
       
   604      *
       
   605      * Implements {@link Zend_View_Helper_Navigation_Helper::hasContainer()}.
       
   606      *
       
   607      * @return bool  whether the helper has a container or not
       
   608      */
       
   609     public function hasContainer()
       
   610     {
       
   611         return null !== $this->_container;
       
   612     }
       
   613 
       
   614     /**
       
   615      * Checks if the helper has an ACL instance
       
   616      *
       
   617      * Implements {@link Zend_View_Helper_Navigation_Helper::hasAcl()}.
       
   618      *
       
   619      * @return bool  whether the helper has a an ACL instance or not
       
   620      */
       
   621     public function hasAcl()
       
   622     {
       
   623         return null !== $this->_acl;
       
   624     }
       
   625 
       
   626     /**
       
   627      * Checks if the helper has an ACL role
       
   628      *
       
   629      * Implements {@link Zend_View_Helper_Navigation_Helper::hasRole()}.
       
   630      *
       
   631      * @return bool  whether the helper has a an ACL role or not
       
   632      */
       
   633     public function hasRole()
       
   634     {
       
   635         return null !== $this->_role;
       
   636     }
       
   637 
       
   638     /**
       
   639      * Checks if the helper has a translator
       
   640      *
       
   641      * Implements {@link Zend_View_Helper_Navigation_Helper::hasTranslator()}.
       
   642      *
       
   643      * @return bool  whether the helper has a translator or not
       
   644      */
       
   645     public function hasTranslator()
       
   646     {
       
   647         return null !== $this->_translator;
       
   648     }
       
   649 
       
   650     /**
       
   651      * Returns an HTML string containing an 'a' element for the given page
       
   652      *
       
   653      * @param  Zend_Navigation_Page $page  page to generate HTML for
       
   654      * @return string                      HTML string for the given page
       
   655      */
       
   656     public function htmlify(Zend_Navigation_Page $page)
       
   657     {
       
   658         // get label and title for translating
       
   659         $label = $page->getLabel();
       
   660         $title = $page->getTitle();
       
   661 
       
   662         if ($this->getUseTranslator() && $t = $this->getTranslator()) {
       
   663             if (is_string($label) && !empty($label)) {
       
   664                 $label = $t->translate($label);
       
   665             }
       
   666             if (is_string($title) && !empty($title)) {
       
   667                 $title = $t->translate($title);
       
   668             }
       
   669         }
       
   670 
       
   671         // get attribs for anchor element
       
   672         $attribs = array(
       
   673             'id'     => $page->getId(),
       
   674             'title'  => $title,
       
   675             'class'  => $page->getClass(),
       
   676             'href'   => $page->getHref(),
       
   677             'target' => $page->getTarget()
       
   678         );
       
   679 
       
   680         return '<a' . $this->_htmlAttribs($attribs) . '>'
       
   681              . $this->view->escape($label)
       
   682              . '</a>';
       
   683     }
       
   684 
       
   685     // Iterator filter methods:
       
   686 
       
   687     /**
       
   688      * Determines whether a page should be accepted when iterating
       
   689      *
       
   690      * Rules:
       
   691      * - If a page is not visible it is not accepted, unless RenderInvisible has
       
   692      *   been set to true.
       
   693      * - If helper has no ACL, page is accepted
       
   694      * - If helper has ACL, but no role, page is not accepted
       
   695      * - If helper has ACL and role:
       
   696      *  - Page is accepted if it has no resource or privilege
       
   697      *  - Page is accepted if ACL allows page's resource or privilege
       
   698      * - If page is accepted by the rules above and $recursive is true, the page
       
   699      *   will not be accepted if it is the descendant of a non-accepted page.
       
   700      *
       
   701      * @param  Zend_Navigation_Page $page      page to check
       
   702      * @param  bool                $recursive  [optional] if true, page will not
       
   703      *                                         be accepted if it is the
       
   704      *                                         descendant of a page that is not
       
   705      *                                         accepted. Default is true.
       
   706      * @return bool                            whether page should be accepted
       
   707      */
       
   708     public function accept(Zend_Navigation_Page $page, $recursive = true)
       
   709     {
       
   710         // accept by default
       
   711         $accept = true;
       
   712 
       
   713         if (!$page->isVisible(false) && !$this->getRenderInvisible()) {
       
   714             // don't accept invisible pages
       
   715             $accept = false;
       
   716         } elseif ($this->getUseAcl() && !$this->_acceptAcl($page)) {
       
   717             // acl is not amused
       
   718             $accept = false;
       
   719         }
       
   720 
       
   721         if ($accept && $recursive) {
       
   722             $parent = $page->getParent();
       
   723             if ($parent instanceof Zend_Navigation_Page) {
       
   724                 $accept = $this->accept($parent, true);
       
   725             }
       
   726         }
       
   727 
       
   728         return $accept;
       
   729     }
       
   730 
       
   731     /**
       
   732      * Determines whether a page should be accepted by ACL when iterating
       
   733      *
       
   734      * Rules:
       
   735      * - If helper has no ACL, page is accepted
       
   736      * - If page has a resource or privilege defined, page is accepted
       
   737      *   if the ACL allows access to it using the helper's role
       
   738      * - If page has no resource or privilege, page is accepted
       
   739      *
       
   740      * @param  Zend_Navigation_Page $page  page to check
       
   741      * @return bool                        whether page is accepted by ACL
       
   742      */
       
   743     protected function _acceptAcl(Zend_Navigation_Page $page)
       
   744     {
       
   745         if (!$acl = $this->getAcl()) {
       
   746             // no acl registered means don't use acl
       
   747             return true;
       
   748         }
       
   749 
       
   750         $role = $this->getRole();
       
   751         $resource = $page->getResource();
       
   752         $privilege = $page->getPrivilege();
       
   753 
       
   754         if ($resource || $privilege) {
       
   755             // determine using helper role and page resource/privilege
       
   756             return $acl->isAllowed($role, $resource, $privilege);
       
   757         }
       
   758 
       
   759         return true;
       
   760     }
       
   761 
       
   762     // Util methods:
       
   763 
       
   764     /**
       
   765      * Retrieve whitespace representation of $indent
       
   766      *
       
   767      * @param  int|string $indent
       
   768      * @return string
       
   769      */
       
   770     protected function _getWhitespace($indent)
       
   771     {
       
   772         if (is_int($indent)) {
       
   773             $indent = str_repeat(' ', $indent);
       
   774         }
       
   775 
       
   776         return (string) $indent;
       
   777     }
       
   778 
       
   779     /**
       
   780      * Converts an associative array to a string of tag attributes.
       
   781      *
       
   782      * Overloads {@link Zend_View_Helper_HtmlElement::_htmlAttribs()}.
       
   783      *
       
   784      * @param  array $attribs  an array where each key-value pair is converted
       
   785      *                         to an attribute name and value
       
   786      * @return string          an attribute string
       
   787      */
       
   788     protected function _htmlAttribs($attribs)
       
   789     {
       
   790         // filter out null values and empty string values
       
   791         foreach ($attribs as $key => $value) {
       
   792             if ($value === null || (is_string($value) && !strlen($value))) {
       
   793                 unset($attribs[$key]);
       
   794             }
       
   795         }
       
   796 
       
   797         return parent::_htmlAttribs($attribs);
       
   798     }
       
   799 
       
   800     /**
       
   801      * Normalize an ID
       
   802      *
       
   803      * Overrides {@link Zend_View_Helper_HtmlElement::_normalizeId()}.
       
   804      *
       
   805      * @param  string $value
       
   806      * @return string
       
   807      */
       
   808     protected function _normalizeId($value)
       
   809     {
       
   810         $prefix = get_class($this);
       
   811         $prefix = strtolower(trim(substr($prefix, strrpos($prefix, '_')), '_'));
       
   812 
       
   813         return $prefix . '-' . $value;
       
   814     }
       
   815 
       
   816     // Static methods:
       
   817 
       
   818     /**
       
   819      * Sets default ACL to use if another ACL is not explicitly set
       
   820      *
       
   821      * @param  Zend_Acl $acl  [optional] ACL object. Default is null, which
       
   822      *                        sets no ACL object.
       
   823      * @return void
       
   824      */
       
   825     public static function setDefaultAcl(Zend_Acl $acl = null)
       
   826     {
       
   827         self::$_defaultAcl = $acl;
       
   828     }
       
   829 
       
   830     /**
       
   831      * Sets default ACL role(s) to use when iterating pages if not explicitly
       
   832      * set later with {@link setRole()}
       
   833      *
       
   834      * @param  midex $role               [optional] role to set. Expects null,
       
   835      *                                   string, or an instance of
       
   836      *                                   {@link Zend_Acl_Role_Interface}.
       
   837      *                                   Default is null, which sets no default
       
   838      *                                   role.
       
   839      * @throws Zend_View_Exception       if role is invalid
       
   840      * @return void
       
   841      */
       
   842     public static function setDefaultRole($role = null)
       
   843     {
       
   844         if (null === $role ||
       
   845             is_string($role) ||
       
   846             $role instanceof Zend_Acl_Role_Interface) {
       
   847             self::$_defaultRole = $role;
       
   848         } else {
       
   849             require_once 'Zend/View/Exception.php';
       
   850             throw new Zend_View_Exception(
       
   851                 '$role must be null|string|Zend_Acl_Role_Interface'
       
   852             );
       
   853         }
       
   854     }
       
   855 }