Mercurial > hg > isophonics-drupal-site
comparison vendor/zendframework/zend-feed/src/Writer/Renderer/Entry/Atom.php @ 0:4c8ae668cc8c
Initial import (non-working)
| author | Chris Cannam |
|---|---|
| date | Wed, 29 Nov 2017 16:09:58 +0000 |
| parents | |
| children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:4c8ae668cc8c |
|---|---|
| 1 <?php | |
| 2 /** | |
| 3 * Zend Framework (http://framework.zend.com/) | |
| 4 * | |
| 5 * @link http://github.com/zendframework/zf2 for the canonical source repository | |
| 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) | |
| 7 * @license http://framework.zend.com/license/new-bsd New BSD License | |
| 8 */ | |
| 9 | |
| 10 namespace Zend\Feed\Writer\Renderer\Entry; | |
| 11 | |
| 12 use DateTime; | |
| 13 use DOMDocument; | |
| 14 use DOMElement; | |
| 15 use Zend\Feed\Uri; | |
| 16 use Zend\Feed\Writer; | |
| 17 use Zend\Feed\Writer\Renderer; | |
| 18 use Zend\Validator; | |
| 19 | |
| 20 class Atom extends Renderer\AbstractRenderer implements Renderer\RendererInterface | |
| 21 { | |
| 22 /** | |
| 23 * Constructor | |
| 24 * | |
| 25 * @param Writer\Entry $container | |
| 26 */ | |
| 27 public function __construct(Writer\Entry $container) | |
| 28 { | |
| 29 parent::__construct($container); | |
| 30 } | |
| 31 | |
| 32 /** | |
| 33 * Render atom entry | |
| 34 * | |
| 35 * @return Atom | |
| 36 */ | |
| 37 public function render() | |
| 38 { | |
| 39 $this->dom = new DOMDocument('1.0', $this->container->getEncoding()); | |
| 40 $this->dom->formatOutput = true; | |
| 41 $entry = $this->dom->createElementNS(Writer\Writer::NAMESPACE_ATOM_10, 'entry'); | |
| 42 $this->dom->appendChild($entry); | |
| 43 | |
| 44 $this->_setSource($this->dom, $entry); | |
| 45 $this->_setTitle($this->dom, $entry); | |
| 46 $this->_setDescription($this->dom, $entry); | |
| 47 $this->_setDateCreated($this->dom, $entry); | |
| 48 $this->_setDateModified($this->dom, $entry); | |
| 49 $this->_setLink($this->dom, $entry); | |
| 50 $this->_setId($this->dom, $entry); | |
| 51 $this->_setAuthors($this->dom, $entry); | |
| 52 $this->_setEnclosure($this->dom, $entry); | |
| 53 $this->_setContent($this->dom, $entry); | |
| 54 $this->_setCategories($this->dom, $entry); | |
| 55 | |
| 56 foreach ($this->extensions as $ext) { | |
| 57 $ext->setType($this->getType()); | |
| 58 $ext->setRootElement($this->getRootElement()); | |
| 59 $ext->setDOMDocument($this->getDOMDocument(), $entry); | |
| 60 $ext->render(); | |
| 61 } | |
| 62 | |
| 63 return $this; | |
| 64 } | |
| 65 | |
| 66 /** | |
| 67 * Set entry title | |
| 68 * | |
| 69 * @param DOMDocument $dom | |
| 70 * @param DOMElement $root | |
| 71 * @return void | |
| 72 * @throws Writer\Exception\InvalidArgumentException | |
| 73 */ | |
| 74 protected function _setTitle(DOMDocument $dom, DOMElement $root) | |
| 75 { | |
| 76 if (!$this->getDataContainer()->getTitle()) { | |
| 77 $message = 'Atom 1.0 entry elements MUST contain exactly one' | |
| 78 . ' atom:title element but a title has not been set'; | |
| 79 $exception = new Writer\Exception\InvalidArgumentException($message); | |
| 80 if (!$this->ignoreExceptions) { | |
| 81 throw $exception; | |
| 82 } else { | |
| 83 $this->exceptions[] = $exception; | |
| 84 return; | |
| 85 } | |
| 86 } | |
| 87 $title = $dom->createElement('title'); | |
| 88 $root->appendChild($title); | |
| 89 $title->setAttribute('type', 'html'); | |
| 90 $cdata = $dom->createCDATASection($this->getDataContainer()->getTitle()); | |
| 91 $title->appendChild($cdata); | |
| 92 } | |
| 93 | |
| 94 /** | |
| 95 * Set entry description | |
| 96 * | |
| 97 * @param DOMDocument $dom | |
| 98 * @param DOMElement $root | |
| 99 * @return void | |
| 100 */ | |
| 101 protected function _setDescription(DOMDocument $dom, DOMElement $root) | |
| 102 { | |
| 103 if (!$this->getDataContainer()->getDescription()) { | |
| 104 return; // unless src content or base64 | |
| 105 } | |
| 106 $subtitle = $dom->createElement('summary'); | |
| 107 $root->appendChild($subtitle); | |
| 108 $subtitle->setAttribute('type', 'html'); | |
| 109 $cdata = $dom->createCDATASection( | |
| 110 $this->getDataContainer()->getDescription() | |
| 111 ); | |
| 112 $subtitle->appendChild($cdata); | |
| 113 } | |
| 114 | |
| 115 /** | |
| 116 * Set date entry was modified | |
| 117 * | |
| 118 * @param DOMDocument $dom | |
| 119 * @param DOMElement $root | |
| 120 * @return void | |
| 121 * @throws Writer\Exception\InvalidArgumentException | |
| 122 */ | |
| 123 protected function _setDateModified(DOMDocument $dom, DOMElement $root) | |
| 124 { | |
| 125 if (!$this->getDataContainer()->getDateModified()) { | |
| 126 $message = 'Atom 1.0 entry elements MUST contain exactly one' | |
| 127 . ' atom:updated element but a modification date has not been set'; | |
| 128 $exception = new Writer\Exception\InvalidArgumentException($message); | |
| 129 if (!$this->ignoreExceptions) { | |
| 130 throw $exception; | |
| 131 } else { | |
| 132 $this->exceptions[] = $exception; | |
| 133 return; | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 $updated = $dom->createElement('updated'); | |
| 138 $root->appendChild($updated); | |
| 139 $text = $dom->createTextNode( | |
| 140 $this->getDataContainer()->getDateModified()->format(DateTime::ATOM) | |
| 141 ); | |
| 142 $updated->appendChild($text); | |
| 143 } | |
| 144 | |
| 145 /** | |
| 146 * Set date entry was created | |
| 147 * | |
| 148 * @param DOMDocument $dom | |
| 149 * @param DOMElement $root | |
| 150 * @return void | |
| 151 */ | |
| 152 protected function _setDateCreated(DOMDocument $dom, DOMElement $root) | |
| 153 { | |
| 154 if (!$this->getDataContainer()->getDateCreated()) { | |
| 155 return; | |
| 156 } | |
| 157 $el = $dom->createElement('published'); | |
| 158 $root->appendChild($el); | |
| 159 $text = $dom->createTextNode( | |
| 160 $this->getDataContainer()->getDateCreated()->format(DateTime::ATOM) | |
| 161 ); | |
| 162 $el->appendChild($text); | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * Set entry authors | |
| 167 * | |
| 168 * @param DOMDocument $dom | |
| 169 * @param DOMElement $root | |
| 170 * @return void | |
| 171 */ | |
| 172 protected function _setAuthors(DOMDocument $dom, DOMElement $root) | |
| 173 { | |
| 174 $authors = $this->container->getAuthors(); | |
| 175 if ((!$authors || empty($authors))) { | |
| 176 /** | |
| 177 * This will actually trigger an Exception at the feed level if | |
| 178 * a feed level author is not set. | |
| 179 */ | |
| 180 return; | |
| 181 } | |
| 182 foreach ($authors as $data) { | |
| 183 $author = $this->dom->createElement('author'); | |
| 184 $name = $this->dom->createElement('name'); | |
| 185 $author->appendChild($name); | |
| 186 $root->appendChild($author); | |
| 187 $text = $dom->createTextNode($data['name']); | |
| 188 $name->appendChild($text); | |
| 189 if (array_key_exists('email', $data)) { | |
| 190 $email = $this->dom->createElement('email'); | |
| 191 $author->appendChild($email); | |
| 192 $text = $dom->createTextNode($data['email']); | |
| 193 $email->appendChild($text); | |
| 194 } | |
| 195 if (array_key_exists('uri', $data)) { | |
| 196 $uri = $this->dom->createElement('uri'); | |
| 197 $author->appendChild($uri); | |
| 198 $text = $dom->createTextNode($data['uri']); | |
| 199 $uri->appendChild($text); | |
| 200 } | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 /** | |
| 205 * Set entry enclosure | |
| 206 * | |
| 207 * @param DOMDocument $dom | |
| 208 * @param DOMElement $root | |
| 209 * @return void | |
| 210 */ | |
| 211 protected function _setEnclosure(DOMDocument $dom, DOMElement $root) | |
| 212 { | |
| 213 $data = $this->container->getEnclosure(); | |
| 214 if ((!$data || empty($data))) { | |
| 215 return; | |
| 216 } | |
| 217 $enclosure = $this->dom->createElement('link'); | |
| 218 $enclosure->setAttribute('rel', 'enclosure'); | |
| 219 if (isset($data['type'])) { | |
| 220 $enclosure->setAttribute('type', $data['type']); | |
| 221 } | |
| 222 if (isset($data['length'])) { | |
| 223 $enclosure->setAttribute('length', $data['length']); | |
| 224 } | |
| 225 $enclosure->setAttribute('href', $data['uri']); | |
| 226 $root->appendChild($enclosure); | |
| 227 } | |
| 228 | |
| 229 protected function _setLink(DOMDocument $dom, DOMElement $root) | |
| 230 { | |
| 231 if (!$this->getDataContainer()->getLink()) { | |
| 232 return; | |
| 233 } | |
| 234 $link = $dom->createElement('link'); | |
| 235 $root->appendChild($link); | |
| 236 $link->setAttribute('rel', 'alternate'); | |
| 237 $link->setAttribute('type', 'text/html'); | |
| 238 $link->setAttribute('href', $this->getDataContainer()->getLink()); | |
| 239 } | |
| 240 | |
| 241 /** | |
| 242 * Set entry identifier | |
| 243 * | |
| 244 * @param DOMDocument $dom | |
| 245 * @param DOMElement $root | |
| 246 * @return void | |
| 247 * @throws Writer\Exception\InvalidArgumentException | |
| 248 */ | |
| 249 protected function _setId(DOMDocument $dom, DOMElement $root) | |
| 250 { | |
| 251 if (!$this->getDataContainer()->getId() | |
| 252 && !$this->getDataContainer()->getLink()) { | |
| 253 $message = 'Atom 1.0 entry elements MUST contain exactly one ' | |
| 254 . 'atom:id element, or as an alternative, we can use the same ' | |
| 255 . 'value as atom:link however neither a suitable link nor an ' | |
| 256 . 'id have been set'; | |
| 257 $exception = new Writer\Exception\InvalidArgumentException($message); | |
| 258 if (!$this->ignoreExceptions) { | |
| 259 throw $exception; | |
| 260 } else { | |
| 261 $this->exceptions[] = $exception; | |
| 262 return; | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 if (!$this->getDataContainer()->getId()) { | |
| 267 $this->getDataContainer()->setId( | |
| 268 $this->getDataContainer()->getLink() | |
| 269 ); | |
| 270 } | |
| 271 if (!Uri::factory($this->getDataContainer()->getId())->isValid() | |
| 272 && !preg_match( | |
| 273 "#^urn:[a-zA-Z0-9][a-zA-Z0-9\-]{1,31}:([a-zA-Z0-9\(\)\+\,\.\:\=\@\;\$\_\!\*\-]|%[0-9a-fA-F]{2})*#", | |
| 274 $this->getDataContainer()->getId() | |
| 275 ) | |
| 276 && !$this->_validateTagUri($this->getDataContainer()->getId()) | |
| 277 ) { | |
| 278 throw new Writer\Exception\InvalidArgumentException('Atom 1.0 IDs must be a valid URI/IRI'); | |
| 279 } | |
| 280 $id = $dom->createElement('id'); | |
| 281 $root->appendChild($id); | |
| 282 $text = $dom->createTextNode($this->getDataContainer()->getId()); | |
| 283 $id->appendChild($text); | |
| 284 } | |
| 285 | |
| 286 /** | |
| 287 * Validate a URI using the tag scheme (RFC 4151) | |
| 288 * | |
| 289 * @param string $id | |
| 290 * @return bool | |
| 291 */ | |
| 292 protected function _validateTagUri($id) | |
| 293 { | |
| 294 if (preg_match( | |
| 295 '/^tag:(?P<name>.*),(?P<date>\d{4}-?\d{0,2}-?\d{0,2}):(?P<specific>.*)(.*:)*$/', | |
| 296 $id, | |
| 297 $matches | |
| 298 )) { | |
| 299 $dvalid = false; | |
| 300 $date = $matches['date']; | |
| 301 $d6 = strtotime($date); | |
| 302 if ((strlen($date) == 4) && $date <= date('Y')) { | |
| 303 $dvalid = true; | |
| 304 } elseif ((strlen($date) == 7) && ($d6 < strtotime("now"))) { | |
| 305 $dvalid = true; | |
| 306 } elseif ((strlen($date) == 10) && ($d6 < strtotime("now"))) { | |
| 307 $dvalid = true; | |
| 308 } | |
| 309 $validator = new Validator\EmailAddress; | |
| 310 if ($validator->isValid($matches['name'])) { | |
| 311 $nvalid = true; | |
| 312 } else { | |
| 313 $nvalid = $validator->isValid('info@' . $matches['name']); | |
| 314 } | |
| 315 return $dvalid && $nvalid; | |
| 316 } | |
| 317 return false; | |
| 318 } | |
| 319 | |
| 320 /** | |
| 321 * Set entry content | |
| 322 * | |
| 323 * @param DOMDocument $dom | |
| 324 * @param DOMElement $root | |
| 325 * @return void | |
| 326 * @throws Writer\Exception\InvalidArgumentException | |
| 327 */ | |
| 328 protected function _setContent(DOMDocument $dom, DOMElement $root) | |
| 329 { | |
| 330 $content = $this->getDataContainer()->getContent(); | |
| 331 if (!$content && !$this->getDataContainer()->getLink()) { | |
| 332 $message = 'Atom 1.0 entry elements MUST contain exactly one ' | |
| 333 . 'atom:content element, or as an alternative, at least one link ' | |
| 334 . 'with a rel attribute of "alternate" to indicate an alternate ' | |
| 335 . 'method to consume the content.'; | |
| 336 $exception = new Writer\Exception\InvalidArgumentException($message); | |
| 337 if (!$this->ignoreExceptions) { | |
| 338 throw $exception; | |
| 339 } else { | |
| 340 $this->exceptions[] = $exception; | |
| 341 return; | |
| 342 } | |
| 343 } | |
| 344 if (!$content) { | |
| 345 return; | |
| 346 } | |
| 347 $element = $dom->createElement('content'); | |
| 348 $element->setAttribute('type', 'xhtml'); | |
| 349 $xhtmlElement = $this->_loadXhtml($content); | |
| 350 $deep = version_compare(PHP_VERSION, '7', 'ge') ? 1 : true; | |
| 351 $xhtml = $dom->importNode($xhtmlElement, $deep); | |
| 352 $element->appendChild($xhtml); | |
| 353 $root->appendChild($element); | |
| 354 } | |
| 355 | |
| 356 /** | |
| 357 * Load a HTML string and attempt to normalise to XML | |
| 358 */ | |
| 359 protected function _loadXhtml($content) | |
| 360 { | |
| 361 if (class_exists('tidy', false)) { | |
| 362 $tidy = new \tidy; | |
| 363 $config = [ | |
| 364 'output-xhtml' => true, | |
| 365 'show-body-only' => true, | |
| 366 'quote-nbsp' => false | |
| 367 ]; | |
| 368 $encoding = str_replace('-', '', $this->getEncoding()); | |
| 369 $tidy->parseString($content, $config, $encoding); | |
| 370 $tidy->cleanRepair(); | |
| 371 $xhtml = (string) $tidy; | |
| 372 } else { | |
| 373 $xhtml = $content; | |
| 374 } | |
| 375 $xhtml = preg_replace([ | |
| 376 "/(<[\/]?)([a-zA-Z]+)/" | |
| 377 ], '$1xhtml:$2', $xhtml); | |
| 378 $dom = new DOMDocument('1.0', $this->getEncoding()); | |
| 379 $dom->loadXML( | |
| 380 '<xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml">' | |
| 381 . $xhtml | |
| 382 . '</xhtml:div>' | |
| 383 ); | |
| 384 return $dom->documentElement; | |
| 385 } | |
| 386 | |
| 387 /** | |
| 388 * Set entry categories | |
| 389 * | |
| 390 * @param DOMDocument $dom | |
| 391 * @param DOMElement $root | |
| 392 * @return void | |
| 393 */ | |
| 394 protected function _setCategories(DOMDocument $dom, DOMElement $root) | |
| 395 { | |
| 396 $categories = $this->getDataContainer()->getCategories(); | |
| 397 if (!$categories) { | |
| 398 return; | |
| 399 } | |
| 400 foreach ($categories as $cat) { | |
| 401 $category = $dom->createElement('category'); | |
| 402 $category->setAttribute('term', $cat['term']); | |
| 403 if (isset($cat['label'])) { | |
| 404 $category->setAttribute('label', $cat['label']); | |
| 405 } else { | |
| 406 $category->setAttribute('label', $cat['term']); | |
| 407 } | |
| 408 if (isset($cat['scheme'])) { | |
| 409 $category->setAttribute('scheme', $cat['scheme']); | |
| 410 } | |
| 411 $root->appendChild($category); | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 /** | |
| 416 * Append Source element (Atom 1.0 Feed Metadata) | |
| 417 * | |
| 418 * @param DOMDocument $dom | |
| 419 * @param DOMElement $root | |
| 420 * @return void | |
| 421 */ | |
| 422 protected function _setSource(DOMDocument $dom, DOMElement $root) | |
| 423 { | |
| 424 $source = $this->getDataContainer()->getSource(); | |
| 425 if (!$source) { | |
| 426 return; | |
| 427 } | |
| 428 $renderer = new Renderer\Feed\AtomSource($source); | |
| 429 $renderer->setType($this->getType()); | |
| 430 $element = $renderer->render()->getElement(); | |
| 431 $imported = $dom->importNode($element, true); | |
| 432 $root->appendChild($imported); | |
| 433 } | |
| 434 } |
