1: <?php
2:
3: /**
4: * Base feed class for generating syndication feeds
5: *
6: * @package alpha::util::feeds
7: * @since 1.0
8: * @author John Collins <dev@alphaframework.org>
9: * @version $Id: AlphaFeed.inc 1624 2012-12-21 12:17:55Z alphadevx $
10: * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
11: * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
12: * All rights reserved.
13: *
14: * <pre>
15: * Redistribution and use in source and binary forms, with or
16: * without modification, are permitted provided that the
17: * following conditions are met:
18: *
19: * * Redistributions of source code must retain the above
20: * copyright notice, this list of conditions and the
21: * following disclaimer.
22: * * Redistributions in binary form must reproduce the above
23: * copyright notice, this list of conditions and the
24: * following disclaimer in the documentation and/or other
25: * materials provided with the distribution.
26: * * Neither the name of the Alpha Framework nor the names
27: * of its contributors may be used to endorse or promote
28: * products derived from this software without specific
29: * prior written permission.
30: *
31: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
42: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44: * </pre>
45: *
46: */
47: abstract class AlphaFeed {
48: /**
49: * The DOMDocument object used to create the feed
50: *
51: * @var DOMDocument
52: * @since 1.0
53: */
54: protected $rssDoc;
55:
56: /**
57: * The DOMElement object used to hold the item or entry elements
58: *
59: * @var DOMElement
60: * @since 1.0
61: */
62: protected $docElement;
63:
64: /**
65: * Holds the DOMElement to which metadata is added for the feed
66: *
67: * @var DOMElement
68: * @since 1.0
69: */
70: protected $root;
71:
72: /**
73: * The actual root tag used in each feed type
74: *
75: * @var string
76: * @since 1.0
77: */
78: protected $rootTag;
79:
80: /**
81: * An array of feed items
82: *
83: * @var array
84: * @since 1.0
85: */
86: protected $items;
87:
88: /**
89: * If the feed format has a channel or not
90: *
91: * @var boolean
92: * @since 1.0
93: */
94: protected $hasChannel = true;
95:
96: /**
97: * Maps the tags to the feed-specific tags
98: *
99: * @var array
100: * @since 1.0
101: */
102: protected $tagMap = array('item'=>'item','feeddesc'=>'description','itemdesc'=>'description');
103:
104: /**
105: * The BO which we will serve up in this feed
106: *
107: * @var AlphaDAO
108: * @since 1.0
109: */
110: private $BO;
111:
112: /**
113: * An array containing BO field names -> RSS field name mappings
114: *
115: * @var array
116: * @since 1.0
117: */
118: protected $fieldNameMappings;
119:
120: /**
121: * The XML namespace to use in the generated feed
122: *
123: * @var string
124: */
125: protected $nameSpace;
126:
127: /**
128: * Trace logger
129: *
130: * @var Logger
131: * @since 1.0
132: */
133: private static $logger = null;
134:
135: /**
136: * The constructor
137: *
138: * @param string $BOName The classname of the BO to render a feed for.
139: * @param string $title The title of the feed.
140: * @param string $url The base URL for the feed.
141: * @param string $description The description of the feed.
142: * @param string $pubDate The publish date, only used in Atom feeds.
143: * @param integer $id The feed id, only used in Atom feeds.
144: * @param integer $limit The amount of items to render in the feed.
145: * @throws IllegalArguementException
146: * @since 1.0
147: */
148: public function __construct($BOName, $title, $url, $description, $pubDate = null, $id = null, $limit = 10) {
149: self::$logger = new Logger('AlphaFeed');
150: self::$logger->debug('>>__construct(BOName=['.$BOName.'], title=['.$title.'], url=['.$url.'], description=['.$description.'], pubDate=['.$pubDate.'], id=['.$id.'], limit=['.$limit.'])');
151:
152: $this->rssDoc = new DOMDocument();
153: $this->rssDoc->loadXML($this->rootTag);
154: $this->docElement = $this->rssDoc->documentElement;
155:
156: try {
157: AlphaDAO::loadClassDef($BOName);
158: $this->BO = new $BOName;
159: }catch (IllegalArguementException $e) {
160: self::$logger->error('Unable to load the class definition for the class ['.$BOName.'] while trying to generate a feed!');
161: throw $e;
162: }
163:
164: if ($this->hasChannel) {
165: $root = $this->createFeedElement('channel');
166: $this->root = $this->docElement->appendChild($root);
167: }else{
168: $this->root = $this->docElement;
169: }
170:
171: $this->createRSSNode('feed', $this->root, $title, $url, $description, $pubDate, $id);
172:
173: self::$logger->debug('<<__construct');
174: }
175:
176: /**
177: * Method to load all of the BO items to the feed from the database, from the newest to the
178: * $limit provided
179: *
180: * @param integer $limit The amount of items to render in the feed.
181: * @param string $sortBy The name of the field to sort the feed by.
182: * @since 1.0
183: */
184: public function loadBOs($limit, $sortBy) {
185:
186: $BOs = $this->BO->loadAll(0, $limit, $sortBy, 'DESC');
187:
188: AlphaDAO::disconnect();
189:
190: foreach($BOs as $BO) {
191: $this->addBO($BO);
192: }
193: }
194:
195: /**
196: * Method for adding a BO to the current feed
197: *
198: * @param AlphaDAO $BO
199: */
200: public function addBO($BO) {
201: $title = $BO->get($this->fieldNameMappings['title']);
202: $url = $BO->get($this->fieldNameMappings['url']);
203:
204: if(isset($this->fieldNameMappings['description'])) {
205: $description = $BO->get($this->fieldNameMappings['description']);
206: }else{
207: $description = '';
208: }
209:
210: if(isset($this->fieldNameMappings['pubDate'])) {
211: $dateTS = strtotime($BO->get($this->fieldNameMappings['pubDate']));
212: $pubDate = date(DATE_ATOM, $dateTS);
213: }else{
214: $pubDate = '';
215: }
216:
217: if(isset($this->fieldNameMappings['id']))
218: $id = $BO->get($this->fieldNameMappings['id']);
219: else
220: $id = '';
221:
222: $this->addItem($title, $url, $description, $pubDate, $id);
223: }
224:
225: /**
226: * Method for mapping BO fieldnames to feed field names
227: *
228: * @param string $title The title of the feed.
229: * @param string $url The base URL for the feed.
230: * @param string $description The description of the feed.
231: * @param string $pubDate The publish date, only used in Atom feeds.
232: * @param integer $id The feed id, only used in Atom feeds.
233: * @since 1.0
234: */
235: public function setFieldMappings($title, $url, $description=null, $pubDate=null, $id=null) {
236: $this->fieldNameMappings = array(
237: 'title' => $title,
238: 'url' => $url
239: );
240:
241: if(isset($description))
242: $this->fieldNameMappings['description'] = $description;
243:
244: if(isset($pubDate))
245: $this->fieldNameMappings['pubDate'] = $pubDate;
246:
247: if(isset($id))
248: $this->fieldNameMappings['id'] = $id;
249: }
250:
251: /**
252: * Method for creating a new feed element
253: *
254: * @param string $name The name of the element.
255: * @param string $value The value of the element.
256: * @return DOMElement
257: * @since 1.0
258: */
259: protected function createFeedElement($name, $value=null) {
260: $value = htmlspecialchars($value);
261:
262: if($this->nameSpace == null) {
263: return $this->rssDoc->createElement($name, $value);
264: }else{
265: return $this->rssDoc->createElementNS($this->nameSpace, $name, $value);
266: }
267: }
268:
269: /**
270: * Method for creating link elements (note that Atom has a different format)
271: *
272: * @param DOMElement $parent The parent element.
273: * @param string $url The URL for the link.
274: * @since 1.0
275: */
276: protected function createLink($parent, $url) {
277: $link = $this->createFeedElement('link', $url);
278: $parent->appendChild($link);
279: }
280:
281: /**
282: * Method for creating an RSS node with a title, url and description
283: *
284: * @param integer $type Can be either (item|feed) to indicate the type of node we are creating.
285: * @param DOMElement $parent The parent element.
286: * @param string $title The title of the feed.
287: * @param string $url The base URL for the feed.
288: * @param string $description The description of the feed.
289: * @param string $pubDate The publish date, only used in Atom feeds.
290: * @param integer $id The feed id, only used in Atom feeds.
291: * @since 1.0
292: * @throws IllegalArguementException
293: */
294: protected function createRSSNode($type, $parent, $title, $url, $description, $pubDate=null, $id = null) {
295: $this->createLink($parent, $url);
296: $title = $this->createFeedElement('title', $title);
297: $parent->appendChild($title);
298:
299: if ($type == 'item') {
300: $titletag = $this->tagMap['itemdesc'];
301: }else if ($type == 'feed') {
302: $titletag = $this->tagMap['feeddesc'];
303: }else{
304: throw new IllegalArguementException('The type paramater ['.$type.'] provided is invalid!');
305: }
306:
307: $description = $this->createFeedElement($titletag, $description);
308: $parent->appendChild($description);
309:
310: // id elements and updated elements are just for Atom!
311: if ($id != null) {
312: $idnode = $this->createFeedElement('id', $id);
313: $parent->appendChild($idnode);
314: }
315:
316: if ($pubDate != null) {
317: $datenode = $this->createFeedElement('updated', $pubDate);
318: $parent->appendChild($datenode);
319: }
320: }
321:
322: /**
323: * Method for adding an item to a feed
324: *
325: * @param string $title The title of the feed.
326: * @param string $url The base URL for the feed.
327: * @param string $description The description of the feed.
328: * @param string $pubDate The publish date, only used in Atom feeds.
329: * @param integer $id The feed id, only used in Atom feeds.
330: * @since 1.0
331: */
332: protected function addItem($title, $url, $description=null, $pubDate=null, $id=null) {
333: $item = $this->createFeedElement($this->tagMap['item']);
334:
335: if ($this->docElement->appendChild($item)) {
336: $this->createRSSNode('item', $item, $title, $url, $description, $pubDate, $id);
337: }
338: }
339:
340: /**
341: * Returns the formatted XML for the feed as a string
342: *
343: * @return string
344: * @since 1.0
345: */
346: public function render() {
347: if ($this->rssDoc) {
348: $this->rssDoc->formatOutput = true;
349: return $this->rssDoc->saveXML();
350: }else{
351: return '';
352: }
353: }
354: }
355:
356: ?>