Source of file AbstractHelper.php
Size: 26,126 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/View/Helper/Navigation/AbstractHelper.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946 | <?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\View\Helper\Navigation; use RecursiveIteratorIterator; use Zend\EventManager\EventManager; use Zend\EventManager\EventManagerAwareInterface; use Zend\EventManager\EventManagerInterface; use Zend\I18n\Translator\TranslatorInterface as Translator; use Zend\I18n\Translator\TranslatorAwareInterface; use Zend\Navigation; use Zend\Navigation\Page\AbstractPage; use Zend\Permissions\Acl; use Zend\ServiceManager\ServiceLocatorAwareInterface; use Zend\ServiceManager\ServiceLocatorInterface; use Zend\View; use Zend\View\Exception; /** * Base class for navigational helpers */ abstract class AbstractHelper extends View\Helper\AbstractHtmlElement implements EventManagerAwareInterface, HelperInterface, ServiceLocatorAwareInterface, TranslatorAwareInterface { /** * @var EventManagerInterface */ protected $events; /** * @var ServiceLocatorInterface */ protected $serviceLocator; /** * AbstractContainer to operate on by default * * @var Navigation\AbstractContainer */ protected $container; /** * The minimum depth a page must have to be included when rendering * * @var int */ protected $minDepth; /** * The maximum depth a page can have to be included when rendering * * @var int */ protected $maxDepth; /** * Indentation string * * @var string */ protected $indent = ''; /** * ACL to use when iterating pages * * @var Acl\AclInterface */ protected $acl; /** * Whether invisible items should be rendered by this helper * * @var bool */ protected $renderInvisible = false; /** * ACL role to use when iterating pages * * @var string|Acl\Role\RoleInterface */ protected $role; /** * Whether ACL should be used for filtering out pages * * @var bool */ protected $useAcl = true; /** * Translator (optional) * * @var Translator */ protected $translator; /** * Translator text domain (optional) * * @var string */ protected $translatorTextDomain = 'default'; /** * Whether translator should be used * * @var bool */ protected $translatorEnabled = true; /** * Default ACL to use when iterating pages if not explicitly set in the * instance by calling {@link setAcl()} * * @var Acl\AclInterface */ protected static $defaultAcl; /** * Default ACL role to use when iterating pages if not explicitly set in the * instance by calling {@link setRole()} * * @var string|Acl\Role\RoleInterface */ protected static $defaultRole; /** * Magic overload: Proxy calls to the navigation container * * @param string $method method name in container * @param array $arguments rguments to pass * @return mixed * @throws Navigation\Exception\ExceptionInterface */ public function __call($method, array $arguments = array()) { return call_user_func_array( array($this->getContainer(), $method), $arguments); } /** * Magic overload: Proxy to {@link render()}. * * This method will trigger an E_USER_ERROR if rendering the helper causes * an exception to be thrown. * * Implements {@link HelperInterface::__toString()}. * * @return string */ public function __toString() { try { return $this->render(); } catch (\Exception $e) { $msg = get_class($e) . ': ' . $e->getMessage(); trigger_error($msg, E_USER_ERROR); return ''; } } /** * Finds the deepest active page in the given container * * @param Navigation\AbstractContainer $container container to search * @param int|null $minDepth [optional] minimum depth * required for page to be * valid. Default is to use * {@link getMinDepth()}. A * null value means no minimum * depth required. * @param int|null $maxDepth [optional] maximum depth * a page can have to be * valid. Default is to use * {@link getMaxDepth()}. A * null value means no maximum * depth required. * @return array an associative array with * the values 'depth' and * 'page', or an empty array * if not found */ public function findActive($container, $minDepth = null, $maxDepth = -1) { $this->parseContainer($container); if (!is_int($minDepth)) { $minDepth = $this->getMinDepth(); } if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) { $maxDepth = $this->getMaxDepth(); } $found = null; $foundDepth = -1; $iterator = new RecursiveIteratorIterator( $container, RecursiveIteratorIterator::CHILD_FIRST ); /** @var \Zend\Navigation\Page\AbstractPage $page */ foreach ($iterator as $page) { $currDepth = $iterator->getDepth(); if ($currDepth < $minDepth || !$this->accept($page)) { // page is not accepted continue; } if ($page->isActive(false) && $currDepth > $foundDepth) { // found an active page at a deeper level than before $found = $page; $foundDepth = $currDepth; } } if (is_int($maxDepth) && $foundDepth > $maxDepth) { while ($foundDepth > $maxDepth) { if (--$foundDepth < $minDepth) { $found = null; break; } $found = $found->getParent(); if (!$found instanceof AbstractPage) { $found = null; break; } } } if ($found) { return array('page' => $found, 'depth' => $foundDepth); } return array(); } /** * Verifies container and eventually fetches it from service locator if it is a string * * @param Navigation\AbstractContainer|string|null $container * @throws Exception\InvalidArgumentException */ protected function parseContainer(&$container = null) { if (null === $container) { return; } if (is_string($container)) { if (!$this->getServiceLocator()) { throw new Exception\InvalidArgumentException(sprintf( 'Attempted to set container with alias "%s" but no ServiceLocator was set', $container )); } /** * Load the navigation container from the root service locator * * The navigation container is probably located in Zend\ServiceManager\ServiceManager * and not in the View\HelperPluginManager. If the set service locator is a * HelperPluginManager, access the navigation container via the main service locator. */ $sl = $this->getServiceLocator(); if ($sl instanceof View\HelperPluginManager) { $sl = $sl->getServiceLocator(); } $container = $sl->get($container); return; } if (!$container instanceof Navigation\AbstractContainer) { throw new Exception\InvalidArgumentException( 'Container must be a string alias or an instance of ' . 'Zend\Navigation\AbstractContainer' ); } } // Iterator filter methods: /** * Determines whether a page should be accepted when iterating * * Default listener may be 'overridden' by attaching listener to 'isAllowed' * method. Listener must be 'short circuited' if overriding default ACL * listener. * * Rules: * - If a page is not visible it is not accepted, unless RenderInvisible has * been set to true * - If $useAcl is true (default is true): * - Page is accepted if listener returns true, otherwise false * - If page is accepted and $recursive is true, the page * will not be accepted if it is the descendant of a non-accepted page * * @param AbstractPage $page page to check * @param bool $recursive [optional] if true, page will not be * accepted if it is the descendant of * a page that is not accepted. Default * is true * * @return bool Whether page should be accepted */ public function accept(AbstractPage $page, $recursive = true) { $accept = true; if (!$page->isVisible(false) && !$this->getRenderInvisible()) { $accept = false; } elseif ($this->getUseAcl()) { $acl = $this->getAcl(); $role = $this->getRole(); $params = array('acl' => $acl, 'page' => $page, 'role' => $role); $accept = $this->isAllowed($params); } if ($accept && $recursive) { $parent = $page->getParent(); if ($parent instanceof AbstractPage) { $accept = $this->accept($parent, true); } } return $accept; } /** * Determines whether a page should be allowed given certain parameters * * @param array $params * @return bool */ protected function isAllowed($params) { $results = $this->getEventManager()->trigger(__FUNCTION__, $this, $params); return $results->last(); } // Util methods: /** * Retrieve whitespace representation of $indent * * @param int|string $indent * @return string */ protected function getWhitespace($indent) { if (is_int($indent)) { $indent = str_repeat(' ', $indent); } return (string) $indent; } /** * Converts an associative array to a string of tag attributes. * * Overloads {@link View\Helper\AbstractHtmlElement::htmlAttribs()}. * * @param array $attribs an array where each key-value pair is converted * to an attribute name and value * @return string */ protected function htmlAttribs($attribs) { // filter out null values and empty string values foreach ($attribs as $key => $value) { if ($value === null || (is_string($value) && !strlen($value))) { unset($attribs[$key]); } } return parent::htmlAttribs($attribs); } /** * Returns an HTML string containing an 'a' element for the given page * * @param AbstractPage $page page to generate HTML for * @return string HTML string (<a href="…">Label</a>) */ public function htmlify(AbstractPage $page) { $label = $this->translate($page->getLabel(), $page->getTextDomain()); $title = $this->translate($page->getTitle(), $page->getTextDomain()); // get attribs for anchor element $attribs = array( 'id' => $page->getId(), 'title' => $title, 'class' => $page->getClass(), 'href' => $page->getHref(), 'target' => $page->getTarget() ); /** @var \Zend\View\Helper\EscapeHtml $escaper */ $escaper = $this->view->plugin('escapeHtml'); $label = $escaper($label); return '<a' . $this->htmlAttribs($attribs) . '>' . $label . '</a>'; } /** * Translate a message (for label, title, …) * * @param string $message ID of the message to translate * @param string $textDomain Text domain (category name for the translations) * @return string Translated message */ protected function translate($message, $textDomain = null) { if (is_string($message) && !empty($message)) { if (null !== ($translator = $this->getTranslator())) { if (null === $textDomain) { $textDomain = $this->getTranslatorTextDomain(); } return $translator->translate($message, $textDomain); } } return $message; } /** * Normalize an ID * * Overrides {@link View\Helper\AbstractHtmlElement::normalizeId()}. * * @param string $value * @return string */ protected function normalizeId($value) { $prefix = get_class($this); $prefix = strtolower(trim(substr($prefix, strrpos($prefix, '\\')), '\\')); return $prefix . '-' . $value; } /** * Sets ACL to use when iterating pages * * Implements {@link HelperInterface::setAcl()}. * * @param Acl\AclInterface $acl ACL object. * @return AbstractHelper */ public function setAcl(Acl\AclInterface $acl = null) { $this->acl = $acl; return $this; } /** * Returns ACL or null if it isn't set using {@link setAcl()} or * {@link setDefaultAcl()} * * Implements {@link HelperInterface::getAcl()}. * * @return Acl\AclInterface|null ACL object or null */ public function getAcl() { if ($this->acl === null && static::$defaultAcl !== null) { return static::$defaultAcl; } return $this->acl; } /** * Checks if the helper has an ACL instance * * Implements {@link HelperInterface::hasAcl()}. * * @return bool */ public function hasAcl() { if ($this->acl instanceof Acl\Acl || static::$defaultAcl instanceof Acl\Acl ) { return true; } return false; } /** * Set the event manager. * * @param EventManagerInterface $events * @return AbstractHelper */ public function setEventManager(EventManagerInterface $events) { $events->setIdentifiers(array( __CLASS__, get_called_class(), )); $this->events = $events; $this->setDefaultListeners(); return $this; } /** * Get the event manager. * * @return EventManagerInterface */ public function getEventManager() { if (null === $this->events) { $this->setEventManager(new EventManager()); } return $this->events; } /** * Sets navigation container the helper operates on by default * * Implements {@link HelperInterface::setContainer()}. * * @param string|Navigation\AbstractContainer $container Default is null, meaning container will be reset. * @return AbstractHelper */ public function setContainer($container = null) { $this->parseContainer($container); $this->container = $container; return $this; } /** * Returns the navigation container helper operates on by default * * Implements {@link HelperInterface::getContainer()}. * * If no container is set, a new container will be instantiated and * stored in the helper. * * @return Navigation\AbstractContainer navigation container */ public function getContainer() { if (null === $this->container) { $this->container = new Navigation\Navigation(); } return $this->container; } /** * Checks if the helper has a container * * Implements {@link HelperInterface::hasContainer()}. * * @return bool */ public function hasContainer() { return null !== $this->container; } /** * Set the indentation string for using in {@link render()}, optionally a * number of spaces to indent with * * @param string|int $indent * @return AbstractHelper */ public function setIndent($indent) { $this->indent = $this->getWhitespace($indent); return $this; } /** * Returns indentation * * @return string */ public function getIndent() { return $this->indent; } /** * Sets the maximum depth a page can have to be included when rendering * * @param int $maxDepth Default is null, which sets no maximum depth. * @return AbstractHelper */ public function setMaxDepth($maxDepth = null) { if (null === $maxDepth || is_int($maxDepth)) { $this->maxDepth = $maxDepth; } else { $this->maxDepth = (int) $maxDepth; } return $this; } /** * Returns maximum depth a page can have to be included when rendering * * @return int|null */ public function getMaxDepth() { return $this->maxDepth; } /** * Sets the minimum depth a page must have to be included when rendering * * @param int $minDepth Default is null, which sets no minimum depth. * @return AbstractHelper */ public function setMinDepth($minDepth = null) { if (null === $minDepth || is_int($minDepth)) { $this->minDepth = $minDepth; } else { $this->minDepth = (int) $minDepth; } return $this; } /** * Returns minimum depth a page must have to be included when rendering * * @return int|null */ public function getMinDepth() { if (!is_int($this->minDepth) || $this->minDepth < 0) { return 0; } return $this->minDepth; } /** * Render invisible items? * * @param bool $renderInvisible * @return AbstractHelper */ public function setRenderInvisible($renderInvisible = true) { $this->renderInvisible = (bool) $renderInvisible; return $this; } /** * Return renderInvisible flag * * @return bool */ public function getRenderInvisible() { return $this->renderInvisible; } /** * Sets ACL role(s) to use when iterating pages * * Implements {@link HelperInterface::setRole()}. * * @param mixed $role [optional] role to set. Expects a string, an * instance of type {@link Acl\Role\RoleInterface}, or null. Default * is null, which will set no role. * @return AbstractHelper * @throws Exception\InvalidArgumentException */ public function setRole($role = null) { if (null === $role || is_string($role) || $role instanceof Acl\Role\RoleInterface ) { $this->role = $role; } else { throw new Exception\InvalidArgumentException(sprintf( '$role must be a string, null, or an instance of ' . 'Zend\Permissions\Role\RoleInterface; %s given', (is_object($role) ? get_class($role) : gettype($role)) )); } return $this; } /** * Returns ACL role to use when iterating pages, or null if it isn't set * using {@link setRole()} or {@link setDefaultRole()} * * Implements {@link HelperInterface::getRole()}. * * @return string|Acl\Role\RoleInterface|null */ public function getRole() { if ($this->role === null && static::$defaultRole !== null) { return static::$defaultRole; } return $this->role; } /** * Checks if the helper has an ACL role * * Implements {@link HelperInterface::hasRole()}. * * @return bool */ public function hasRole() { if ($this->role instanceof Acl\Role\RoleInterface || is_string($this->role) || static::$defaultRole instanceof Acl\Role\RoleInterface || is_string(static::$defaultRole) ) { return true; } return false; } /** * Set the service locator. * * @param ServiceLocatorInterface $serviceLocator * @return AbstractHelper */ public function setServiceLocator(ServiceLocatorInterface $serviceLocator) { $this->serviceLocator = $serviceLocator; return $this; } /** * Get the service locator. * * @return ServiceLocatorInterface */ public function getServiceLocator() { return $this->serviceLocator; } // Translator methods - Good candidate to refactor as a trait with PHP 5.4 /** * Sets translator to use in helper * * @param Translator $translator [optional] translator. * Default is null, which sets no translator. * @param string $textDomain [optional] text domain * Default is null, which skips setTranslatorTextDomain * @return AbstractHelper */ public function setTranslator(Translator $translator = null, $textDomain = null) { $this->translator = $translator; if (null !== $textDomain) { $this->setTranslatorTextDomain($textDomain); } return $this; } /** * Returns translator used in helper * * @return Translator|null */ public function getTranslator() { if (! $this->isTranslatorEnabled()) { return null; } return $this->translator; } /** * Checks if the helper has a translator * * @return bool */ public function hasTranslator() { return (bool) $this->getTranslator(); } /** * Sets whether translator is enabled and should be used * * @param bool $enabled * @return AbstractHelper */ public function setTranslatorEnabled($enabled = true) { $this->translatorEnabled = (bool) $enabled; return $this; } /** * Returns whether translator is enabled and should be used * * @return bool */ public function isTranslatorEnabled() { return $this->translatorEnabled; } /** * Set translation text domain * * @param string $textDomain * @return AbstractHelper */ public function setTranslatorTextDomain($textDomain = 'default') { $this->translatorTextDomain = $textDomain; return $this; } /** * Return the translation text domain * * @return string */ public function getTranslatorTextDomain() { return $this->translatorTextDomain; } /** * Sets whether ACL should be used * * Implements {@link HelperInterface::setUseAcl()}. * * @param bool $useAcl * @return AbstractHelper */ public function setUseAcl($useAcl = true) { $this->useAcl = (bool) $useAcl; return $this; } /** * Returns whether ACL should be used * * Implements {@link HelperInterface::getUseAcl()}. * * @return bool */ public function getUseAcl() { return $this->useAcl; } // Static methods: /** * Sets default ACL to use if another ACL is not explicitly set * * @param Acl\AclInterface $acl [optional] ACL object. Default is null, which * sets no ACL object. * @return void */ public static function setDefaultAcl(Acl\AclInterface $acl = null) { static::$defaultAcl = $acl; } /** * Sets default ACL role(s) to use when iterating pages if not explicitly * set later with {@link setRole()} * * @param mixed $role [optional] role to set. Expects null, string, or an * instance of {@link Acl\Role\RoleInterface}. Default is null, which * sets no default role. * @return void * @throws Exception\InvalidArgumentException if role is invalid */ public static function setDefaultRole($role = null) { if (null === $role || is_string($role) || $role instanceof Acl\Role\RoleInterface ) { static::$defaultRole = $role; } else { throw new Exception\InvalidArgumentException(sprintf( '$role must be null|string|Zend\Permissions\Role\RoleInterface; received "%s"', (is_object($role) ? get_class($role) : gettype($role)) )); } } /** * Attaches default ACL listeners, if ACLs are in use */ protected function setDefaultListeners() { if (!$this->getUseAcl()) { return; } $this->getEventManager()->getSharedManager()->attach( 'Zend\View\Helper\Navigation\AbstractHelper', 'isAllowed', array('Zend\View\Helper\Navigation\Listener\AclListener', 'accept') ); } } |