Viewing file: Links.php (25.41 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?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-2009 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id: Links.php 16971 2009-07-22 18:05:45Z mikaelkael $ */
/** * @see Zend_View_Helper_Navigation_HelperAbstract */ require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
/** * Helper for printing <link> elements * * @category Zend * @package Zend_View * @subpackage Helper * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_View_Helper_Navigation_Links extends Zend_View_Helper_Navigation_HelperAbstract { /**#@+ * Constants used for specifying which link types to find and render * * @var int */ const RENDER_ALTERNATE = 0x0001; const RENDER_STYLESHEET = 0x0002; const RENDER_START = 0x0004; const RENDER_NEXT = 0x0008; const RENDER_PREV = 0x0010; const RENDER_CONTENTS = 0x0020; const RENDER_INDEX = 0x0040; const RENDER_GLOSSARY = 0x0080; const RENDER_COPYRIGHT = 0x0100; const RENDER_CHAPTER = 0x0200; const RENDER_SECTION = 0x0400; const RENDER_SUBSECTION = 0x0800; const RENDER_APPENDIX = 0x1000; const RENDER_HELP = 0x2000; const RENDER_BOOKMARK = 0x4000; const RENDER_CUSTOM = 0x8000; const RENDER_ALL = 0xffff; /**#@+**/
/** * Maps render constants to W3C link types * * @var array */ protected static $_RELATIONS = array( self::RENDER_ALTERNATE => 'alternate', self::RENDER_STYLESHEET => 'stylesheet', self::RENDER_START => 'start', self::RENDER_NEXT => 'next', self::RENDER_PREV => 'prev', self::RENDER_CONTENTS => 'contents', self::RENDER_INDEX => 'index', self::RENDER_GLOSSARY => 'glossary', self::RENDER_COPYRIGHT => 'copyright', self::RENDER_CHAPTER => 'chapter', self::RENDER_SECTION => 'section', self::RENDER_SUBSECTION => 'subsection', self::RENDER_APPENDIX => 'appendix', self::RENDER_HELP => 'help', self::RENDER_BOOKMARK => 'bookmark' );
/** * The helper's render flag * * @see render() * @see setRenderFlag() * @var int */ protected $_renderFlag = self::RENDER_ALL;
/** * Root container * * Used for preventing methods to traverse above the container given to * the {@link render()} method. * * @see _findRoot() * * @var Zend_Navigation_Container */ protected $_root;
/** * 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_Links fluent interface, returns * self */ public function links(Zend_Navigation_Container $container = null) { if (null !== $container) { $this->setContainer($container); }
return $this; }
/** * Magic overload: Proxy calls to {@link findRelation()} or container * * Examples of finder calls: * <code> * // METHOD // SAME AS * $h->findRelNext($page); // $h->findRelation($page, 'rel', 'next') * $h->findRevSection($page); // $h->findRelation($page, 'rev', 'section'); * $h->findRelFoo($page); // $h->findRelation($page, 'rel', 'foo'); * </code> * * @param string $method method name * @param array $arguments method arguments * @throws Zend_Navigation_Exception if method does not exist in container */ public function __call($method, array $arguments = array()) { if (@preg_match('/find(Rel|Rev)(.+)/', $method, $match)) { return $this->findRelation($arguments[0], strtolower($match[1]), strtolower($match[2])); }
return parent::__call($method, $arguments); }
// Accessors:
/** * Sets the helper's render flag * * The helper uses the bitwise '&' operator against the hex values of the * render constants. This means that the flag can is "bitwised" value of * the render constants. Examples: * <code> * // render all links except glossary * $flag = Zend_View_Helper_Navigation_Links:RENDER_ALL ^ * Zend_View_Helper_Navigation_Links:RENDER_GLOSSARY; * $helper->setRenderFlag($flag); * * // render only chapters and sections * $flag = Zend_View_Helper_Navigation_Links:RENDER_CHAPTER | * Zend_View_Helper_Navigation_Links:RENDER_SECTION; * $helper->setRenderFlag($flag); * * // render only relations that are not native W3C relations * $helper->setRenderFlag(Zend_View_Helper_Navigation_Links:RENDER_CUSTOM); * * // render all relations (default) * $helper->setRenderFlag(Zend_View_Helper_Navigation_Links:RENDER_ALL); * </code> * * Note that custom relations can also be rendered directly using the * {@link renderLink()} method. * * @param int $renderFlag render flag * @return Zend_View_Helper_Navigation_Links fluent interface, returns self */ public function setRenderFlag($renderFlag) { $this->_renderFlag = (int) $renderFlag; return $this; }
/** * Returns the helper's render flag * * @return int render flag */ public function getRenderFlag() { return $this->_renderFlag; }
// Finder methods:
/** * Finds all relations (forward and reverse) for the given $page * * The form of the returned array: * <code> * // $page denotes an instance of Zend_Navigation_Page * $returned = array( * 'rel' => array( * 'alternate' => array($page, $page, $page), * 'start' => array($page), * 'next' => array($page), * 'prev' => array($page), * 'canonical' => array($page) * ), * 'rev' => array( * 'section' => array($page) * ) * ); * </code> * * @param Zend_Navigation_Page $page page to find links for * @return array related pages */ public function findAllRelations(Zend_Navigation_Page $page, $flag = null) { if (!is_int($flag)) { $flag = self::RENDER_ALL; }
$result = array('rel' => array(), 'rev' => array()); $native = array_values(self::$_RELATIONS);
foreach (array_keys($result) as $rel) { $meth = 'getDefined' . ucfirst($rel); $types = array_merge($native, array_diff($page->$meth(), $native));
foreach ($types as $type) { if (!$relFlag = array_search($type, self::$_RELATIONS)) { $relFlag = self::RENDER_CUSTOM; } if (!($flag & $relFlag)) { continue; } if ($found = $this->findRelation($page, $rel, $type)) { if (!is_array($found)) { $found = array($found); } $result[$rel][$type] = $found; } } }
return $result; }
/** * Finds relations of the given $rel=$type from $page * * This method will first look for relations in the page instance, then * by searching the root container if nothing was found in the page. * * @param Zend_Navigation_Page $page page to find relations for * @param string $rel relation, "rel" or "rev" * @param string $type link type, e.g. 'start', 'next' * @return Zend_Navigaiton_Page|array|null page(s), or null if not found * @throws Zend_View_Exception if $rel is not "rel" or "rev" */ public function findRelation(Zend_Navigation_Page $page, $rel, $type) { if (!in_array($rel, array('rel', 'rev'))) { require_once 'Zend/View/Exception.php'; throw new Zend_View_Exception(sprintf( 'Invalid argument: $rel must be "rel" or "rev"; "%s" given', $rel)); }
if (!$result = $this->_findFromProperty($page, $rel, $type)) { $result = $this->_findFromSearch($page, $rel, $type); }
return $result; }
/** * Finds relations of given $type for $page by checking if the * relation is specified as a property of $page * * @param Zend_Navigation_Page $page page to find relations for * @param string $rel relation, 'rel' or 'rev' * @param string $type link type, e.g. 'start', 'next' * @return Zend_Navigation_Page|array|null page(s), or null if not found */ protected function _findFromProperty(Zend_Navigation_Page $page, $rel, $type) { $method = 'get' . ucfirst($rel); if ($result = $page->$method($type)) { if ($result = $this->_convertToPages($result)) { if (!is_array($result)) { $result = array($result); }
foreach ($result as $key => $page) { if (!$this->accept($page)) { unset($result[$key]); } }
return count($result) == 1 ? $result[0] : $result; } }
return null; }
/** * Finds relations of given $rel=$type for $page by using the helper to * search for the relation in the root container * * @param Zend_Navigation_Page $page page to find relations for * @param string $rel relation, 'rel' or 'rev' * @param string $type link type, e.g. 'start', 'next', etc * @return array|null array of pages, or null if not found */ protected function _findFromSearch(Zend_Navigation_Page $page, $rel, $type) { $found = null;
$method = 'search' . ucfirst($rel) . ucfirst($type); if (method_exists($this, $method)) { $found = $this->$method($page); }
return $found; }
// Search methods:
/** * Searches the root container for the forward 'start' relation of the given * $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to the first document in a collection of documents. This link type * tells search engines which document is considered by the author to be the * starting point of the collection. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|null page or null */ public function searchRelStart(Zend_Navigation_Page $page) { $found = $this->_findRoot($page); if (!$found instanceof Zend_Navigation_Page) { $found->rewind(); $found = $found->current(); }
if ($found === $page || !$this->accept($found)) { $found = null; }
return $found; }
/** * Searches the root container for the forward 'next' relation of the given * $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to the next document in a linear sequence of documents. User * agents may choose to preload the "next" document, to reduce the perceived * load time. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|null page(s) or null */ public function searchRelNext(Zend_Navigation_Page $page) { $found = null; $break = false; $iterator = new RecursiveIteratorIterator($this->_findRoot($page), RecursiveIteratorIterator::SELF_FIRST); foreach ($iterator as $intermediate) { if ($intermediate === $page) { // current page; break at next accepted page $break = true; continue; }
if ($break && $this->accept($intermediate)) { $found = $intermediate; break; } }
return $found; }
/** * Searches the root container for the forward 'prev' relation of the given * $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to the previous document in an ordered series of documents. Some * user agents also support the synonym "Previous". * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|null page or null */ public function searchRelPrev(Zend_Navigation_Page $page) { $found = null; $prev = null; $iterator = new RecursiveIteratorIterator( $this->_findRoot($page), RecursiveIteratorIterator::SELF_FIRST); foreach ($iterator as $intermediate) { if (!$this->accept($intermediate)) { continue; } if ($intermediate === $page) { $found = $prev; break; }
$prev = $intermediate; }
return $found; }
/** * Searches the root container for forward 'chapter' relations of the given * $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to a document serving as a chapter in a collection of documents. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|array|null page(s) or null */ public function searchRelChapter(Zend_Navigation_Page $page) { $found = array();
// find first level of pages $root = $this->_findRoot($page);
// find start page(s) $start = $this->findRelation($page, 'rel', 'start'); if (!is_array($start)) { $start = array($start); }
foreach ($root as $chapter) { // exclude self and start page from chapters if ($chapter !== $page && !in_array($chapter, $start) && $this->accept($chapter)) { $found[] = $chapter; } }
switch (count($found)) { case 0: return null; case 1: return $found[0]; default: return $found; } }
/** * Searches the root container for forward 'section' relations of the given * $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to a document serving as a section in a collection of documents. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|array|null page(s) or null */ public function searchRelSection(Zend_Navigation_Page $page) { $found = array();
// check if given page has pages and is a chapter page if ($page->hasPages() && $this->_findRoot($page)->hasPage($page)) { foreach ($page as $section) { if ($this->accept($section)) { $found[] = $section; } } }
switch (count($found)) { case 0: return null; case 1: return $found[0]; default: return $found; } }
/** * Searches the root container for forward 'subsection' relations of the * given $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to a document serving as a subsection in a collection of * documents. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|array|null page(s) or null */ public function searchRelSubsection(Zend_Navigation_Page $page) { $found = array();
if ($page->hasPages()) { // given page has child pages, loop chapters foreach ($this->_findRoot($page) as $chapter) { // is page a section? if ($chapter->hasPage($page)) { foreach ($page as $subsection) { if ($this->accept($subsection)) { $found[] = $subsection; } } } } }
switch (count($found)) { case 0: return null; case 1: return $found[0]; default: return $found; } }
/** * Searches the root container for the reverse 'section' relation of the * given $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to a document serving as a section in a collection of documents. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|null page(s) or null */ public function searchRevSection(Zend_Navigation_Page $page) { $found = null;
if ($parent = $page->getParent()) { if ($parent instanceof Zend_Navigation_Page && $this->_findRoot($page)->hasPage($parent)) { $found = $parent; } }
return $found; }
/** * Searches the root container for the reverse 'section' relation of the * given $page * * From {@link http://www.w3.org/TR/html4/types.html#type-links}: * Refers to a document serving as a subsection in a collection of * documents. * * @param Zend_Navigation_Page $page page to find relation for * @return Zend_Navigation_Page|null page(s) or null */ public function searchRevSubsection(Zend_Navigation_Page $page) { $found = null;
if ($parent = $page->getParent()) { if ($parent instanceof Zend_Navigation_Page) { $root = $this->_findRoot($page); foreach ($root as $chapter) { if ($chapter->hasPage($parent)) { $found = $parent; break; } } } }
return $found; }
// Util methods:
/** * Returns the root container of the given page * * When rendering a container, the render method still store the given * container as the root container, and unset it when done rendering. This * makes sure finder methods will not traverse above the container given * to the render method. * * @param Zend_Navigaiton_Page $page page to find root for * @return Zend_Navigation_Container the root container of the given page */ protected function _findRoot(Zend_Navigation_Page $page) { if ($this->_root) { return $this->_root; }
$root = $page;
while ($parent = $page->getParent()) { $root = $parent; if ($parent instanceof Zend_Navigation_Page) { $page = $parent; } else { break; } }
return $root; }
/** * Converts a $mixed value to an array of pages * * @param mixed $mixed mixed value to get page(s) from * @param bool $recursive whether $value should be looped * if it is an array or a config * @return Zend_Navigation_Page|array|null empty if unable to convert */ protected function _convertToPages($mixed, $recursive = true) { if (is_object($mixed)) { if ($mixed instanceof Zend_Navigation_Page) { // value is a page instance; return directly return $mixed; } elseif ($mixed instanceof Zend_Navigation_Container) { // value is a container; return pages in it $pages = array(); foreach ($mixed as $page) { $pages[] = $page; } return $pages; } elseif ($mixed instanceof Zend_Config) { // convert config object to array and extract return $this->_convertToPages($mixed->toArray(), $recursive); } } elseif (is_string($mixed)) { // value is a string; make an URI page return Zend_Navigation_Page::factory(array( 'type' => 'uri', 'uri' => $mixed )); } elseif (is_array($mixed) && !empty($mixed)) { if ($recursive && is_numeric(key($mixed))) { // first key is numeric; assume several pages $pages = array(); foreach ($mixed as $value) { if ($value = $this->_convertToPages($value, false)) { $pages[] = $value; } } return $pages; } else { // pass array to factory directly try { $page = Zend_Navigation_Page::factory($mixed); return $page; } catch (Exception $e) { } } }
// nothing found return null; }
// Render methods:
/** * Renders the given $page as a link element, with $attrib = $relation * * @param Zend_Navigation_Page $page the page to render the link for * @param string $attrib the attribute to use for $type, * either 'rel' or 'rev' * @param string $relation relation type, muse be one of; * alternate, appendix, bookmark, * chapter, contents, copyright, * glossary, help, home, index, next, * prev, section, start, stylesheet, * subsection * @return string rendered link element * @throws Zend_View_Exception if $attrib is invalid */ public function renderLink(Zend_Navigation_Page $page, $attrib, $relation) { if (!in_array($attrib, array('rel', 'rev'))) { require_once 'Zend/View/Exception.php'; throw new Zend_View_Exception(sprintf( 'Invalid relation attribute "%s", must be "rel" or "rev"', $attrib)); }
if (!$href = $page->getHref()) { return ''; }
// TODO: add more attribs // http://www.w3.org/TR/html401/struct/links.html#h-12.2 $attribs = array( $attrib => $relation, 'href' => $href, 'title' => $page->getLabel() );
return '<link' . $this->_htmlAttribs($attribs) . $this->getClosingBracket(); }
// Zend_View_Helper_Navigation_Helper:
/** * Renders helper * * Implements {@link Zend_View_Helper_Navigation_Helper::render()}. * * @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 (null === $container) { $container = $this->getContainer(); }
if ($active = $this->findActive($container)) { $active = $active['page']; } else { // no active page return ''; }
$output = ''; $indent = $this->getIndent(); $this->_root = $container;
$result = $this->findAllRelations($active, $this->getRenderFlag()); foreach ($result as $attrib => $types) { foreach ($types as $relation => $pages) { foreach ($pages as $page) { if ($r = $this->renderLink($page, $attrib, $relation)) { $output .= $indent . $r . self::EOL; } } } }
$this->_root = null;
// return output (trim last newline by spec) return strlen($output) ? rtrim($output, self::EOL) : ''; } }
|