1 <?php
2 /**
3 * RSSFeed class
4 *
5 * This class is used to create an RSS feed.
6 * @todo Improve documentation
7 * @package sapphire
8 * @subpackage integration
9 */
10 class RSSFeed extends ViewableData {
11 /**
12 * Casting information for this object's methods.
13 * Let's us use $Title.XML in templates
14 */
15 public static $casting = array(
16 "Title" => "Varchar",
17 "Description" => "Varchar",
18 );
19
20 /**
21 * Holds the feed entries
22 *
23 * @var DataObjectSet
24 */
25 protected $entries;
26
27 /**
28 * Title of the feed
29 *
30 * @var string
31 */
32 protected $title;
33
34 /**
35 * Description of the feed
36 *
37 * @var string
38 */
39 protected $description;
40
41 /**
42 * Link to the feed
43 *
44 * @var string
45 */
46 protected $link;
47
48 /**
49 * Name of the title field of feed entries
50 *
51 * @var string
52 */
53 protected $titleField;
54
55 /**
56 * Name of the description field of feed entries
57 *
58 * @var string
59 */
60 protected $descriptionField;
61
62 /**
63 * Name of the author field of feed entries
64 *
65 * @var string
66 */
67 protected $authorField;
68
69 /**
70 * Last modification of the RSS feed
71 *
72 * @var int Unix timestamp of the last modification
73 */
74 protected $lastModified;
75
76 /**
77 * ETag for the RSS feed (used for client-site caching)
78 *
79 * @var string The value for the HTTP ETag header.
80 */
81 protected $etag;
82
83 /**
84 * Constructor
85 *
86 * @param DataObjectSet $entries RSS feed entries
87 * @param string $link Link to the feed
88 * @param string $title Title of the feed
89 * @param string $description Description of the field
90 * @param string $titleField Name of the field that should be used for the
91 * titles for the feed entries
92 * @param string $descriptionField Name of the field that should be used
93 * for the description for the feed
94 * entries
95 * @param string $authorField Name of the field that should be used for
96 * the author for the feed entries
97 * @param int $lastModified Unix timestamp of the latest modification
98 * (latest posting)
99 * @param string $etag The ETag is an unique identifier that is changed
100 * every time the representation does
101 */
102 function __construct(DataObjectSet $entries, $link, $title,
103 $description = null, $titleField = "Title",
104 $descriptionField = "Content", $authorField = null,
105 $lastModified = null, $etag = null) {
106 $this->entries = $entries;
107 $this->link = $link;
108 $this->description = $description;
109 $this->title = $title;
110
111 $this->titleField = $titleField;
112 $this->descriptionField = $descriptionField;
113 $this->authorField = $authorField;
114
115 $this->lastModified = $lastModified;
116 $this->etag = $etag;
117
118 parent::__construct();
119 }
120
121 /**
122 * Include an link to the feed
123 *
124 * @param string $url URL of the feed
125 * @param string $title Title to show
126 */
127 static function linkToFeed($url, $title = null) {
128 $title = Convert::raw2xml($title);
129 Requirements::insertHeadTags(
130 '<link rel="alternate" type="application/rss+xml" title="' . $title .
131 '" href="' . $url . '" />');
132 }
133
134 /**
135 * Get the RSS feed entries
136 *
137 * @return DataObjectSet Returns the {@link RSSFeed_Entry} objects.
138 */
139 function Entries() {
140 $output = new DataObjectSet();
141 if(isset($this->entries)) {
142 foreach($this->entries as $entry) {
143 $output->push(new RSSFeed_Entry($entry, $this->titleField, $this->descriptionField, $this->authorField));
144 }
145 }
146 return $output;
147 }
148
149 /**
150 * Get the title of thisfeed
151 *
152 * @return string Returns the title of the feed.
153 */
154 function Title() {
155 return $this->title;
156 }
157
158 /**
159 * Get the URL of this feed
160 *
161 * @param string $action
162 * @return string Returns the URL of the feed.
163 */
164 function Link($action = null) {
165 return Controller::join_links(Director::absoluteURL($this->link), $action);
166 }
167
168 /**
169 * Get the description of this feed
170 *
171 * @return string Returns the description of the feed.
172 */
173 function Description() {
174 return $this->description;
175 }
176
177 /**
178 * Output the feed to the browser
179 */
180 function outputToBrowser() {
181 if(is_int($this->lastModified)) {
182 HTTP::register_modification_timestamp($this->lastModified);
183 header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT');
184 }
185 if(!empty($this->etag)) {
186 HTTP::register_etag($this->etag);
187 }
188
189 $body = $this->feedContent();
190 HTTP::add_cache_headers();
191 header("Content-type: text/xml");
192 echo $body;
193 }
194
195 /**
196 * Return the content of the RSS feed
197 */
198 function feedContent() {
199 SSViewer::set_source_file_comments(false);
200 return str_replace(' ', ' ', $this->renderWith('RSSFeed'));
201 }
202 }
203
204 /**
205 * RSSFeed_Entry class
206 *
207 * This class is used for entries of an RSS feed.
208 *
209 * @see RSSFeed
210 * @package sapphire
211 * @subpackage integration
212 */
213 class RSSFeed_Entry extends ViewableData {
214 /**
215 * The object that represents the item, it contains all the data.
216 *
217 * @var mixed
218 */
219 protected $failover;
220
221 /**
222 * Name of the title field of feed entries
223 *
224 * @var string
225 */
226 protected $titleField;
227
228 /**
229 * Name of the description field of feed entries
230 *
231 * @var string
232 */
233 protected $descriptionField;
234
235 /**
236 * Name of the author field of feed entries
237 *
238 * @var string
239 */
240 protected $authorField;
241
242 /**
243 * Create a new RSSFeed entry.
244 */
245 function __construct($entry, $titleField, $descriptionField,
246 $authorField) {
247 $this->failover = $entry;
248 $this->titleField = $titleField;
249 $this->descriptionField = $descriptionField;
250 $this->authorField = $authorField;
251
252 parent::__construct();
253 }
254
255 /**
256 * Get the description of this entry
257 *
258 * @return string Returns the description of the entry.
259 */
260 function Title() {
261 return $this->rssField($this->titleField, 'Varchar');
262 }
263
264 /**
265 * Get the description of this entry
266 *
267 * @return string Returns the description of the entry.
268 */
269 function Description() {
270 return $this->rssField($this->descriptionField, 'Text');
271 }
272
273 /**
274 * Get the author of this entry
275 *
276 * @return string Returns the author of the entry.
277 */
278 function Author() {
279 if($this->authorField) return $this->failover->obj($this->authorField);
280 }
281
282 /**
283 * Return the named field as an obj() call from $this->failover.
284 * Default to the given class if there's no casting information.
285 */
286 function rssField($fieldName, $defaultClass = 'Varchar') {
287 if($fieldName) {
288 if($this->failover->castingHelper($fieldName)) {
289 $value = $this->failover->$fieldName;
290 $obj = $this->failover->obj($fieldName);
291 $obj->setValue($value);
292 return $obj;
293 } else {
294 $obj = new $defaultClass($fieldName);
295 $obj->setValue($this->failover->XML_val($fieldName));
296 return $obj;
297 }
298 }
299 }
300
301 /**
302 * Get a link to this entry
303 *
304 * @return string Returns the URL of this entry
305 */
306 function AbsoluteLink() {
307 if($this->failover->hasMethod('AbsoluteLink')) return $this->failover->AbsoluteLink();
308 else if($this->failover->hasMethod('Link')) return Director::absoluteURL($this->failover->Link());
309 else user_error($this->failover->class . " object has either an AbsoluteLink nor a Link method. Can't put a link in the RSS feed", E_USER_WARNING);
310 }
311 }
312 ?>