File indexing completed on 2025-01-12 05:21:59
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_Feed_Writer 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Feed_Writer_Renderer_RendererAbstract 0024 */ 0025 // require_once 'Zend/Feed/Writer/Renderer/RendererAbstract.php'; 0026 0027 // require_once 'Zend/Feed/Writer/Renderer/Feed/Atom/Source.php'; 0028 0029 /** @see Zend_Xml_Security */ 0030 // require_once 'Zend/Xml/Security.php'; 0031 0032 /** 0033 * @category Zend 0034 * @package Zend_Feed_Writer 0035 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0036 * @license http://framework.zend.com/license/new-bsd New BSD License 0037 */ 0038 class Zend_Feed_Writer_Renderer_Entry_Atom 0039 extends Zend_Feed_Writer_Renderer_RendererAbstract 0040 implements Zend_Feed_Writer_Renderer_RendererInterface 0041 { 0042 /** 0043 * Constructor 0044 * 0045 * @param Zend_Feed_Writer_Entry $container 0046 * @return void 0047 */ 0048 public function __construct (Zend_Feed_Writer_Entry $container) 0049 { 0050 parent::__construct($container); 0051 } 0052 0053 /** 0054 * Render atom entry 0055 * 0056 * @return Zend_Feed_Writer_Renderer_Entry_Atom 0057 */ 0058 public function render() 0059 { 0060 $this->_dom = new DOMDocument('1.0', $this->_container->getEncoding()); 0061 $this->_dom->formatOutput = true; 0062 $entry = $this->_dom->createElementNS(Zend_Feed_Writer::NAMESPACE_ATOM_10, 'entry'); 0063 $this->_dom->appendChild($entry); 0064 0065 $this->_setSource($this->_dom, $entry); 0066 $this->_setTitle($this->_dom, $entry); 0067 $this->_setDescription($this->_dom, $entry); 0068 $this->_setDateCreated($this->_dom, $entry); 0069 $this->_setDateModified($this->_dom, $entry); 0070 $this->_setLink($this->_dom, $entry); 0071 $this->_setId($this->_dom, $entry); 0072 $this->_setAuthors($this->_dom, $entry); 0073 $this->_setEnclosure($this->_dom, $entry); 0074 $this->_setContent($this->_dom, $entry); 0075 $this->_setCategories($this->_dom, $entry); 0076 0077 foreach ($this->_extensions as $ext) { 0078 $ext->setType($this->getType()); 0079 $ext->setRootElement($this->getRootElement()); 0080 $ext->setDomDocument($this->getDomDocument(), $entry); 0081 $ext->render(); 0082 } 0083 0084 return $this; 0085 } 0086 0087 /** 0088 * Set entry title 0089 * 0090 * @param DOMDocument $dom 0091 * @param DOMElement $root 0092 * @return void 0093 */ 0094 protected function _setTitle(DOMDocument $dom, DOMElement $root) 0095 { 0096 if(!$this->getDataContainer()->getTitle()) { 0097 // require_once 'Zend/Feed/Exception.php'; 0098 $message = 'Atom 1.0 entry elements MUST contain exactly one' 0099 . ' atom:title element but a title has not been set'; 0100 $exception = new Zend_Feed_Exception($message); 0101 if (!$this->_ignoreExceptions) { 0102 throw $exception; 0103 } else { 0104 $this->_exceptions[] = $exception; 0105 return; 0106 } 0107 } 0108 $title = $dom->createElement('title'); 0109 $root->appendChild($title); 0110 $title->setAttribute('type', 'html'); 0111 $cdata = $dom->createCDATASection($this->getDataContainer()->getTitle()); 0112 $title->appendChild($cdata); 0113 } 0114 0115 /** 0116 * Set entry description 0117 * 0118 * @param DOMDocument $dom 0119 * @param DOMElement $root 0120 * @return void 0121 */ 0122 protected function _setDescription(DOMDocument $dom, DOMElement $root) 0123 { 0124 if(!$this->getDataContainer()->getDescription()) { 0125 return; // unless src content or base64 0126 } 0127 $subtitle = $dom->createElement('summary'); 0128 $root->appendChild($subtitle); 0129 $subtitle->setAttribute('type', 'html'); 0130 $cdata = $dom->createCDATASection( 0131 $this->getDataContainer()->getDescription() 0132 ); 0133 $subtitle->appendChild($cdata); 0134 } 0135 0136 /** 0137 * Set date entry was modified 0138 * 0139 * @param DOMDocument $dom 0140 * @param DOMElement $root 0141 * @return void 0142 */ 0143 protected function _setDateModified(DOMDocument $dom, DOMElement $root) 0144 { 0145 if(!$this->getDataContainer()->getDateModified()) { 0146 // require_once 'Zend/Feed/Exception.php'; 0147 $message = 'Atom 1.0 entry elements MUST contain exactly one' 0148 . ' atom:updated element but a modification date has not been set'; 0149 $exception = new Zend_Feed_Exception($message); 0150 if (!$this->_ignoreExceptions) { 0151 throw $exception; 0152 } else { 0153 $this->_exceptions[] = $exception; 0154 return; 0155 } 0156 } 0157 0158 $updated = $dom->createElement('updated'); 0159 $root->appendChild($updated); 0160 $text = $dom->createTextNode( 0161 $this->getDataContainer()->getDateModified()->get(Zend_Date::ISO_8601) 0162 ); 0163 $updated->appendChild($text); 0164 } 0165 0166 /** 0167 * Set date entry was created 0168 * 0169 * @param DOMDocument $dom 0170 * @param DOMElement $root 0171 * @return void 0172 */ 0173 protected function _setDateCreated(DOMDocument $dom, DOMElement $root) 0174 { 0175 if (!$this->getDataContainer()->getDateCreated()) { 0176 return; 0177 } 0178 $el = $dom->createElement('published'); 0179 $root->appendChild($el); 0180 $text = $dom->createTextNode( 0181 $this->getDataContainer()->getDateCreated()->get(Zend_Date::ISO_8601) 0182 ); 0183 $el->appendChild($text); 0184 } 0185 0186 /** 0187 * Set entry authors 0188 * 0189 * @param DOMDocument $dom 0190 * @param DOMElement $root 0191 * @return void 0192 */ 0193 protected function _setAuthors(DOMDocument $dom, DOMElement $root) 0194 { 0195 $authors = $this->_container->getAuthors(); 0196 if ((!$authors || empty($authors))) { 0197 /** 0198 * This will actually trigger an Exception at the feed level if 0199 * a feed level author is not set. 0200 */ 0201 return; 0202 } 0203 foreach ($authors as $data) { 0204 $author = $this->_dom->createElement('author'); 0205 $name = $this->_dom->createElement('name'); 0206 $author->appendChild($name); 0207 $root->appendChild($author); 0208 $text = $dom->createTextNode($data['name']); 0209 $name->appendChild($text); 0210 if (array_key_exists('email', $data)) { 0211 $email = $this->_dom->createElement('email'); 0212 $author->appendChild($email); 0213 $text = $dom->createTextNode($data['email']); 0214 $email->appendChild($text); 0215 } 0216 if (array_key_exists('uri', $data)) { 0217 $uri = $this->_dom->createElement('uri'); 0218 $author->appendChild($uri); 0219 $text = $dom->createTextNode($data['uri']); 0220 $uri->appendChild($text); 0221 } 0222 } 0223 } 0224 0225 /** 0226 * Set entry enclosure 0227 * 0228 * @param DOMDocument $dom 0229 * @param DOMElement $root 0230 * @return void 0231 */ 0232 protected function _setEnclosure(DOMDocument $dom, DOMElement $root) 0233 { 0234 $data = $this->_container->getEnclosure(); 0235 if ((!$data || empty($data))) { 0236 return; 0237 } 0238 $enclosure = $this->_dom->createElement('link'); 0239 $enclosure->setAttribute('rel', 'enclosure'); 0240 if (isset($data['type'])) { 0241 $enclosure->setAttribute('type', $data['type']); 0242 } 0243 if (isset($data['length'])) { 0244 $enclosure->setAttribute('length', $data['length']); 0245 } 0246 $enclosure->setAttribute('href', $data['uri']); 0247 $root->appendChild($enclosure); 0248 } 0249 0250 protected function _setLink(DOMDocument $dom, DOMElement $root) 0251 { 0252 if(!$this->getDataContainer()->getLink()) { 0253 return; 0254 } 0255 $link = $dom->createElement('link'); 0256 $root->appendChild($link); 0257 $link->setAttribute('rel', 'alternate'); 0258 $link->setAttribute('type', 'text/html'); 0259 $link->setAttribute('href', $this->getDataContainer()->getLink()); 0260 } 0261 0262 /** 0263 * Set entry identifier 0264 * 0265 * @param DOMDocument $dom 0266 * @param DOMElement $root 0267 * @return void 0268 */ 0269 protected function _setId(DOMDocument $dom, DOMElement $root) 0270 { 0271 if(!$this->getDataContainer()->getId() 0272 && !$this->getDataContainer()->getLink()) { 0273 // require_once 'Zend/Feed/Exception.php'; 0274 $message = 'Atom 1.0 entry elements MUST contain exactly one ' 0275 . 'atom:id element, or as an alternative, we can use the same ' 0276 . 'value as atom:link however neither a suitable link nor an ' 0277 . 'id have been set'; 0278 $exception = new Zend_Feed_Exception($message); 0279 if (!$this->_ignoreExceptions) { 0280 throw $exception; 0281 } else { 0282 $this->_exceptions[] = $exception; 0283 return; 0284 } 0285 } 0286 0287 if (!$this->getDataContainer()->getId()) { 0288 $this->getDataContainer()->setId( 0289 $this->getDataContainer()->getLink()); 0290 } 0291 if (!Zend_Uri::check($this->getDataContainer()->getId()) && 0292 !preg_match("#^urn:[a-zA-Z0-9][a-zA-Z0-9\-]{1,31}:([a-zA-Z0-9\(\)\+\,\.\:\=\@\;\$\_\!\*\-]|%[0-9a-fA-F]{2})*#", 0293 $this->getDataContainer()->getId() 0294 ) && !$this->_validateTagUri($this->getDataContainer()->getId())) { 0295 // require_once 'Zend/Feed/Exception.php'; 0296 throw new Zend_Feed_Exception('Atom 1.0 IDs must be a valid URI/IRI'); 0297 } 0298 $id = $dom->createElement('id'); 0299 $root->appendChild($id); 0300 $text = $dom->createTextNode($this->getDataContainer()->getId()); 0301 $id->appendChild($text); 0302 } 0303 0304 /** 0305 * Validate a URI using the tag scheme (RFC 4151) 0306 * 0307 * @param string $id 0308 * @return bool 0309 */ 0310 protected function _validateTagUri($id) 0311 { 0312 if (preg_match('/^tag:(?<name>.*),(?<date>\d{4}-?\d{0,2}-?\d{0,2}):(?<specific>.*)(.*:)*$/', $id, $matches)) { 0313 $dvalid = false; 0314 $nvalid = false; 0315 $date = $matches['date']; 0316 $d6 = strtotime($date); 0317 if ((strlen($date) == 4) && $date <= date('Y')) { 0318 $dvalid = true; 0319 } elseif ((strlen($date) == 7) && ($d6 < strtotime("now"))) { 0320 $dvalid = true; 0321 } elseif ((strlen($date) == 10) && ($d6 < strtotime("now"))) { 0322 $dvalid = true; 0323 } 0324 $validator = new Zend_Validate_EmailAddress; 0325 if ($validator->isValid($matches['name'])) { 0326 $nvalid = true; 0327 } else { 0328 $nvalid = $validator->isValid('info@' . $matches['name']); 0329 } 0330 return $dvalid && $nvalid; 0331 0332 } 0333 return false; 0334 } 0335 0336 /** 0337 * Set entry content 0338 * 0339 * @param DOMDocument $dom 0340 * @param DOMElement $root 0341 * @return void 0342 */ 0343 protected function _setContent(DOMDocument $dom, DOMElement $root) 0344 { 0345 $content = $this->getDataContainer()->getContent(); 0346 if (!$content && !$this->getDataContainer()->getLink()) { 0347 // require_once 'Zend/Feed/Exception.php'; 0348 $message = 'Atom 1.0 entry elements MUST contain exactly one ' 0349 . 'atom:content element, or as an alternative, at least one link ' 0350 . 'with a rel attribute of "alternate" to indicate an alternate ' 0351 . 'method to consume the content.'; 0352 $exception = new Zend_Feed_Exception($message); 0353 if (!$this->_ignoreExceptions) { 0354 throw $exception; 0355 } else { 0356 $this->_exceptions[] = $exception; 0357 return; 0358 } 0359 } 0360 if (!$content) { 0361 return; 0362 } 0363 $element = $dom->createElement('content'); 0364 $element->setAttribute('type', 'xhtml'); 0365 $xhtmlElement = $this->_loadXhtml($content); 0366 $xhtml = $dom->importNode($xhtmlElement, true); 0367 $element->appendChild($xhtml); 0368 $root->appendChild($element); 0369 } 0370 0371 /** 0372 * Load a HTML string and attempt to normalise to XML 0373 */ 0374 protected function _loadXhtml($content) 0375 { 0376 $xhtml = ''; 0377 if (class_exists('tidy', false)) { 0378 $tidy = new tidy; 0379 $config = array( 0380 'output-xhtml' => true, 0381 'show-body-only' => true, 0382 'quote-nbsp' => false 0383 ); 0384 $encoding = str_replace('-', '', $this->getEncoding()); 0385 $tidy->parseString($content, $config, $encoding); 0386 $tidy->cleanRepair(); 0387 $xhtml = (string) $tidy; 0388 } else { 0389 $xhtml = $content; 0390 } 0391 $xhtml = preg_replace(array( 0392 "/(<[\/]?)([a-zA-Z]+)/" 0393 ), '$1xhtml:$2', $xhtml); 0394 $dom = new DOMDocument('1.0', $this->getEncoding()); 0395 0396 $dom = Zend_Xml_Security::scan('<xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml">' 0397 . $xhtml . '</xhtml:div>', $dom); 0398 return $dom->documentElement; 0399 } 0400 0401 /** 0402 * Set entry cateories 0403 * 0404 * @param DOMDocument $dom 0405 * @param DOMElement $root 0406 * @return void 0407 */ 0408 protected function _setCategories(DOMDocument $dom, DOMElement $root) 0409 { 0410 $categories = $this->getDataContainer()->getCategories(); 0411 if (!$categories) { 0412 return; 0413 } 0414 foreach ($categories as $cat) { 0415 $category = $dom->createElement('category'); 0416 $category->setAttribute('term', $cat['term']); 0417 if (isset($cat['label'])) { 0418 $category->setAttribute('label', $cat['label']); 0419 } else { 0420 $category->setAttribute('label', $cat['term']); 0421 } 0422 if (isset($cat['scheme'])) { 0423 $category->setAttribute('scheme', $cat['scheme']); 0424 } 0425 $root->appendChild($category); 0426 } 0427 } 0428 0429 /** 0430 * Append Source element (Atom 1.0 Feed Metadata) 0431 * 0432 * @param DOMDocument $dom 0433 * @param DOMElement $root 0434 * @return void 0435 */ 0436 protected function _setSource(DOMDocument $dom, DOMElement $root) 0437 { 0438 $source = $this->getDataContainer()->getSource(); 0439 if (!$source) { 0440 return; 0441 } 0442 $renderer = new Zend_Feed_Writer_Renderer_Feed_Atom_Source($source); 0443 $renderer->setType($this->getType()); 0444 $element = $renderer->render()->getElement(); 0445 $imported = $dom->importNode($element, true); 0446 $root->appendChild($imported); 0447 } 0448 }