|
1 <?php |
|
2 |
|
3 /** |
|
4 * Zend Framework |
|
5 * |
|
6 * LICENSE |
|
7 * |
|
8 * This source file is subject to the new BSD license that is bundled |
|
9 * with this package in the file LICENSE.txt. |
|
10 * It is also available through the world-wide-web at this URL: |
|
11 * http://framework.zend.com/license/new-bsd |
|
12 * If you did not receive a copy of the license and are unable to |
|
13 * obtain it through the world-wide-web, please send an email |
|
14 * to license@zend.com so we can send you a copy immediately. |
|
15 * |
|
16 * @category Zend |
|
17 * @package Zend_Feed |
|
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: Rss.php 20096 2010-01-06 02:05:09Z bkarwin $ |
|
21 */ |
|
22 |
|
23 |
|
24 /** |
|
25 * @see Zend_Feed_Abstract |
|
26 */ |
|
27 require_once 'Zend/Feed/Abstract.php'; |
|
28 |
|
29 /** |
|
30 * @see Zend_Feed_Entry_Rss |
|
31 */ |
|
32 require_once 'Zend/Feed/Entry/Rss.php'; |
|
33 |
|
34 |
|
35 /** |
|
36 * RSS channel class |
|
37 * |
|
38 * The Zend_Feed_Rss class is a concrete subclass of |
|
39 * Zend_Feed_Abstract meant for representing RSS channels. It does not |
|
40 * add any methods to its parent, just provides a classname to check |
|
41 * against with the instanceof operator, and expects to be handling |
|
42 * RSS-formatted data instead of Atom. |
|
43 * |
|
44 * @category Zend |
|
45 * @package Zend_Feed |
|
46 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
47 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
48 */ |
|
49 class Zend_Feed_Rss extends Zend_Feed_Abstract |
|
50 { |
|
51 /** |
|
52 * The classname for individual channel elements. |
|
53 * |
|
54 * @var string |
|
55 */ |
|
56 protected $_entryClassName = 'Zend_Feed_Entry_Rss'; |
|
57 |
|
58 /** |
|
59 * The element name for individual channel elements (RSS <item>s). |
|
60 * |
|
61 * @var string |
|
62 */ |
|
63 protected $_entryElementName = 'item'; |
|
64 |
|
65 /** |
|
66 * The default namespace for RSS channels. |
|
67 * |
|
68 * @var string |
|
69 */ |
|
70 protected $_defaultNamespace = 'rss'; |
|
71 |
|
72 /** |
|
73 * Override Zend_Feed_Abstract to set up the $_element and $_entries aliases. |
|
74 * |
|
75 * @return void |
|
76 * @throws Zend_Feed_Exception |
|
77 */ |
|
78 public function __wakeup() |
|
79 { |
|
80 parent::__wakeup(); |
|
81 |
|
82 // Find the base channel element and create an alias to it. |
|
83 $rdfTags = $this->_element->getElementsByTagNameNS('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'RDF'); |
|
84 if ($rdfTags->length != 0) { |
|
85 $this->_element = $rdfTags->item(0); |
|
86 } else { |
|
87 $this->_element = $this->_element->getElementsByTagName('channel')->item(0); |
|
88 } |
|
89 if (!$this->_element) { |
|
90 /** |
|
91 * @see Zend_Feed_Exception |
|
92 */ |
|
93 require_once 'Zend/Feed/Exception.php'; |
|
94 throw new Zend_Feed_Exception('No root <channel> element found, cannot parse channel.'); |
|
95 } |
|
96 |
|
97 // Find the entries and save a pointer to them for speed and |
|
98 // simplicity. |
|
99 $this->_buildEntryCache(); |
|
100 } |
|
101 |
|
102 |
|
103 /** |
|
104 * Make accessing some individual elements of the channel easier. |
|
105 * |
|
106 * Special accessors 'item' and 'items' are provided so that if |
|
107 * you wish to iterate over an RSS channel's items, you can do so |
|
108 * using foreach ($channel->items as $item) or foreach |
|
109 * ($channel->item as $item). |
|
110 * |
|
111 * @param string $var The property to access. |
|
112 * @return mixed |
|
113 */ |
|
114 public function __get($var) |
|
115 { |
|
116 switch ($var) { |
|
117 case 'item': |
|
118 // fall through to the next case |
|
119 case 'items': |
|
120 return $this; |
|
121 |
|
122 default: |
|
123 return parent::__get($var); |
|
124 } |
|
125 } |
|
126 |
|
127 /** |
|
128 * Generate the header of the feed when working in write mode |
|
129 * |
|
130 * @param array $array the data to use |
|
131 * @return DOMElement root node |
|
132 */ |
|
133 protected function _mapFeedHeaders($array) |
|
134 { |
|
135 $channel = $this->_element->createElement('channel'); |
|
136 |
|
137 $title = $this->_element->createElement('title'); |
|
138 $title->appendChild($this->_element->createCDATASection($array->title)); |
|
139 $channel->appendChild($title); |
|
140 |
|
141 $link = $this->_element->createElement('link', $array->link); |
|
142 $channel->appendChild($link); |
|
143 |
|
144 $desc = isset($array->description) ? $array->description : ''; |
|
145 $description = $this->_element->createElement('description'); |
|
146 $description->appendChild($this->_element->createCDATASection($desc)); |
|
147 $channel->appendChild($description); |
|
148 |
|
149 $pubdate = isset($array->lastUpdate) ? $array->lastUpdate : time(); |
|
150 $pubdate = $this->_element->createElement('pubDate', date(DATE_RSS, $pubdate)); |
|
151 $channel->appendChild($pubdate); |
|
152 |
|
153 if (isset($array->published)) { |
|
154 $lastBuildDate = $this->_element->createElement('lastBuildDate', date(DATE_RSS, $array->published)); |
|
155 $channel->appendChild($lastBuildDate); |
|
156 } |
|
157 |
|
158 $editor = ''; |
|
159 if (!empty($array->email)) { |
|
160 $editor .= $array->email; |
|
161 } |
|
162 if (!empty($array->author)) { |
|
163 $editor .= ' (' . $array->author . ')'; |
|
164 } |
|
165 if (!empty($editor)) { |
|
166 $author = $this->_element->createElement('managingEditor', ltrim($editor)); |
|
167 $channel->appendChild($author); |
|
168 } |
|
169 if (isset($array->webmaster)) { |
|
170 $channel->appendChild($this->_element->createElement('webMaster', $array->webmaster)); |
|
171 } |
|
172 |
|
173 if (!empty($array->copyright)) { |
|
174 $copyright = $this->_element->createElement('copyright', $array->copyright); |
|
175 $channel->appendChild($copyright); |
|
176 } |
|
177 |
|
178 if (isset($array->category)) { |
|
179 $category = $this->_element->createElement('category', $array->category); |
|
180 $channel->appendChild($category); |
|
181 } |
|
182 |
|
183 if (!empty($array->image)) { |
|
184 $image = $this->_element->createElement('image'); |
|
185 $url = $this->_element->createElement('url', $array->image); |
|
186 $image->appendChild($url); |
|
187 $imagetitle = $this->_element->createElement('title'); |
|
188 $imagetitle->appendChild($this->_element->createCDATASection($array->title)); |
|
189 $image->appendChild($imagetitle); |
|
190 $imagelink = $this->_element->createElement('link', $array->link); |
|
191 $image->appendChild($imagelink); |
|
192 |
|
193 $channel->appendChild($image); |
|
194 } |
|
195 |
|
196 $generator = !empty($array->generator) ? $array->generator : 'Zend_Feed'; |
|
197 $generator = $this->_element->createElement('generator', $generator); |
|
198 $channel->appendChild($generator); |
|
199 |
|
200 if (!empty($array->language)) { |
|
201 $language = $this->_element->createElement('language', $array->language); |
|
202 $channel->appendChild($language); |
|
203 } |
|
204 |
|
205 $doc = $this->_element->createElement('docs', 'http://blogs.law.harvard.edu/tech/rss'); |
|
206 $channel->appendChild($doc); |
|
207 |
|
208 if (isset($array->cloud)) { |
|
209 $cloud = $this->_element->createElement('cloud'); |
|
210 $cloud->setAttribute('domain', $array->cloud['uri']->getHost()); |
|
211 $cloud->setAttribute('port', $array->cloud['uri']->getPort()); |
|
212 $cloud->setAttribute('path', $array->cloud['uri']->getPath()); |
|
213 $cloud->setAttribute('registerProcedure', $array->cloud['procedure']); |
|
214 $cloud->setAttribute('protocol', $array->cloud['protocol']); |
|
215 $channel->appendChild($cloud); |
|
216 } |
|
217 |
|
218 if (isset($array->ttl)) { |
|
219 $ttl = $this->_element->createElement('ttl', $array->ttl); |
|
220 $channel->appendChild($ttl); |
|
221 } |
|
222 |
|
223 if (isset($array->rating)) { |
|
224 $rating = $this->_element->createElement('rating', $array->rating); |
|
225 $channel->appendChild($rating); |
|
226 } |
|
227 |
|
228 if (isset($array->textInput)) { |
|
229 $textinput = $this->_element->createElement('textInput'); |
|
230 $textinput->appendChild($this->_element->createElement('title', $array->textInput['title'])); |
|
231 $textinput->appendChild($this->_element->createElement('description', $array->textInput['description'])); |
|
232 $textinput->appendChild($this->_element->createElement('name', $array->textInput['name'])); |
|
233 $textinput->appendChild($this->_element->createElement('link', $array->textInput['link'])); |
|
234 $channel->appendChild($textinput); |
|
235 } |
|
236 |
|
237 if (isset($array->skipHours)) { |
|
238 $skipHours = $this->_element->createElement('skipHours'); |
|
239 foreach ($array->skipHours as $hour) { |
|
240 $skipHours->appendChild($this->_element->createElement('hour', $hour)); |
|
241 } |
|
242 $channel->appendChild($skipHours); |
|
243 } |
|
244 |
|
245 if (isset($array->skipDays)) { |
|
246 $skipDays = $this->_element->createElement('skipDays'); |
|
247 foreach ($array->skipDays as $day) { |
|
248 $skipDays->appendChild($this->_element->createElement('day', $day)); |
|
249 } |
|
250 $channel->appendChild($skipDays); |
|
251 } |
|
252 |
|
253 if (isset($array->itunes)) { |
|
254 $this->_buildiTunes($channel, $array); |
|
255 } |
|
256 |
|
257 return $channel; |
|
258 } |
|
259 |
|
260 /** |
|
261 * Adds the iTunes extensions to a root node |
|
262 * |
|
263 * @param DOMElement $root |
|
264 * @param array $array |
|
265 * @return void |
|
266 */ |
|
267 private function _buildiTunes(DOMElement $root, $array) |
|
268 { |
|
269 /* author node */ |
|
270 $author = ''; |
|
271 if (isset($array->itunes->author)) { |
|
272 $author = $array->itunes->author; |
|
273 } elseif (isset($array->author)) { |
|
274 $author = $array->author; |
|
275 } |
|
276 if (!empty($author)) { |
|
277 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:author', $author); |
|
278 $root->appendChild($node); |
|
279 } |
|
280 |
|
281 /* owner node */ |
|
282 $author = ''; |
|
283 $email = ''; |
|
284 if (isset($array->itunes->owner)) { |
|
285 if (isset($array->itunes->owner['name'])) { |
|
286 $author = $array->itunes->owner['name']; |
|
287 } |
|
288 if (isset($array->itunes->owner['email'])) { |
|
289 $email = $array->itunes->owner['email']; |
|
290 } |
|
291 } |
|
292 if (empty($author) && isset($array->author)) { |
|
293 $author = $array->author; |
|
294 } |
|
295 if (empty($email) && isset($array->email)) { |
|
296 $email = $array->email; |
|
297 } |
|
298 if (!empty($author) || !empty($email)) { |
|
299 $owner = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:owner'); |
|
300 if (!empty($author)) { |
|
301 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:name', $author); |
|
302 $owner->appendChild($node); |
|
303 } |
|
304 if (!empty($email)) { |
|
305 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:email', $email); |
|
306 $owner->appendChild($node); |
|
307 } |
|
308 $root->appendChild($owner); |
|
309 } |
|
310 $image = ''; |
|
311 if (isset($array->itunes->image)) { |
|
312 $image = $array->itunes->image; |
|
313 } elseif (isset($array->image)) { |
|
314 $image = $array->image; |
|
315 } |
|
316 if (!empty($image)) { |
|
317 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:image'); |
|
318 $node->setAttribute('href', $image); |
|
319 $root->appendChild($node); |
|
320 } |
|
321 $subtitle = ''; |
|
322 if (isset($array->itunes->subtitle)) { |
|
323 $subtitle = $array->itunes->subtitle; |
|
324 } elseif (isset($array->description)) { |
|
325 $subtitle = $array->description; |
|
326 } |
|
327 if (!empty($subtitle)) { |
|
328 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:subtitle', $subtitle); |
|
329 $root->appendChild($node); |
|
330 } |
|
331 $summary = ''; |
|
332 if (isset($array->itunes->summary)) { |
|
333 $summary = $array->itunes->summary; |
|
334 } elseif (isset($array->description)) { |
|
335 $summary = $array->description; |
|
336 } |
|
337 if (!empty($summary)) { |
|
338 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:summary', $summary); |
|
339 $root->appendChild($node); |
|
340 } |
|
341 if (isset($array->itunes->block)) { |
|
342 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:block', $array->itunes->block); |
|
343 $root->appendChild($node); |
|
344 } |
|
345 if (isset($array->itunes->explicit)) { |
|
346 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:explicit', $array->itunes->explicit); |
|
347 $root->appendChild($node); |
|
348 } |
|
349 if (isset($array->itunes->keywords)) { |
|
350 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:keywords', $array->itunes->keywords); |
|
351 $root->appendChild($node); |
|
352 } |
|
353 if (isset($array->itunes->new_feed_url)) { |
|
354 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:new-feed-url', $array->itunes->new_feed_url); |
|
355 $root->appendChild($node); |
|
356 } |
|
357 if (isset($array->itunes->category)) { |
|
358 foreach ($array->itunes->category as $i => $category) { |
|
359 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category'); |
|
360 $node->setAttribute('text', $category['main']); |
|
361 $root->appendChild($node); |
|
362 $add_end_category = false; |
|
363 if (!empty($category['sub'])) { |
|
364 $add_end_category = true; |
|
365 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category'); |
|
366 $node->setAttribute('text', $category['sub']); |
|
367 $root->appendChild($node); |
|
368 } |
|
369 if ($i > 0 || $add_end_category) { |
|
370 $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category'); |
|
371 $root->appendChild($node); |
|
372 } |
|
373 } |
|
374 } |
|
375 } |
|
376 |
|
377 /** |
|
378 * Generate the entries of the feed when working in write mode |
|
379 * |
|
380 * The following nodes are constructed for each feed entry |
|
381 * <item> |
|
382 * <title>entry title</title> |
|
383 * <link>url to feed entry</link> |
|
384 * <guid>url to feed entry</guid> |
|
385 * <description>short text</description> |
|
386 * <content:encoded>long version, can contain html</content:encoded> |
|
387 * </item> |
|
388 * |
|
389 * @param DOMElement $root the root node to use |
|
390 * @param array $array the data to use |
|
391 * @return void |
|
392 */ |
|
393 protected function _mapFeedEntries(DOMElement $root, $array) |
|
394 { |
|
395 Zend_Feed::registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/'); |
|
396 |
|
397 foreach ($array as $dataentry) { |
|
398 $item = $this->_element->createElement('item'); |
|
399 |
|
400 $title = $this->_element->createElement('title'); |
|
401 $title->appendChild($this->_element->createCDATASection($dataentry->title)); |
|
402 $item->appendChild($title); |
|
403 |
|
404 if (isset($dataentry->author)) { |
|
405 $author = $this->_element->createElement('author', $dataentry->author); |
|
406 $item->appendChild($author); |
|
407 } |
|
408 |
|
409 $link = $this->_element->createElement('link', $dataentry->link); |
|
410 $item->appendChild($link); |
|
411 |
|
412 if (isset($dataentry->guid)) { |
|
413 $guid = $this->_element->createElement('guid', $dataentry->guid); |
|
414 if (!Zend_Uri::check($dataentry->guid)) { |
|
415 $guid->setAttribute('isPermaLink', 'false'); |
|
416 } |
|
417 $item->appendChild($guid); |
|
418 } |
|
419 |
|
420 $description = $this->_element->createElement('description'); |
|
421 $description->appendChild($this->_element->createCDATASection($dataentry->description)); |
|
422 $item->appendChild($description); |
|
423 |
|
424 if (isset($dataentry->content)) { |
|
425 $content = $this->_element->createElement('content:encoded'); |
|
426 $content->appendChild($this->_element->createCDATASection($dataentry->content)); |
|
427 $item->appendChild($content); |
|
428 } |
|
429 |
|
430 $pubdate = isset($dataentry->lastUpdate) ? $dataentry->lastUpdate : time(); |
|
431 $pubdate = $this->_element->createElement('pubDate', date(DATE_RSS, $pubdate)); |
|
432 $item->appendChild($pubdate); |
|
433 |
|
434 if (isset($dataentry->category)) { |
|
435 foreach ($dataentry->category as $category) { |
|
436 $node = $this->_element->createElement('category', $category['term']); |
|
437 if (isset($category['scheme'])) { |
|
438 $node->setAttribute('domain', $category['scheme']); |
|
439 } |
|
440 $item->appendChild($node); |
|
441 } |
|
442 } |
|
443 |
|
444 if (isset($dataentry->source)) { |
|
445 $source = $this->_element->createElement('source', $dataentry->source['title']); |
|
446 $source->setAttribute('url', $dataentry->source['url']); |
|
447 $item->appendChild($source); |
|
448 } |
|
449 |
|
450 if (isset($dataentry->comments)) { |
|
451 $comments = $this->_element->createElement('comments', $dataentry->comments); |
|
452 $item->appendChild($comments); |
|
453 } |
|
454 if (isset($dataentry->commentRss)) { |
|
455 $comments = $this->_element->createElementNS('http://wellformedweb.org/CommentAPI/', |
|
456 'wfw:commentRss', |
|
457 $dataentry->commentRss); |
|
458 $item->appendChild($comments); |
|
459 } |
|
460 |
|
461 |
|
462 if (isset($dataentry->enclosure)) { |
|
463 foreach ($dataentry->enclosure as $enclosure) { |
|
464 $node = $this->_element->createElement('enclosure'); |
|
465 $node->setAttribute('url', $enclosure['url']); |
|
466 if (isset($enclosure['type'])) { |
|
467 $node->setAttribute('type', $enclosure['type']); |
|
468 } |
|
469 if (isset($enclosure['length'])) { |
|
470 $node->setAttribute('length', $enclosure['length']); |
|
471 } |
|
472 $item->appendChild($node); |
|
473 } |
|
474 } |
|
475 |
|
476 $root->appendChild($item); |
|
477 } |
|
478 } |
|
479 |
|
480 /** |
|
481 * Override Zend_Feed_Element to include <rss> root node |
|
482 * |
|
483 * @return string |
|
484 */ |
|
485 public function saveXml() |
|
486 { |
|
487 // Return a complete document including XML prologue. |
|
488 $doc = new DOMDocument($this->_element->ownerDocument->version, |
|
489 $this->_element->ownerDocument->actualEncoding); |
|
490 $root = $doc->createElement('rss'); |
|
491 |
|
492 // Use rss version 2.0 |
|
493 $root->setAttribute('version', '2.0'); |
|
494 |
|
495 // Content namespace |
|
496 $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:content', 'http://purl.org/rss/1.0/modules/content/'); |
|
497 $root->appendChild($doc->importNode($this->_element, true)); |
|
498 |
|
499 // Append root node |
|
500 $doc->appendChild($root); |
|
501 |
|
502 // Format output |
|
503 $doc->formatOutput = true; |
|
504 |
|
505 return $doc->saveXML(); |
|
506 } |
|
507 |
|
508 /** |
|
509 * Send feed to a http client with the correct header |
|
510 * |
|
511 * @return void |
|
512 * @throws Zend_Feed_Exception if headers have already been sent |
|
513 */ |
|
514 public function send() |
|
515 { |
|
516 if (headers_sent()) { |
|
517 /** |
|
518 * @see Zend_Feed_Exception |
|
519 */ |
|
520 require_once 'Zend/Feed/Exception.php'; |
|
521 throw new Zend_Feed_Exception('Cannot send RSS because headers have already been sent.'); |
|
522 } |
|
523 |
|
524 header('Content-Type: application/rss+xml; charset=' . $this->_element->ownerDocument->actualEncoding); |
|
525 |
|
526 echo $this->saveXml(); |
|
527 } |
|
528 |
|
529 } |