File indexing completed on 2024-12-29 05:28:12

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_View
0017  * @subpackage Helper
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_View_Helper_Navigation_HelperAbstract
0025  */
0026 // require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
0027 
0028 /**
0029  * Helper for printing sitemaps
0030  *
0031  * @link http://www.sitemaps.org/protocol.php
0032  *
0033  * @category   Zend
0034  * @package    Zend_View
0035  * @subpackage Helper
0036  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0037  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0038  */
0039 class Zend_View_Helper_Navigation_Sitemap
0040     extends Zend_View_Helper_Navigation_HelperAbstract
0041 {
0042     /**
0043      * Namespace for the <urlset> tag
0044      *
0045      * @var string
0046      */
0047     const SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
0048 
0049     /**
0050      * Schema URL
0051      *
0052      * @var string
0053      */
0054     const SITEMAP_XSD = 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd';
0055 
0056     /**
0057      * Whether the XML declaration should be included in XML output
0058      *
0059      * @var bool
0060      */
0061     protected $_useXmlDeclaration = true;
0062 
0063     /**
0064      * Whether sitemap should be validated using Zend_Validate_Sitemap_*
0065      *
0066      * @var bool
0067      */
0068     protected $_useSitemapValidators = true;
0069 
0070     /**
0071      * Whether sitemap should be schema validated when generated
0072      *
0073      * @var bool
0074      */
0075     protected $_useSchemaValidation = false;
0076 
0077     /**
0078      * Server url
0079      *
0080      * @var string
0081      */
0082     protected $_serverUrl;
0083 
0084     /**
0085      * View helper entry point:
0086      * Retrieves helper and optionally sets container to operate on
0087      *
0088      * @param  Zend_Navigation_Container $container  [optional] container to
0089      *                                               operate on
0090      * @return Zend_View_Helper_Navigation_Sitemap   fluent interface, returns
0091      *                                               self
0092      */
0093     public function sitemap(Zend_Navigation_Container $container = null)
0094     {
0095         if (null !== $container) {
0096             $this->setContainer($container);
0097         }
0098 
0099         return $this;
0100     }
0101 
0102     // Accessors:
0103 
0104     /**
0105      * Sets whether the XML declaration should be used in output
0106      *
0107      * @param  bool $useXmlDecl                     whether XML delcaration
0108      *                                              should be rendered
0109      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
0110      *                                              self
0111      */
0112     public function setUseXmlDeclaration($useXmlDecl)
0113     {
0114         $this->_useXmlDeclaration = (bool) $useXmlDecl;
0115         return $this;
0116     }
0117 
0118     /**
0119      * Returns whether the XML declaration should be used in output
0120      *
0121      * @return bool  whether the XML declaration should be used in output
0122      */
0123     public function getUseXmlDeclaration()
0124     {
0125         return $this->_useXmlDeclaration;
0126     }
0127 
0128     /**
0129      * Sets whether sitemap should be validated using Zend_Validate_Sitemap_*
0130      *
0131      * @param  bool $useSitemapValidators           whether sitemap validators
0132      *                                              should be used
0133      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
0134      *                                              self
0135      */
0136     public function setUseSitemapValidators($useSitemapValidators)
0137     {
0138         $this->_useSitemapValidators = (bool) $useSitemapValidators;
0139         return $this;
0140     }
0141 
0142     /**
0143      * Returns whether sitemap should be validated using Zend_Validate_Sitemap_*
0144      *
0145      * @return bool  whether sitemap should be validated using validators
0146      */
0147     public function getUseSitemapValidators()
0148     {
0149         return $this->_useSitemapValidators;
0150     }
0151 
0152     /**
0153      * Sets whether sitemap should be schema validated when generated
0154      *
0155      * @param  bool $schemaValidation               whether sitemap should
0156      *                                              validated using XSD Schema
0157      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
0158      *                                              self
0159      */
0160     public function setUseSchemaValidation($schemaValidation)
0161     {
0162         $this->_useSchemaValidation = (bool) $schemaValidation;
0163         return $this;
0164     }
0165 
0166     /**
0167      * Returns true if sitemap should be schema validated when generated
0168      *
0169      * @return bool
0170      */
0171     public function getUseSchemaValidation()
0172     {
0173         return $this->_useSchemaValidation;
0174     }
0175 
0176     /**
0177      * Sets server url (scheme and host-related stuff without request URI)
0178      *
0179      * E.g. http://www.example.com
0180      *
0181      * @param  string $serverUrl                    server URL to set (only
0182      *                                              scheme and host)
0183      * @throws Zend_Uri_Exception                   if invalid server URL
0184      * @return Zend_View_Helper_Navigation_Sitemap  fluent interface, returns
0185      *                                              self
0186      */
0187     public function setServerUrl($serverUrl)
0188     {
0189         // require_once 'Zend/Uri.php';
0190         $uri = Zend_Uri::factory($serverUrl);
0191         $uri->setFragment('');
0192         $uri->setPath('');
0193         $uri->setQuery('');
0194 
0195         if ($uri->valid()) {
0196             $this->_serverUrl = $uri->getUri();
0197         } else {
0198             // require_once 'Zend/Uri/Exception.php';
0199             $e = new Zend_Uri_Exception(sprintf(
0200                     'Invalid server URL: "%s"',
0201                     $serverUrl));
0202             $e->setView($this->view);
0203             throw $e;
0204         }
0205 
0206         return $this;
0207     }
0208 
0209     /**
0210      * Returns server URL
0211      *
0212      * @return string  server URL
0213      */
0214     public function getServerUrl()
0215     {
0216         if (!isset($this->_serverUrl)) {
0217             $this->_serverUrl = $this->view->serverUrl();
0218         }
0219 
0220         return $this->_serverUrl;
0221     }
0222 
0223     // Helper methods:
0224 
0225     /**
0226      * Escapes string for XML usage
0227      *
0228      * @param  string $string  string to escape
0229      * @return string          escaped string
0230      */
0231     protected function _xmlEscape($string)
0232     {
0233         $enc = 'UTF-8';
0234         if ($this->view instanceof Zend_View_Interface
0235             && method_exists($this->view, 'getEncoding')
0236         ) {
0237             $enc = $this->view->getEncoding();
0238         }
0239 
0240         // do not encode existing HTML entities
0241         return htmlspecialchars($string, ENT_QUOTES, $enc, false);
0242     }
0243 
0244     // Public methods:
0245 
0246     /**
0247      * Returns an escaped absolute URL for the given page
0248      *
0249      * @param  Zend_Navigation_Page $page  page to get URL from
0250      * @return string
0251      */
0252     public function url(Zend_Navigation_Page $page)
0253     {
0254         $href = $page->getHref();
0255 
0256         if (!isset($href{0})) {
0257             // no href
0258             return '';
0259         } elseif ($href{0} == '/') {
0260             // href is relative to root; use serverUrl helper
0261             $url = $this->getServerUrl() . $href;
0262         } elseif (preg_match('/^[a-z]+:/im', (string) $href)) {
0263             // scheme is given in href; assume absolute URL already
0264             $url = (string) $href;
0265         } else {
0266             // href is relative to current document; use url helpers
0267             $url = $this->getServerUrl()
0268                  . rtrim($this->view->url(), '/') . '/'
0269                  . $href;
0270         }
0271 
0272         return $this->_xmlEscape($url);
0273     }
0274 
0275     /**
0276      * Returns a DOMDocument containing the Sitemap XML for the given container
0277      *
0278      * @param  Zend_Navigation_Container $container  [optional] container to get
0279      *                                               breadcrumbs from, defaults
0280      *                                               to what is registered in the
0281      *                                               helper
0282      * @return DOMDocument                           DOM representation of the
0283      *                                               container
0284      * @throws Zend_View_Exception                   if schema validation is on
0285      *                                               and the sitemap is invalid
0286      *                                               according to the sitemap
0287      *                                               schema, or if sitemap
0288      *                                               validators are used and the
0289      *                                               loc element fails validation
0290      */
0291     public function getDomSitemap(Zend_Navigation_Container $container = null)
0292     {
0293         if (null === $container) {
0294             $container = $this->getContainer();
0295         }
0296 
0297         // check if we should validate using our own validators
0298         if ($this->getUseSitemapValidators()) {
0299             // require_once 'Zend/Validate/Sitemap/Changefreq.php';
0300             // require_once 'Zend/Validate/Sitemap/Lastmod.php';
0301             // require_once 'Zend/Validate/Sitemap/Loc.php';
0302             // require_once 'Zend/Validate/Sitemap/Priority.php';
0303 
0304             // create validators
0305             $locValidator        = new Zend_Validate_Sitemap_Loc();
0306             $lastmodValidator    = new Zend_Validate_Sitemap_Lastmod();
0307             $changefreqValidator = new Zend_Validate_Sitemap_Changefreq();
0308             $priorityValidator   = new Zend_Validate_Sitemap_Priority();
0309         }
0310 
0311         // create document
0312         $dom = new DOMDocument('1.0', 'UTF-8');
0313         $dom->formatOutput = $this->getFormatOutput();
0314 
0315         // ...and urlset (root) element
0316         $urlSet = $dom->createElementNS(self::SITEMAP_NS, 'urlset');
0317         $dom->appendChild($urlSet);
0318 
0319         // create iterator
0320         $iterator = new RecursiveIteratorIterator($container,
0321             RecursiveIteratorIterator::SELF_FIRST);
0322 
0323         $maxDepth = $this->getMaxDepth();
0324         if (is_int($maxDepth)) {
0325             $iterator->setMaxDepth($maxDepth);
0326         }
0327         $minDepth = $this->getMinDepth();
0328         if (!is_int($minDepth) || $minDepth < 0) {
0329             $minDepth = 0;
0330         }
0331 
0332         // iterate container
0333         foreach ($iterator as $page) {
0334             if ($iterator->getDepth() < $minDepth || !$this->accept($page)) {
0335                 // page should not be included
0336                 continue;
0337             }
0338 
0339             // get absolute url from page
0340             if (!$url = $this->url($page)) {
0341                 // skip page if it has no url (rare case)
0342                 continue;
0343             }
0344 
0345             // create url node for this page
0346             $urlNode = $dom->createElementNS(self::SITEMAP_NS, 'url');
0347             $urlSet->appendChild($urlNode);
0348 
0349             if ($this->getUseSitemapValidators() &&
0350                 !$locValidator->isValid($url)) {
0351                 // require_once 'Zend/View/Exception.php';
0352                 $e = new Zend_View_Exception(sprintf(
0353                         'Encountered an invalid URL for Sitemap XML: "%s"',
0354                         $url));
0355                 $e->setView($this->view);
0356                 throw $e;
0357             }
0358 
0359             // put url in 'loc' element
0360             $urlNode->appendChild($dom->createElementNS(self::SITEMAP_NS,
0361                                                         'loc', $url));
0362 
0363             // add 'lastmod' element if a valid lastmod is set in page
0364             if (isset($page->lastmod)) {
0365                 $lastmod = strtotime((string) $page->lastmod);
0366 
0367                 // prevent 1970-01-01...
0368                 if ($lastmod !== false) {
0369                     $lastmod = date('c', $lastmod);
0370                 }
0371 
0372                 if (!$this->getUseSitemapValidators() ||
0373                     $lastmodValidator->isValid($lastmod)) {
0374                     $urlNode->appendChild(
0375                         $dom->createElementNS(self::SITEMAP_NS, 'lastmod',
0376                                               $lastmod)
0377                     );
0378                 }
0379             }
0380 
0381             // add 'changefreq' element if a valid changefreq is set in page
0382             if (isset($page->changefreq)) {
0383                 $changefreq = $page->changefreq;
0384                 if (!$this->getUseSitemapValidators() ||
0385                     $changefreqValidator->isValid($changefreq)) {
0386                     $urlNode->appendChild(
0387                         $dom->createElementNS(self::SITEMAP_NS, 'changefreq',
0388                                               $changefreq)
0389                     );
0390                 }
0391             }
0392 
0393             // add 'priority' element if a valid priority is set in page
0394             if (isset($page->priority)) {
0395                 $priority = $page->priority;
0396                 if (!$this->getUseSitemapValidators() ||
0397                     $priorityValidator->isValid($priority)) {
0398                     $urlNode->appendChild(
0399                         $dom->createElementNS(self::SITEMAP_NS, 'priority',
0400                                               $priority)
0401                     );
0402                 }
0403             }
0404         }
0405 
0406         // validate using schema if specified
0407         if ($this->getUseSchemaValidation()) {
0408             if (!@$dom->schemaValidate(self::SITEMAP_XSD)) {
0409                 // require_once 'Zend/View/Exception.php';
0410                 $e = new Zend_View_Exception(sprintf(
0411                         'Sitemap is invalid according to XML Schema at "%s"',
0412                         self::SITEMAP_XSD));
0413                 $e->setView($this->view);
0414                 throw $e;
0415             }
0416         }
0417 
0418         return $dom;
0419     }
0420 
0421     // Zend_View_Helper_Navigation_Helper:
0422 
0423     /**
0424      * Renders helper
0425      *
0426      * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
0427      *
0428      * @param  Zend_Navigation_Container $container  [optional] container to
0429      *                                               render. Default is to
0430      *                                               render the container
0431      *                                               registered in the helper.
0432      * @return string                                helper output
0433      */
0434     public function render(Zend_Navigation_Container $container = null)
0435     {
0436         $dom = $this->getDomSitemap($container);
0437 
0438         $xml = $this->getUseXmlDeclaration() ?
0439                $dom->saveXML() :
0440                $dom->saveXML($dom->documentElement);
0441 
0442         return rtrim($xml, self::EOL);
0443     }
0444 }