web/enmi/Zend/View/Helper/Navigation/Links.php
changeset 19 1c2f13fd785c
parent 0 4eba9c11703f
equal deleted inserted replaced
18:bd595ad770fc 19:1c2f13fd785c
       
     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: Links.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 /**
       
    24  * @see Zend_View_Helper_Navigation_HelperAbstract
       
    25  */
       
    26 require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
       
    27 
       
    28 /**
       
    29  * Helper for printing <link> elements
       
    30  *
       
    31  * @category   Zend
       
    32  * @package    Zend_View
       
    33  * @subpackage Helper
       
    34  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    35  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    36  */
       
    37 class Zend_View_Helper_Navigation_Links
       
    38     extends Zend_View_Helper_Navigation_HelperAbstract
       
    39 {
       
    40     /**#@+
       
    41      * Constants used for specifying which link types to find and render
       
    42      *
       
    43      * @var int
       
    44      */
       
    45     const RENDER_ALTERNATE  = 0x0001;
       
    46     const RENDER_STYLESHEET = 0x0002;
       
    47     const RENDER_START      = 0x0004;
       
    48     const RENDER_NEXT       = 0x0008;
       
    49     const RENDER_PREV       = 0x0010;
       
    50     const RENDER_CONTENTS   = 0x0020;
       
    51     const RENDER_INDEX      = 0x0040;
       
    52     const RENDER_GLOSSARY   = 0x0080;
       
    53     const RENDER_COPYRIGHT  = 0x0100;
       
    54     const RENDER_CHAPTER    = 0x0200;
       
    55     const RENDER_SECTION    = 0x0400;
       
    56     const RENDER_SUBSECTION = 0x0800;
       
    57     const RENDER_APPENDIX   = 0x1000;
       
    58     const RENDER_HELP       = 0x2000;
       
    59     const RENDER_BOOKMARK   = 0x4000;
       
    60     const RENDER_CUSTOM     = 0x8000;
       
    61     const RENDER_ALL        = 0xffff;
       
    62     /**#@+**/
       
    63 
       
    64     /**
       
    65      * Maps render constants to W3C link types
       
    66      *
       
    67      * @var array
       
    68      */
       
    69     protected static $_RELATIONS = array(
       
    70         self::RENDER_ALTERNATE  => 'alternate',
       
    71         self::RENDER_STYLESHEET => 'stylesheet',
       
    72         self::RENDER_START      => 'start',
       
    73         self::RENDER_NEXT       => 'next',
       
    74         self::RENDER_PREV       => 'prev',
       
    75         self::RENDER_CONTENTS   => 'contents',
       
    76         self::RENDER_INDEX      => 'index',
       
    77         self::RENDER_GLOSSARY   => 'glossary',
       
    78         self::RENDER_COPYRIGHT  => 'copyright',
       
    79         self::RENDER_CHAPTER    => 'chapter',
       
    80         self::RENDER_SECTION    => 'section',
       
    81         self::RENDER_SUBSECTION => 'subsection',
       
    82         self::RENDER_APPENDIX   => 'appendix',
       
    83         self::RENDER_HELP       => 'help',
       
    84         self::RENDER_BOOKMARK   => 'bookmark'
       
    85     );
       
    86 
       
    87     /**
       
    88      * The helper's render flag
       
    89      *
       
    90      * @see render()
       
    91      * @see setRenderFlag()
       
    92      * @var int
       
    93      */
       
    94     protected $_renderFlag = self::RENDER_ALL;
       
    95 
       
    96     /**
       
    97      * Root container
       
    98      *
       
    99      * Used for preventing methods to traverse above the container given to
       
   100      * the {@link render()} method.
       
   101      *
       
   102      * @see _findRoot()
       
   103      *
       
   104      * @var Zend_Navigation_Container
       
   105      */
       
   106     protected $_root;
       
   107 
       
   108     /**
       
   109      * View helper entry point:
       
   110      * Retrieves helper and optionally sets container to operate on
       
   111      *
       
   112      * @param  Zend_Navigation_Container $container  [optional] container to
       
   113      *                                               operate on
       
   114      * @return Zend_View_Helper_Navigation_Links     fluent interface, returns
       
   115      *                                               self
       
   116      */
       
   117     public function links(Zend_Navigation_Container $container = null)
       
   118     {
       
   119         if (null !== $container) {
       
   120             $this->setContainer($container);
       
   121         }
       
   122 
       
   123         return $this;
       
   124     }
       
   125 
       
   126     /**
       
   127      * Magic overload: Proxy calls to {@link findRelation()} or container
       
   128      *
       
   129      * Examples of finder calls:
       
   130      * <code>
       
   131      * // METHOD                  // SAME AS
       
   132      * $h->findRelNext($page);    // $h->findRelation($page, 'rel', 'next')
       
   133      * $h->findRevSection($page); // $h->findRelation($page, 'rev', 'section');
       
   134      * $h->findRelFoo($page);     // $h->findRelation($page, 'rel', 'foo');
       
   135      * </code>
       
   136      *
       
   137      * @param  string $method             method name
       
   138      * @param  array  $arguments          method arguments
       
   139      * @throws Zend_Navigation_Exception  if method does not exist in container
       
   140      */
       
   141     public function __call($method, array $arguments = array())
       
   142     {
       
   143         if (@preg_match('/find(Rel|Rev)(.+)/', $method, $match)) {
       
   144             return $this->findRelation($arguments[0],
       
   145                                        strtolower($match[1]),
       
   146                                        strtolower($match[2]));
       
   147         }
       
   148 
       
   149         return parent::__call($method, $arguments);
       
   150     }
       
   151 
       
   152     // Accessors:
       
   153 
       
   154     /**
       
   155      * Sets the helper's render flag
       
   156      *
       
   157      * The helper uses the bitwise '&' operator against the hex values of the
       
   158      * render constants. This means that the flag can is "bitwised" value of
       
   159      * the render constants. Examples:
       
   160      * <code>
       
   161      * // render all links except glossary
       
   162      * $flag = Zend_View_Helper_Navigation_Links:RENDER_ALL ^
       
   163      *         Zend_View_Helper_Navigation_Links:RENDER_GLOSSARY;
       
   164      * $helper->setRenderFlag($flag);
       
   165      *
       
   166      * // render only chapters and sections
       
   167      * $flag = Zend_View_Helper_Navigation_Links:RENDER_CHAPTER |
       
   168      *         Zend_View_Helper_Navigation_Links:RENDER_SECTION;
       
   169      * $helper->setRenderFlag($flag);
       
   170      *
       
   171      * // render only relations that are not native W3C relations
       
   172      * $helper->setRenderFlag(Zend_View_Helper_Navigation_Links:RENDER_CUSTOM);
       
   173      *
       
   174      * // render all relations (default)
       
   175      * $helper->setRenderFlag(Zend_View_Helper_Navigation_Links:RENDER_ALL);
       
   176      * </code>
       
   177      *
       
   178      * Note that custom relations can also be rendered directly using the
       
   179      * {@link renderLink()} method.
       
   180      *
       
   181      * @param  int $renderFlag                    render flag
       
   182      * @return Zend_View_Helper_Navigation_Links  fluent interface, returns self
       
   183      */
       
   184     public function setRenderFlag($renderFlag)
       
   185     {
       
   186         $this->_renderFlag = (int) $renderFlag;
       
   187         return $this;
       
   188     }
       
   189 
       
   190     /**
       
   191      * Returns the helper's render flag
       
   192      *
       
   193      * @return int  render flag
       
   194      */
       
   195     public function getRenderFlag()
       
   196     {
       
   197         return $this->_renderFlag;
       
   198     }
       
   199 
       
   200     // Finder methods:
       
   201 
       
   202     /**
       
   203      * Finds all relations (forward and reverse) for the given $page
       
   204      *
       
   205      * The form of the returned array:
       
   206      * <code>
       
   207      * // $page denotes an instance of Zend_Navigation_Page
       
   208      * $returned = array(
       
   209      *     'rel' => array(
       
   210      *         'alternate' => array($page, $page, $page),
       
   211      *         'start'     => array($page),
       
   212      *         'next'      => array($page),
       
   213      *         'prev'      => array($page),
       
   214      *         'canonical' => array($page)
       
   215      *     ),
       
   216      *     'rev' => array(
       
   217      *         'section'   => array($page)
       
   218      *     )
       
   219      * );
       
   220      * </code>
       
   221      *
       
   222      * @param  Zend_Navigation_Page $page  page to find links for
       
   223      * @return array                       related pages
       
   224      */
       
   225     public function findAllRelations(Zend_Navigation_Page $page,
       
   226                                      $flag = null)
       
   227     {
       
   228         if (!is_int($flag)) {
       
   229             $flag = self::RENDER_ALL;
       
   230         }
       
   231 
       
   232         $result = array('rel' => array(), 'rev' => array());
       
   233         $native = array_values(self::$_RELATIONS);
       
   234 
       
   235         foreach (array_keys($result) as $rel) {
       
   236             $meth = 'getDefined' . ucfirst($rel);
       
   237             $types = array_merge($native, array_diff($page->$meth(), $native));
       
   238 
       
   239             foreach ($types as $type) {
       
   240                 if (!$relFlag = array_search($type, self::$_RELATIONS)) {
       
   241                     $relFlag = self::RENDER_CUSTOM;
       
   242                 }
       
   243                 if (!($flag & $relFlag)) {
       
   244                     continue;
       
   245                 }
       
   246                 if ($found = $this->findRelation($page, $rel, $type)) {
       
   247                     if (!is_array($found)) {
       
   248                         $found = array($found);
       
   249                     }
       
   250                     $result[$rel][$type] = $found;
       
   251                 }
       
   252             }
       
   253         }
       
   254 
       
   255         return $result;
       
   256     }
       
   257 
       
   258     /**
       
   259      * Finds relations of the given $rel=$type from $page
       
   260      *
       
   261      * This method will first look for relations in the page instance, then
       
   262      * by searching the root container if nothing was found in the page.
       
   263      *
       
   264      * @param  Zend_Navigation_Page $page       page to find relations for
       
   265      * @param  string              $rel         relation, "rel" or "rev"
       
   266      * @param  string              $type        link type, e.g. 'start', 'next'
       
   267      * @return Zend_Navigaiton_Page|array|null  page(s), or null if not found
       
   268      * @throws Zend_View_Exception              if $rel is not "rel" or "rev"
       
   269      */
       
   270     public function findRelation(Zend_Navigation_Page $page, $rel, $type)
       
   271     {
       
   272         if (!in_array($rel, array('rel', 'rev'))) {
       
   273             require_once 'Zend/View/Exception.php';
       
   274             $e = new Zend_View_Exception(sprintf(
       
   275                 'Invalid argument: $rel must be "rel" or "rev"; "%s" given',
       
   276                 $rel));
       
   277             $e->setView($this->view);
       
   278             throw $e;
       
   279         }
       
   280 
       
   281         if (!$result = $this->_findFromProperty($page, $rel, $type)) {
       
   282             $result = $this->_findFromSearch($page, $rel, $type);
       
   283         }
       
   284 
       
   285         return $result;
       
   286     }
       
   287 
       
   288     /**
       
   289      * Finds relations of given $type for $page by checking if the
       
   290      * relation is specified as a property of $page
       
   291      *
       
   292      * @param  Zend_Navigation_Page $page       page to find relations for
       
   293      * @param  string              $rel         relation, 'rel' or 'rev'
       
   294      * @param  string              $type        link type, e.g. 'start', 'next'
       
   295      * @return Zend_Navigation_Page|array|null  page(s), or null if not found
       
   296      */
       
   297     protected function _findFromProperty(Zend_Navigation_Page $page, $rel, $type)
       
   298     {
       
   299         $method = 'get' . ucfirst($rel);
       
   300         if ($result = $page->$method($type)) {
       
   301             if ($result = $this->_convertToPages($result)) {
       
   302                 if (!is_array($result)) {
       
   303                     $result = array($result);
       
   304                 }
       
   305 
       
   306                 foreach ($result as $key => $page) {
       
   307                     if (!$this->accept($page)) {
       
   308                         unset($result[$key]);
       
   309                     }
       
   310                 }
       
   311 
       
   312                 return count($result) == 1 ? $result[0] : $result;
       
   313             }
       
   314         }
       
   315 
       
   316         return null;
       
   317     }
       
   318 
       
   319     /**
       
   320      * Finds relations of given $rel=$type for $page by using the helper to
       
   321      * search for the relation in the root container
       
   322      *
       
   323      * @param  Zend_Navigation_Page $page  page to find relations for
       
   324      * @param  string              $rel    relation, 'rel' or 'rev'
       
   325      * @param  string              $type   link type, e.g. 'start', 'next', etc
       
   326      * @return array|null                  array of pages, or null if not found
       
   327      */
       
   328     protected function _findFromSearch(Zend_Navigation_Page $page, $rel, $type)
       
   329     {
       
   330         $found = null;
       
   331 
       
   332         $method = 'search' . ucfirst($rel) . ucfirst($type);
       
   333         if (method_exists($this, $method)) {
       
   334             $found = $this->$method($page);
       
   335         }
       
   336 
       
   337         return $found;
       
   338     }
       
   339 
       
   340     // Search methods:
       
   341 
       
   342     /**
       
   343      * Searches the root container for the forward 'start' relation of the given
       
   344      * $page
       
   345      *
       
   346      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   347      * Refers to the first document in a collection of documents. This link type
       
   348      * tells search engines which document is considered by the author to be the
       
   349      * starting point of the collection.
       
   350      *
       
   351      * @param  Zend_Navigation_Page $page  page to find relation for
       
   352      * @return Zend_Navigation_Page|null   page or null
       
   353      */
       
   354     public function searchRelStart(Zend_Navigation_Page $page)
       
   355     {
       
   356         $found = $this->_findRoot($page);
       
   357         if (!$found instanceof Zend_Navigation_Page) {
       
   358             $found->rewind();
       
   359             $found = $found->current();
       
   360         }
       
   361 
       
   362         if ($found === $page || !$this->accept($found)) {
       
   363             $found = null;
       
   364         }
       
   365 
       
   366         return $found;
       
   367     }
       
   368 
       
   369     /**
       
   370      * Searches the root container for the forward 'next' relation of the given
       
   371      * $page
       
   372      *
       
   373      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   374      * Refers to the next document in a linear sequence of documents. User
       
   375      * agents may choose to preload the "next" document, to reduce the perceived
       
   376      * load time.
       
   377      *
       
   378      * @param  Zend_Navigation_Page $page  page to find relation for
       
   379      * @return Zend_Navigation_Page|null   page(s) or null
       
   380      */
       
   381     public function searchRelNext(Zend_Navigation_Page $page)
       
   382     {
       
   383         $found = null;
       
   384         $break = false;
       
   385         $iterator = new RecursiveIteratorIterator($this->_findRoot($page),
       
   386                 RecursiveIteratorIterator::SELF_FIRST);
       
   387         foreach ($iterator as $intermediate) {
       
   388             if ($intermediate === $page) {
       
   389                 // current page; break at next accepted page
       
   390                 $break = true;
       
   391                 continue;
       
   392             }
       
   393 
       
   394             if ($break && $this->accept($intermediate)) {
       
   395                 $found = $intermediate;
       
   396                 break;
       
   397             }
       
   398         }
       
   399 
       
   400         return $found;
       
   401     }
       
   402 
       
   403     /**
       
   404      * Searches the root container for the forward 'prev' relation of the given
       
   405      * $page
       
   406      *
       
   407      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   408      * Refers to the previous document in an ordered series of documents. Some
       
   409      * user agents also support the synonym "Previous".
       
   410      *
       
   411      * @param  Zend_Navigation_Page $page  page to find relation for
       
   412      * @return Zend_Navigation_Page|null   page or null
       
   413      */
       
   414     public function searchRelPrev(Zend_Navigation_Page $page)
       
   415     {
       
   416         $found = null;
       
   417         $prev = null;
       
   418         $iterator = new RecursiveIteratorIterator(
       
   419                 $this->_findRoot($page),
       
   420                 RecursiveIteratorIterator::SELF_FIRST);
       
   421         foreach ($iterator as $intermediate) {
       
   422             if (!$this->accept($intermediate)) {
       
   423                 continue;
       
   424             }
       
   425             if ($intermediate === $page) {
       
   426                 $found = $prev;
       
   427                 break;
       
   428             }
       
   429 
       
   430             $prev = $intermediate;
       
   431         }
       
   432 
       
   433         return $found;
       
   434     }
       
   435 
       
   436     /**
       
   437      * Searches the root container for forward 'chapter' relations of the given
       
   438      * $page
       
   439      *
       
   440      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   441      * Refers to a document serving as a chapter in a collection of documents.
       
   442      *
       
   443      * @param  Zend_Navigation_Page $page       page to find relation for
       
   444      * @return Zend_Navigation_Page|array|null  page(s) or null
       
   445      */
       
   446     public function searchRelChapter(Zend_Navigation_Page $page)
       
   447     {
       
   448         $found = array();
       
   449 
       
   450         // find first level of pages
       
   451         $root = $this->_findRoot($page);
       
   452 
       
   453         // find start page(s)
       
   454         $start = $this->findRelation($page, 'rel', 'start');
       
   455         if (!is_array($start)) {
       
   456             $start = array($start);
       
   457         }
       
   458 
       
   459         foreach ($root as $chapter) {
       
   460             // exclude self and start page from chapters
       
   461             if ($chapter !== $page &&
       
   462                 !in_array($chapter, $start) &&
       
   463                 $this->accept($chapter)) {
       
   464                 $found[] = $chapter;
       
   465             }
       
   466         }
       
   467 
       
   468         switch (count($found)) {
       
   469             case 0:
       
   470                 return null;
       
   471             case 1:
       
   472                 return $found[0];
       
   473             default:
       
   474                 return $found;
       
   475         }
       
   476     }
       
   477 
       
   478     /**
       
   479      * Searches the root container for forward 'section' relations of the given
       
   480      * $page
       
   481      *
       
   482      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   483      * Refers to a document serving as a section in a collection of documents.
       
   484      *
       
   485      * @param  Zend_Navigation_Page $page       page to find relation for
       
   486      * @return Zend_Navigation_Page|array|null  page(s) or null
       
   487      */
       
   488     public function searchRelSection(Zend_Navigation_Page $page)
       
   489     {
       
   490         $found = array();
       
   491 
       
   492         // check if given page has pages and is a chapter page
       
   493         if ($page->hasPages() && $this->_findRoot($page)->hasPage($page)) {
       
   494             foreach ($page as $section) {
       
   495                 if ($this->accept($section)) {
       
   496                     $found[] = $section;
       
   497                 }
       
   498             }
       
   499         }
       
   500 
       
   501         switch (count($found)) {
       
   502             case 0:
       
   503                 return null;
       
   504             case 1:
       
   505                 return $found[0];
       
   506             default:
       
   507                 return $found;
       
   508         }
       
   509     }
       
   510 
       
   511     /**
       
   512      * Searches the root container for forward 'subsection' relations of the
       
   513      * given $page
       
   514      *
       
   515      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   516      * Refers to a document serving as a subsection in a collection of
       
   517      * documents.
       
   518      *
       
   519      * @param  Zend_Navigation_Page $page       page to find relation for
       
   520      * @return Zend_Navigation_Page|array|null  page(s) or null
       
   521      */
       
   522     public function searchRelSubsection(Zend_Navigation_Page $page)
       
   523     {
       
   524         $found = array();
       
   525 
       
   526         if ($page->hasPages()) {
       
   527             // given page has child pages, loop chapters
       
   528             foreach ($this->_findRoot($page) as $chapter) {
       
   529                 // is page a section?
       
   530                 if ($chapter->hasPage($page)) {
       
   531                     foreach ($page as $subsection) {
       
   532                         if ($this->accept($subsection)) {
       
   533                             $found[] = $subsection;
       
   534                         }
       
   535                     }
       
   536                 }
       
   537             }
       
   538         }
       
   539 
       
   540         switch (count($found)) {
       
   541             case 0:
       
   542                 return null;
       
   543             case 1:
       
   544                 return $found[0];
       
   545             default:
       
   546                 return $found;
       
   547         }
       
   548     }
       
   549 
       
   550     /**
       
   551      * Searches the root container for the reverse 'section' relation of the
       
   552      * given $page
       
   553      *
       
   554      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   555      * Refers to a document serving as a section in a collection of documents.
       
   556      *
       
   557      * @param  Zend_Navigation_Page $page  page to find relation for
       
   558      * @return Zend_Navigation_Page|null   page(s) or null
       
   559      */
       
   560     public function searchRevSection(Zend_Navigation_Page $page)
       
   561     {
       
   562         $found = null;
       
   563 
       
   564         if ($parent = $page->getParent()) {
       
   565             if ($parent instanceof Zend_Navigation_Page &&
       
   566                 $this->_findRoot($page)->hasPage($parent)) {
       
   567                 $found = $parent;
       
   568             }
       
   569         }
       
   570 
       
   571         return $found;
       
   572     }
       
   573 
       
   574     /**
       
   575      * Searches the root container for the reverse 'section' relation of the
       
   576      * given $page
       
   577      *
       
   578      * From {@link http://www.w3.org/TR/html4/types.html#type-links}:
       
   579      * Refers to a document serving as a subsection in a collection of
       
   580      * documents.
       
   581      *
       
   582      * @param  Zend_Navigation_Page $page  page to find relation for
       
   583      * @return Zend_Navigation_Page|null   page(s) or null
       
   584      */
       
   585     public function searchRevSubsection(Zend_Navigation_Page $page)
       
   586     {
       
   587         $found = null;
       
   588 
       
   589         if ($parent = $page->getParent()) {
       
   590             if ($parent instanceof Zend_Navigation_Page) {
       
   591                 $root = $this->_findRoot($page);
       
   592                 foreach ($root as $chapter) {
       
   593                     if ($chapter->hasPage($parent)) {
       
   594                         $found = $parent;
       
   595                         break;
       
   596                     }
       
   597                 }
       
   598             }
       
   599         }
       
   600 
       
   601         return $found;
       
   602     }
       
   603 
       
   604     // Util methods:
       
   605 
       
   606     /**
       
   607      * Returns the root container of the given page
       
   608      *
       
   609      * When rendering a container, the render method still store the given
       
   610      * container as the root container, and unset it when done rendering. This
       
   611      * makes sure finder methods will not traverse above the container given
       
   612      * to the render method.
       
   613      *
       
   614      * @param  Zend_Navigaiton_Page $page  page to find root for
       
   615      * @return Zend_Navigation_Container   the root container of the given page
       
   616      */
       
   617     protected function _findRoot(Zend_Navigation_Page $page)
       
   618     {
       
   619         if ($this->_root) {
       
   620             return $this->_root;
       
   621         }
       
   622 
       
   623         $root = $page;
       
   624 
       
   625         while ($parent = $page->getParent()) {
       
   626             $root = $parent;
       
   627             if ($parent instanceof Zend_Navigation_Page) {
       
   628                 $page = $parent;
       
   629             } else {
       
   630                 break;
       
   631             }
       
   632         }
       
   633 
       
   634         return $root;
       
   635     }
       
   636 
       
   637     /**
       
   638      * Converts a $mixed value to an array of pages
       
   639      *
       
   640      * @param  mixed $mixed                     mixed value to get page(s) from
       
   641      * @param  bool  $recursive                 whether $value should be looped
       
   642      *                                          if it is an array or a config
       
   643      * @return Zend_Navigation_Page|array|null  empty if unable to convert
       
   644      */
       
   645     protected function _convertToPages($mixed, $recursive = true)
       
   646     {
       
   647         if (is_object($mixed)) {
       
   648             if ($mixed instanceof Zend_Navigation_Page) {
       
   649                 // value is a page instance; return directly
       
   650                 return $mixed;
       
   651             } elseif ($mixed instanceof Zend_Navigation_Container) {
       
   652                 // value is a container; return pages in it
       
   653                 $pages = array();
       
   654                 foreach ($mixed as $page) {
       
   655                     $pages[] = $page;
       
   656                 }
       
   657                 return $pages;
       
   658             } elseif ($mixed instanceof Zend_Config) {
       
   659                 // convert config object to array and extract
       
   660                 return $this->_convertToPages($mixed->toArray(), $recursive);
       
   661             }
       
   662         } elseif (is_string($mixed)) {
       
   663             // value is a string; make an URI page
       
   664             return Zend_Navigation_Page::factory(array(
       
   665                 'type' => 'uri',
       
   666                 'uri'  => $mixed
       
   667             ));
       
   668         } elseif (is_array($mixed) && !empty($mixed)) {
       
   669             if ($recursive && is_numeric(key($mixed))) {
       
   670                 // first key is numeric; assume several pages
       
   671                 $pages = array();
       
   672                 foreach ($mixed as $value) {
       
   673                     if ($value = $this->_convertToPages($value, false)) {
       
   674                         $pages[] = $value;
       
   675                     }
       
   676                 }
       
   677                 return $pages;
       
   678             } else {
       
   679                 // pass array to factory directly
       
   680                 try {
       
   681                     $page = Zend_Navigation_Page::factory($mixed);
       
   682                     return $page;
       
   683                 } catch (Exception $e) {
       
   684                 }
       
   685             }
       
   686         }
       
   687 
       
   688         // nothing found
       
   689         return null;
       
   690     }
       
   691 
       
   692     // Render methods:
       
   693 
       
   694     /**
       
   695      * Renders the given $page as a link element, with $attrib = $relation
       
   696      *
       
   697      * @param  Zend_Navigation_Page $page      the page to render the link for
       
   698      * @param  string               $attrib    the attribute to use for $type,
       
   699      *                                         either 'rel' or 'rev'
       
   700      * @param  string               $relation  relation type, muse be one of;
       
   701      *                                         alternate, appendix, bookmark,
       
   702      *                                         chapter, contents, copyright,
       
   703      *                                         glossary, help, home, index, next,
       
   704      *                                         prev, section, start, stylesheet,
       
   705      *                                         subsection
       
   706      * @return string                          rendered link element
       
   707      * @throws Zend_View_Exception             if $attrib is invalid
       
   708      */
       
   709     public function renderLink(Zend_Navigation_Page $page, $attrib, $relation)
       
   710     {
       
   711         if (!in_array($attrib, array('rel', 'rev'))) {
       
   712             require_once 'Zend/View/Exception.php';
       
   713             $e = new Zend_View_Exception(sprintf(
       
   714                     'Invalid relation attribute "%s", must be "rel" or "rev"',
       
   715                     $attrib));
       
   716             $e->setView($this->view);
       
   717             throw $e;
       
   718         }
       
   719 
       
   720         if (!$href = $page->getHref()) {
       
   721             return '';
       
   722         }
       
   723 
       
   724         // TODO: add more attribs
       
   725         // http://www.w3.org/TR/html401/struct/links.html#h-12.2
       
   726         $attribs = array(
       
   727             $attrib  => $relation,
       
   728             'href'   => $href,
       
   729             'title'  => $page->getLabel()
       
   730         );
       
   731 
       
   732         return '<link' .
       
   733                $this->_htmlAttribs($attribs) .
       
   734                $this->getClosingBracket();
       
   735     }
       
   736 
       
   737     // Zend_View_Helper_Navigation_Helper:
       
   738 
       
   739     /**
       
   740      * Renders helper
       
   741      *
       
   742      * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
       
   743      *
       
   744      * @param  Zend_Navigation_Container $container  [optional] container to
       
   745      *                                               render. Default is to
       
   746      *                                               render the container
       
   747      *                                               registered in the helper.
       
   748      * @return string                                helper output
       
   749      */
       
   750     public function render(Zend_Navigation_Container $container = null)
       
   751     {
       
   752         if (null === $container) {
       
   753             $container = $this->getContainer();
       
   754         }
       
   755 
       
   756         if ($active = $this->findActive($container)) {
       
   757             $active = $active['page'];
       
   758         } else {
       
   759             // no active page
       
   760             return '';
       
   761         }
       
   762 
       
   763         $output = '';
       
   764         $indent = $this->getIndent();
       
   765         $this->_root = $container;
       
   766 
       
   767         $result = $this->findAllRelations($active, $this->getRenderFlag());
       
   768         foreach ($result as $attrib => $types) {
       
   769             foreach ($types as $relation => $pages) {
       
   770                 foreach ($pages as $page) {
       
   771                     if ($r = $this->renderLink($page, $attrib, $relation)) {
       
   772                         $output .= $indent . $r . self::EOL;
       
   773                     }
       
   774                 }
       
   775             }
       
   776         }
       
   777 
       
   778         $this->_root = null;
       
   779 
       
   780         // return output (trim last newline by spec)
       
   781         return strlen($output) ? rtrim($output, self::EOL) : '';
       
   782     }
       
   783 }