web/lib/Zend/View/Helper/Navigation/Sitemap.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/Zend/View/Helper/Navigation/Sitemap.php	Fri Mar 11 15:05:35 2011 +0100
@@ -0,0 +1,483 @@
+<?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: Sitemap.php 20104 2010-01-06 21:26:01Z matthew $
+ */
+
+/**
+ * @see Zend_View_Helper_Navigation_HelperAbstract
+ */
+require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
+
+/**
+ * Helper for printing sitemaps
+ *
+ * @link http://www.sitemaps.org/protocol.php
+ *
+ * @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_Sitemap
+    extends Zend_View_Helper_Navigation_HelperAbstract
+{
+    /**
+     * Namespace for the <urlset> tag
+     *
+     * @var string
+     */
+    const SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
+
+    /**
+     * Schema URL
+     *
+     * @var string
+     */
+    const SITEMAP_XSD = 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd';
+
+    /**
+     * Whether XML output should be formatted
+     *
+     * @var bool
+     */
+    protected $_formatOutput = false;
+
+    /**
+     * Whether the XML declaration should be included in XML output
+     *
+     * @var bool
+     */
+    protected $_useXmlDeclaration = true;
+
+    /**
+     * Whether sitemap should be validated using Zend_Validate_Sitemap_*
+     *
+     * @var bool
+     */
+    protected $_useSitemapValidators = true;
+
+    /**
+     * Whether sitemap should be schema validated when generated
+     *
+     * @var bool
+     */
+    protected $_useSchemaValidation = false;
+
+    /**
+     * Server url
+     *
+     * @var string
+     */
+    protected $_serverUrl;
+
+    /**
+     * 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_Sitemap   fluent interface, returns
+     *                                               self
+     */
+    public function sitemap(Zend_Navigation_Container $container = null)
+    {
+        if (null !== $container) {
+            $this->setContainer($container);
+        }
+
+        return $this;
+    }
+
+    // Accessors:
+
+    /**
+     * Sets whether XML output should be formatted
+     *
+     * @param  bool $formatOutput                   [optional] whether output
+     *                                              should be formatted. Default
+     *                                              is true.
+     * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
+     *                                              self
+     */
+    public function setFormatOutput($formatOutput = true)
+    {
+        $this->_formatOutput = (bool) $formatOutput;
+        return $this;
+    }
+
+    /**
+     * Returns whether XML output should be formatted
+     *
+     * @return bool  whether XML output should be formatted
+     */
+    public function getFormatOutput()
+    {
+        return $this->_formatOutput;
+    }
+
+    /**
+     * Sets whether the XML declaration should be used in output
+     *
+     * @param  bool $useXmlDecl                     whether XML delcaration
+     *                                              should be rendered
+     * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
+     *                                              self
+     */
+    public function setUseXmlDeclaration($useXmlDecl)
+    {
+        $this->_useXmlDeclaration = (bool) $useXmlDecl;
+        return $this;
+    }
+
+    /**
+     * Returns whether the XML declaration should be used in output
+     *
+     * @return bool  whether the XML declaration should be used in output
+     */
+    public function getUseXmlDeclaration()
+    {
+        return $this->_useXmlDeclaration;
+    }
+
+    /**
+     * Sets whether sitemap should be validated using Zend_Validate_Sitemap_*
+     *
+     * @param  bool $useSitemapValidators           whether sitemap validators
+     *                                              should be used
+     * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
+     *                                              self
+     */
+    public function setUseSitemapValidators($useSitemapValidators)
+    {
+        $this->_useSitemapValidators = (bool) $useSitemapValidators;
+        return $this;
+    }
+
+    /**
+     * Returns whether sitemap should be validated using Zend_Validate_Sitemap_*
+     *
+     * @return bool  whether sitemap should be validated using validators
+     */
+    public function getUseSitemapValidators()
+    {
+        return $this->_useSitemapValidators;
+    }
+
+    /**
+     * Sets whether sitemap should be schema validated when generated
+     *
+     * @param  bool $schemaValidation               whether sitemap should
+     *                                              validated using XSD Schema
+     * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
+     *                                              self
+     */
+    public function setUseSchemaValidation($schemaValidation)
+    {
+        $this->_useSchemaValidation = (bool) $schemaValidation;
+        return $this;
+    }
+
+    /**
+     * Returns true if sitemap should be schema validated when generated
+     *
+     * @return bool
+     */
+    public function getUseSchemaValidation()
+    {
+        return $this->_useSchemaValidation;
+    }
+
+    /**
+     * Sets server url (scheme and host-related stuff without request URI)
+     *
+     * E.g. http://www.example.com
+     *
+     * @param  string $serverUrl                    server URL to set (only
+     *                                              scheme and host)
+     * @throws Zend_Uri_Exception                   if invalid server URL
+     * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
+     *                                              self
+     */
+    public function setServerUrl($serverUrl)
+    {
+        require_once 'Zend/Uri.php';
+        $uri = Zend_Uri::factory($serverUrl);
+        $uri->setFragment('');
+        $uri->setPath('');
+        $uri->setQuery('');
+
+        if ($uri->valid()) {
+            $this->_serverUrl = $uri->getUri();
+        } else {
+            require_once 'Zend/Uri/Exception.php';
+            $e = new Zend_Uri_Exception(sprintf(
+                    'Invalid server URL: "%s"',
+                    $serverUrl));
+            $e->setView($this->view);
+            throw $e;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns server URL
+     *
+     * @return string  server URL
+     */
+    public function getServerUrl()
+    {
+        if (!isset($this->_serverUrl)) {
+            $this->_serverUrl = $this->view->serverUrl();
+        }
+
+        return $this->_serverUrl;
+    }
+
+    // Helper methods:
+
+    /**
+     * Escapes string for XML usage
+     *
+     * @param  string $string  string to escape
+     * @return string          escaped string
+     */
+    protected function _xmlEscape($string)
+    {
+        $enc = 'UTF-8';
+        if ($this->view instanceof Zend_View_Interface
+            && method_exists($this->view, 'getEncoding')
+        ) {
+            $enc = $this->view->getEncoding();
+        }
+
+        // TODO: remove check when minimum PHP version is >= 5.2.3
+        if (version_compare(PHP_VERSION, '5.2.3', '>=')) {
+            // do not encode existing HTML entities
+            return htmlspecialchars($string, ENT_QUOTES, $enc, false);
+        } else {
+            $string = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&amp;', $string);
+            $string = str_replace(array('<', '>', '\'', '"'), array('&lt;', '&gt;', '&#39;', '&quot;'), $string);
+            return $string;
+        }
+    }
+
+    // Public methods:
+
+    /**
+     * Returns an escaped absolute URL for the given page
+     *
+     * @param  Zend_Navigation_Page $page  page to get URL from
+     * @return string
+     */
+    public function url(Zend_Navigation_Page $page)
+    {
+        $href = $page->getHref();
+
+        if (!isset($href{0})) {
+            // no href
+            return '';
+        } elseif ($href{0} == '/') {
+            // href is relative to root; use serverUrl helper
+            $url = $this->getServerUrl() . $href;
+        } elseif (preg_match('/^[a-z]+:/im', (string) $href)) {
+            // scheme is given in href; assume absolute URL already
+            $url = (string) $href;
+        } else {
+            // href is relative to current document; use url helpers
+            $url = $this->getServerUrl()
+                 . rtrim($this->view->url(), '/') . '/'
+                 . $href;
+        }
+
+        return $this->_xmlEscape($url);
+    }
+
+    /**
+     * Returns a DOMDocument containing the Sitemap XML for the given container
+     *
+     * @param  Zend_Navigation_Container $container  [optional] container to get
+     *                                               breadcrumbs from, defaults
+     *                                               to what is registered in the
+     *                                               helper
+     * @return DOMDocument                           DOM representation of the
+     *                                               container
+     * @throws Zend_View_Exception                   if schema validation is on
+     *                                               and the sitemap is invalid
+     *                                               according to the sitemap
+     *                                               schema, or if sitemap
+     *                                               validators are used and the
+     *                                               loc element fails validation
+     */
+    public function getDomSitemap(Zend_Navigation_Container $container = null)
+    {
+        if (null === $container) {
+            $container = $this->getContainer();
+        }
+
+        // check if we should validate using our own validators
+        if ($this->getUseSitemapValidators()) {
+            require_once 'Zend/Validate/Sitemap/Changefreq.php';
+            require_once 'Zend/Validate/Sitemap/Lastmod.php';
+            require_once 'Zend/Validate/Sitemap/Loc.php';
+            require_once 'Zend/Validate/Sitemap/Priority.php';
+
+            // create validators
+            $locValidator        = new Zend_Validate_Sitemap_Loc();
+            $lastmodValidator    = new Zend_Validate_Sitemap_Lastmod();
+            $changefreqValidator = new Zend_Validate_Sitemap_Changefreq();
+            $priorityValidator   = new Zend_Validate_Sitemap_Priority();
+        }
+
+        // create document
+        $dom = new DOMDocument('1.0', 'UTF-8');
+        $dom->formatOutput = $this->getFormatOutput();
+
+        // ...and urlset (root) element
+        $urlSet = $dom->createElementNS(self::SITEMAP_NS, 'urlset');
+        $dom->appendChild($urlSet);
+
+        // create iterator
+        $iterator = new RecursiveIteratorIterator($container,
+            RecursiveIteratorIterator::SELF_FIRST);
+
+        $maxDepth = $this->getMaxDepth();
+        if (is_int($maxDepth)) {
+            $iterator->setMaxDepth($maxDepth);
+        }
+        $minDepth = $this->getMinDepth();
+        if (!is_int($minDepth) || $minDepth < 0) {
+            $minDepth = 0;
+        }
+
+        // iterate container
+        foreach ($iterator as $page) {
+            if ($iterator->getDepth() < $minDepth || !$this->accept($page)) {
+                // page should not be included
+                continue;
+            }
+
+            // get absolute url from page
+            if (!$url = $this->url($page)) {
+                // skip page if it has no url (rare case)
+                continue;
+            }
+
+            // create url node for this page
+            $urlNode = $dom->createElementNS(self::SITEMAP_NS, 'url');
+            $urlSet->appendChild($urlNode);
+
+            if ($this->getUseSitemapValidators() &&
+                !$locValidator->isValid($url)) {
+                require_once 'Zend/View/Exception.php';
+                $e = new Zend_View_Exception(sprintf(
+                        'Encountered an invalid URL for Sitemap XML: "%s"',
+                        $url));
+                $e->setView($this->view);
+                throw $e;
+            }
+
+            // put url in 'loc' element
+            $urlNode->appendChild($dom->createElementNS(self::SITEMAP_NS,
+                                                        'loc', $url));
+
+            // add 'lastmod' element if a valid lastmod is set in page
+            if (isset($page->lastmod)) {
+                $lastmod = strtotime((string) $page->lastmod);
+
+                // prevent 1970-01-01...
+                if ($lastmod !== false) {
+                    $lastmod = date('c', $lastmod);
+                }
+
+                if (!$this->getUseSitemapValidators() ||
+                    $lastmodValidator->isValid($lastmod)) {
+                    $urlNode->appendChild(
+                        $dom->createElementNS(self::SITEMAP_NS, 'lastmod',
+                                              $lastmod)
+                    );
+                }
+            }
+
+            // add 'changefreq' element if a valid changefreq is set in page
+            if (isset($page->changefreq)) {
+                $changefreq = $page->changefreq;
+                if (!$this->getUseSitemapValidators() ||
+                    $changefreqValidator->isValid($changefreq)) {
+                    $urlNode->appendChild(
+                        $dom->createElementNS(self::SITEMAP_NS, 'changefreq',
+                                              $changefreq)
+                    );
+                }
+            }
+
+            // add 'priority' element if a valid priority is set in page
+            if (isset($page->priority)) {
+                $priority = $page->priority;
+                if (!$this->getUseSitemapValidators() ||
+                    $priorityValidator->isValid($priority)) {
+                    $urlNode->appendChild(
+                        $dom->createElementNS(self::SITEMAP_NS, 'priority',
+                                              $priority)
+                    );
+                }
+            }
+        }
+
+        // validate using schema if specified
+        if ($this->getUseSchemaValidation()) {
+            if (!@$dom->schemaValidate(self::SITEMAP_XSD)) {
+                require_once 'Zend/View/Exception.php';
+                $e = new Zend_View_Exception(sprintf(
+                        'Sitemap is invalid according to XML Schema at "%s"',
+                        self::SITEMAP_XSD));
+                $e->setView($this->view);
+                throw $e;
+            }
+        }
+
+        return $dom;
+    }
+
+    // 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)
+    {
+        $dom = $this->getDomSitemap($container);
+
+        $xml = $this->getUseXmlDeclaration() ?
+               $dom->saveXML() :
+               $dom->saveXML($dom->documentElement);
+
+        return rtrim($xml, PHP_EOL);
+    }
+}