--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/Zend/View/Helper/Navigation/Menu.php Mon Dec 13 18:29:26 2010 +0100
@@ -0,0 +1,647 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_View
+ * @subpackage Helper
+ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Menu.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_View_Helper_Navigation_HelperAbstract
+ */
+require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
+
+/**
+ * Helper for rendering menus from navigation containers
+ *
+ * @category Zend
+ * @package Zend_View
+ * @subpackage Helper
+ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_View_Helper_Navigation_Menu
+ extends Zend_View_Helper_Navigation_HelperAbstract
+{
+ /**
+ * CSS class to use for the ul element
+ *
+ * @var string
+ */
+ protected $_ulClass = 'navigation';
+
+ /**
+ * Whether only active branch should be rendered
+ *
+ * @var bool
+ */
+ protected $_onlyActiveBranch = false;
+
+ /**
+ * Whether parents should be rendered when only rendering active branch
+ *
+ * @var bool
+ */
+ protected $_renderParents = true;
+
+ /**
+ * Partial view script to use for rendering menu
+ *
+ * @var string|array
+ */
+ protected $_partial = null;
+
+ /**
+ * View helper entry point:
+ * Retrieves helper and optionally sets container to operate on
+ *
+ * @param Zend_Navigation_Container $container [optional] container to
+ * operate on
+ * @return Zend_View_Helper_Navigation_Menu fluent interface,
+ * returns self
+ */
+ public function menu(Zend_Navigation_Container $container = null)
+ {
+ if (null !== $container) {
+ $this->setContainer($container);
+ }
+
+ return $this;
+ }
+
+ // Accessors:
+
+ /**
+ * Sets CSS class to use for the first 'ul' element when rendering
+ *
+ * @param string $ulClass CSS class to set
+ * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
+ */
+ public function setUlClass($ulClass)
+ {
+ if (is_string($ulClass)) {
+ $this->_ulClass = $ulClass;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns CSS class to use for the first 'ul' element when rendering
+ *
+ * @return string CSS class
+ */
+ public function getUlClass()
+ {
+ return $this->_ulClass;
+ }
+
+ /**
+ * Sets a flag indicating whether only active branch should be rendered
+ *
+ * @param bool $flag [optional] render only active
+ * branch. Default is true.
+ * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
+ */
+ public function setOnlyActiveBranch($flag = true)
+ {
+ $this->_onlyActiveBranch = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Returns a flag indicating whether only active branch should be rendered
+ *
+ * By default, this value is false, meaning the entire menu will be
+ * be rendered.
+ *
+ * @return bool whether only active branch should be rendered
+ */
+ public function getOnlyActiveBranch()
+ {
+ return $this->_onlyActiveBranch;
+ }
+
+ /**
+ * Enables/disables rendering of parents when only rendering active branch
+ *
+ * See {@link setOnlyActiveBranch()} for more information.
+ *
+ * @param bool $flag [optional] render parents when
+ * rendering active branch.
+ * Default is true.
+ * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
+ */
+ public function setRenderParents($flag = true)
+ {
+ $this->_renderParents = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Returns flag indicating whether parents should be rendered when rendering
+ * only the active branch
+ *
+ * By default, this value is true.
+ *
+ * @return bool whether parents should be rendered
+ */
+ public function getRenderParents()
+ {
+ return $this->_renderParents;
+ }
+
+ /**
+ * Sets which partial view script to use for rendering menu
+ *
+ * @param string|array $partial partial view script or null. If
+ * an array is given, it is
+ * expected to contain two values;
+ * the partial view script to use,
+ * and the module where the script
+ * can be found.
+ * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
+ */
+ public function setPartial($partial)
+ {
+ if (null === $partial || is_string($partial) || is_array($partial)) {
+ $this->_partial = $partial;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns partial view script to use for rendering menu
+ *
+ * @return string|array|null
+ */
+ public function getPartial()
+ {
+ return $this->_partial;
+ }
+
+ // Public methods:
+
+ /**
+ * Returns an HTML string containing an 'a' element for the given page if
+ * the page's href is not empty, and a 'span' element if it is empty
+ *
+ * Overrides {@link Zend_View_Helper_Navigation_Abstract::htmlify()}.
+ *
+ * @param Zend_Navigation_Page $page page to generate HTML for
+ * @return string HTML string for the given page
+ */
+ public function htmlify(Zend_Navigation_Page $page)
+ {
+ // get label and title for translating
+ $label = $page->getLabel();
+ $title = $page->getTitle();
+
+ // translate label and title?
+ if ($this->getUseTranslator() && $t = $this->getTranslator()) {
+ if (is_string($label) && !empty($label)) {
+ $label = $t->translate($label);
+ }
+ if (is_string($title) && !empty($title)) {
+ $title = $t->translate($title);
+ }
+ }
+
+ // get attribs for element
+ $attribs = array(
+ 'id' => $page->getId(),
+ 'title' => $title,
+ 'class' => $page->getClass()
+ );
+
+ // does page have a href?
+ if ($href = $page->getHref()) {
+ $element = 'a';
+ $attribs['href'] = $href;
+ $attribs['target'] = $page->getTarget();
+ } else {
+ $element = 'span';
+ }
+
+ return '<' . $element . $this->_htmlAttribs($attribs) . '>'
+ . $this->view->escape($label)
+ . '</' . $element . '>';
+ }
+
+ /**
+ * Normalizes given render options
+ *
+ * @param array $options [optional] options to normalize
+ * @return array normalized options
+ */
+ protected function _normalizeOptions(array $options = array())
+ {
+ if (isset($options['indent'])) {
+ $options['indent'] = $this->_getWhitespace($options['indent']);
+ } else {
+ $options['indent'] = $this->getIndent();
+ }
+
+ if (isset($options['ulClass']) && $options['ulClass'] !== null) {
+ $options['ulClass'] = (string) $options['ulClass'];
+ } else {
+ $options['ulClass'] = $this->getUlClass();
+ }
+
+ if (array_key_exists('minDepth', $options)) {
+ if (null !== $options['minDepth']) {
+ $options['minDepth'] = (int) $options['minDepth'];
+ }
+ } else {
+ $options['minDepth'] = $this->getMinDepth();
+ }
+
+ if ($options['minDepth'] < 0 || $options['minDepth'] === null) {
+ $options['minDepth'] = 0;
+ }
+
+ if (array_key_exists('maxDepth', $options)) {
+ if (null !== $options['maxDepth']) {
+ $options['maxDepth'] = (int) $options['maxDepth'];
+ }
+ } else {
+ $options['maxDepth'] = $this->getMaxDepth();
+ }
+
+ if (!isset($options['onlyActiveBranch'])) {
+ $options['onlyActiveBranch'] = $this->getOnlyActiveBranch();
+ }
+
+ if (!isset($options['renderParents'])) {
+ $options['renderParents'] = $this->getRenderParents();
+ }
+
+ return $options;
+ }
+
+ // Render methods:
+
+ /**
+ * Renders the deepest active menu within [$minDepth, $maxDeth], (called
+ * from {@link renderMenu()})
+ *
+ * @param Zend_Navigation_Container $container container to render
+ * @param array $active active page and depth
+ * @param string $ulClass CSS class for first UL
+ * @param string $indent initial indentation
+ * @param int|null $minDepth minimum depth
+ * @param int|null $maxDepth maximum depth
+ * @return string rendered menu
+ */
+ protected function _renderDeepestMenu(Zend_Navigation_Container $container,
+ $ulClass,
+ $indent,
+ $minDepth,
+ $maxDepth)
+ {
+ if (!$active = $this->findActive($container, $minDepth - 1, $maxDepth)) {
+ return '';
+ }
+
+ // special case if active page is one below minDepth
+ if ($active['depth'] < $minDepth) {
+ if (!$active['page']->hasPages()) {
+ return '';
+ }
+ } else if (!$active['page']->hasPages()) {
+ // found pages has no children; render siblings
+ $active['page'] = $active['page']->getParent();
+ } else if (is_int($maxDepth) && $active['depth'] +1 > $maxDepth) {
+ // children are below max depth; render siblings
+ $active['page'] = $active['page']->getParent();
+ }
+
+ $ulClass = $ulClass ? ' class="' . $ulClass . '"' : '';
+ $html = $indent . '<ul' . $ulClass . '>' . self::EOL;
+
+ foreach ($active['page'] as $subPage) {
+ if (!$this->accept($subPage)) {
+ continue;
+ }
+ $liClass = $subPage->isActive(true) ? ' class="active"' : '';
+ $html .= $indent . ' <li' . $liClass . '>' . self::EOL;
+ $html .= $indent . ' ' . $this->htmlify($subPage) . self::EOL;
+ $html .= $indent . ' </li>' . self::EOL;
+ }
+
+ $html .= $indent . '</ul>';
+
+ return $html;
+ }
+
+ /**
+ * Renders a normal menu (called from {@link renderMenu()})
+ *
+ * @param Zend_Navigation_Container $container container to render
+ * @param string $ulClass CSS class for first UL
+ * @param string $indent initial indentation
+ * @param int|null $minDepth minimum depth
+ * @param int|null $maxDepth maximum depth
+ * @param bool $onlyActive render only active branch?
+ * @return string
+ */
+ protected function _renderMenu(Zend_Navigation_Container $container,
+ $ulClass,
+ $indent,
+ $minDepth,
+ $maxDepth,
+ $onlyActive)
+ {
+ $html = '';
+
+ // find deepest active
+ if ($found = $this->findActive($container, $minDepth, $maxDepth)) {
+ $foundPage = $found['page'];
+ $foundDepth = $found['depth'];
+ } else {
+ $foundPage = null;
+ }
+
+ // create iterator
+ $iterator = new RecursiveIteratorIterator($container,
+ RecursiveIteratorIterator::SELF_FIRST);
+ if (is_int($maxDepth)) {
+ $iterator->setMaxDepth($maxDepth);
+ }
+
+ // iterate container
+ $prevDepth = -1;
+ foreach ($iterator as $page) {
+ $depth = $iterator->getDepth();
+ $isActive = $page->isActive(true);
+ if ($depth < $minDepth || !$this->accept($page)) {
+ // page is below minDepth or not accepted by acl/visibilty
+ continue;
+ } else if ($onlyActive && !$isActive) {
+ // page is not active itself, but might be in the active branch
+ $accept = false;
+ if ($foundPage) {
+ if ($foundPage->hasPage($page)) {
+ // accept if page is a direct child of the active page
+ $accept = true;
+ } else if ($foundPage->getParent()->hasPage($page)) {
+ // page is a sibling of the active page...
+ if (!$foundPage->hasPages() ||
+ is_int($maxDepth) && $foundDepth + 1 > $maxDepth) {
+ // accept if active page has no children, or the
+ // children are too deep to be rendered
+ $accept = true;
+ }
+ }
+ }
+
+ if (!$accept) {
+ continue;
+ }
+ }
+
+ // make sure indentation is correct
+ $depth -= $minDepth;
+ $myIndent = $indent . str_repeat(' ', $depth);
+
+ if ($depth > $prevDepth) {
+ // start new ul tag
+ if ($ulClass && $depth == 0) {
+ $ulClass = ' class="' . $ulClass . '"';
+ } else {
+ $ulClass = '';
+ }
+ $html .= $myIndent . '<ul' . $ulClass . '>' . self::EOL;
+ } else if ($prevDepth > $depth) {
+ // close li/ul tags until we're at current depth
+ for ($i = $prevDepth; $i > $depth; $i--) {
+ $ind = $indent . str_repeat(' ', $i);
+ $html .= $ind . ' </li>' . self::EOL;
+ $html .= $ind . '</ul>' . self::EOL;
+ }
+ // close previous li tag
+ $html .= $myIndent . ' </li>' . self::EOL;
+ } else {
+ // close previous li tag
+ $html .= $myIndent . ' </li>' . self::EOL;
+ }
+
+ // render li tag and page
+ $liClass = $isActive ? ' class="active"' : '';
+ $html .= $myIndent . ' <li' . $liClass . '>' . self::EOL
+ . $myIndent . ' ' . $this->htmlify($page) . self::EOL;
+
+ // store as previous depth for next iteration
+ $prevDepth = $depth;
+ }
+
+ if ($html) {
+ // done iterating container; close open ul/li tags
+ for ($i = $prevDepth+1; $i > 0; $i--) {
+ $myIndent = $indent . str_repeat(' ', $i-1);
+ $html .= $myIndent . ' </li>' . self::EOL
+ . $myIndent . '</ul>' . self::EOL;
+ }
+ $html = rtrim($html, self::EOL);
+ }
+
+ return $html;
+ }
+
+ /**
+ * Renders helper
+ *
+ * Renders a HTML 'ul' for the given $container. If $container is not given,
+ * the container registered in the helper will be used.
+ *
+ * Available $options:
+ *
+ *
+ * @param Zend_Navigation_Container $container [optional] container to
+ * create menu from. Default
+ * is to use the container
+ * retrieved from
+ * {@link getContainer()}.
+ * @param array $options [optional] options for
+ * controlling rendering
+ * @return string rendered menu
+ */
+ public function renderMenu(Zend_Navigation_Container $container = null,
+ array $options = array())
+ {
+ if (null === $container) {
+ $container = $this->getContainer();
+ }
+
+ $options = $this->_normalizeOptions($options);
+
+ if ($options['onlyActiveBranch'] && !$options['renderParents']) {
+ $html = $this->_renderDeepestMenu($container,
+ $options['ulClass'],
+ $options['indent'],
+ $options['minDepth'],
+ $options['maxDepth']);
+ } else {
+ $html = $this->_renderMenu($container,
+ $options['ulClass'],
+ $options['indent'],
+ $options['minDepth'],
+ $options['maxDepth'],
+ $options['onlyActiveBranch']);
+ }
+
+ return $html;
+ }
+
+ /**
+ * Renders the inner-most sub menu for the active page in the $container
+ *
+ * This is a convenience method which is equivalent to the following call:
+ * <code>
+ * renderMenu($container, array(
+ * 'indent' => $indent,
+ * 'ulClass' => $ulClass,
+ * 'minDepth' => null,
+ * 'maxDepth' => null,
+ * 'onlyActiveBranch' => true,
+ * 'renderParents' => false
+ * ));
+ * </code>
+ *
+ * @param Zend_Navigation_Container $container [optional] container to
+ * render. Default is to render
+ * the container registered in
+ * the helper.
+ * @param string $ulClass [optional] CSS class to
+ * use for UL element. Default
+ * is to use the value from
+ * {@link getUlClass()}.
+ * @param string|int $indent [optional] indentation as
+ * a string or number of
+ * spaces. Default is to use
+ * the value retrieved from
+ * {@link getIndent()}.
+ * @return string rendered content
+ */
+ public function renderSubMenu(Zend_Navigation_Container $container = null,
+ $ulClass = null,
+ $indent = null)
+ {
+ return $this->renderMenu($container, array(
+ 'indent' => $indent,
+ 'ulClass' => $ulClass,
+ 'minDepth' => null,
+ 'maxDepth' => null,
+ 'onlyActiveBranch' => true,
+ 'renderParents' => false
+ ));
+ }
+
+ /**
+ * Renders the given $container by invoking the partial view helper
+ *
+ * The container will simply be passed on as a model to the view script
+ * as-is, and will be available in the partial script as 'container', e.g.
+ * <code>echo 'Number of pages: ', count($this->container);</code>.
+ *
+ * @param Zend_Navigation_Container $container [optional] container to
+ * pass to view script. Default
+ * is to use the container
+ * registered in the helper.
+ * @param string|array $partial [optional] partial view
+ * script to use. Default is to
+ * use the partial registered
+ * in the helper. If an array
+ * is given, it is expected to
+ * contain two values; the
+ * partial view script to use,
+ * and the module where the
+ * script can be found.
+ * @return string helper output
+ */
+ public function renderPartial(Zend_Navigation_Container $container = null,
+ $partial = null)
+ {
+ if (null === $container) {
+ $container = $this->getContainer();
+ }
+
+ if (null === $partial) {
+ $partial = $this->getPartial();
+ }
+
+ if (empty($partial)) {
+ require_once 'Zend/View/Exception.php';
+ $e = new Zend_View_Exception(
+ 'Unable to render menu: No partial view script provided'
+ );
+ $e->setView($this->view);
+ throw $e;
+ }
+
+ $model = array(
+ 'container' => $container
+ );
+
+ if (is_array($partial)) {
+ if (count($partial) != 2) {
+ require_once 'Zend/View/Exception.php';
+ $e = new Zend_View_Exception(
+ 'Unable to render menu: A view partial supplied as '
+ . 'an array must contain two values: partial view '
+ . 'script and module where script can be found'
+ );
+ $e->setView($this->view);
+ throw $e;
+ }
+
+ return $this->view->partial($partial[0], $partial[1], $model);
+ }
+
+ return $this->view->partial($partial, null, $model);
+ }
+
+ // Zend_View_Helper_Navigation_Helper:
+
+ /**
+ * Renders menu
+ *
+ * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
+ *
+ * If a partial view is registered in the helper, the menu will be rendered
+ * using the given partial script. If no partial is registered, the menu
+ * will be rendered as an 'ul' element by the helper's internal method.
+ *
+ * @see renderPartial()
+ * @see renderMenu()
+ *
+ * @param Zend_Navigation_Container $container [optional] container to
+ * render. Default is to
+ * render the container
+ * registered in the helper.
+ * @return string helper output
+ */
+ public function render(Zend_Navigation_Container $container = null)
+ {
+ if ($partial = $this->getPartial()) {
+ return $this->renderPartial($container, $partial);
+ } else {
+ return $this->renderMenu($container);
+ }
+ }
+}