Completed
Push — master ( 10f429...3b71b7 )
by Ingo
11:44
created

RSSFeed::getTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
use SilverStripe\ORM\SS_List;
4
use SilverStripe\ORM\ArrayList;
5
use SilverStripe\ORM\FieldType\DBField;
6
use SilverStripe\ORM\FieldType\DBHTMLText;
7
8
/**
9
 * RSSFeed class
10
 *
11
 * This class is used to create an RSS feed.
12
 * @todo Improve documentation
13
 * @package framework
14
 * @subpackage integration
15
 */
16
class RSSFeed extends ViewableData {
17
18
	/**
19
	 * Casting information for this object's methods.
20
	 * Let's us use $Title.XML in templates
21
	 */
22
	private static $casting = array(
23
		"Title" => "Varchar",
24
		"Description" => "Varchar",
25
		"Link" => "Varchar",
26
	);
27
28
	/**
29
	 * Holds the feed entries
30
	 *
31
	 * @var SS_List
32
	 */
33
	protected $entries;
34
35
	/**
36
	 * Title of the feed
37
	 *
38
	 * @var string
39
	 */
40
	protected $title;
41
42
	/**
43
	 * Description of the feed
44
	 *
45
	 * @var string
46
	 */
47
	protected $description;
48
49
	/**
50
	 * Link to the feed
51
	 *
52
	 * @var string
53
	 */
54
	protected $link;
55
56
	/**
57
	 * Name of the title field of feed entries
58
	 *
59
	 * @var string
60
	 */
61
	protected $titleField;
62
63
	/**
64
	 * Name of the description field of feed entries
65
	 *
66
	 * @var string
67
	 */
68
	protected $descriptionField;
69
70
	/**
71
	 * Name of the author field of feed entries
72
	 *
73
	 * @var string
74
	 */
75
	protected $authorField;
76
77
	/**
78
	 * Last modification of the RSS feed
79
	 *
80
	 * @var int Unix timestamp of the last modification
81
	 */
82
	protected $lastModified;
83
84
	/**
85
	 * ETag for the RSS feed (used for client-site caching)
86
	 *
87
	 * @var string The value for the HTTP ETag header.
88
	 */
89
	protected $etag;
90
91
	/**
92
	 * Custom template
93
	 *
94
	 * @var string
95
	 */
96
	protected $template = null;
97
98
	/**
99
	 * Constructor
100
	 *
101
	 * @param SS_List $entries RSS feed entries
102
	 * @param string $link Link to the feed
103
	 * @param string $title Title of the feed
104
	 * @param string $description Description of the field
105
	 * @param string $titleField Name of the field that should be used for the
106
	 *                           titles for the feed entries
107
	 * @param string $descriptionField Name of the field that should be used
108
	 *                                 for the description for the feed
109
	 *                                 entries
110
	 * @param string $authorField Name of the field that should be used for
111
	 *                            the author for the feed entries
112
	 * @param int $lastModified Unix timestamp of the latest modification
113
	 *                          (latest posting)
114
	 * @param string $etag The ETag is an unique identifier that is changed
115
	 *                         every time the representation does
116
	 */
117
	public function __construct(SS_List $entries, $link, $title,
118
											$description = null, $titleField = "Title",
119
											$descriptionField = "Content", $authorField = null,
120
											$lastModified = null, $etag = null) {
121
		$this->entries = $entries;
122
		$this->link = $link;
123
		$this->description = $description;
124
		$this->title = $title;
125
126
		$this->titleField = $titleField;
127
		$this->descriptionField = $descriptionField;
128
		$this->authorField = $authorField;
129
130
		$this->lastModified = $lastModified;
131
		$this->etag = $etag;
132
133
		parent::__construct();
134
	}
135
136
	/**
137
	 * Include an link to the feed
138
	 *
139
	 * @param string $url URL of the feed
140
	 * @param string $title Title to show
141
	 */
142
	public static function linkToFeed($url, $title = null) {
143
		$title = Convert::raw2xml($title);
144
		Requirements::insertHeadTags(
145
			'<link rel="alternate" type="application/rss+xml" title="' . $title .
146
			'" href="' . $url . '" />');
147
	}
148
149
	/**
150
	 * Get the RSS feed entries
151
	 *
152
	 * @return SS_List Returns the {@link RSSFeed_Entry} objects.
153
	 */
154
	public function Entries() {
155
		$output = new ArrayList();
156
157
		if(isset($this->entries)) {
158
			foreach($this->entries as $entry) {
159
				$output->push(
160
					RSSFeed_Entry::create($entry, $this->titleField, $this->descriptionField, $this->authorField));
161
			}
162
		}
163
		return $output;
164
	}
165
166
	/**
167
	 * Get the title of thisfeed
168
	 *
169
	 * @return string Returns the title of the feed.
170
	 */
171
	public function Title() {
172
		return $this->title;
173
	}
174
175
	/**
176
	 * Get the URL of this feed
177
	 *
178
	 * @param string $action
179
	 * @return string Returns the URL of the feed.
180
	 */
181
	public function Link($action = null) {
182
		return Controller::join_links(Director::absoluteURL($this->link), $action);
183
	}
184
185
	/**
186
	 * Get the description of this feed
187
	 *
188
	 * @return string Returns the description of the feed.
189
	 */
190
	public function Description() {
191
		return $this->description;
192
	}
193
194
	/**
195
	 * Output the feed to the browser.
196
	 *
197
	 * TODO: Pass $response object to ->outputToBrowser() to loosen dependence on global state for easier testing/prototyping so dev can inject custom SS_HTTPResponse instance.
198
	 *
199
	 * @return DBHTMLText
200
	 */
201
	public function outputToBrowser() {
202
		$prevState = Config::inst()->get('SSViewer', 'source_file_comments');
203
		Config::inst()->update('SSViewer', 'source_file_comments', false);
204
205
		$response = Controller::curr()->getResponse();
206
207
		if(is_int($this->lastModified)) {
208
			HTTP::register_modification_timestamp($this->lastModified);
209
			$response->addHeader("Last-Modified", gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT');
210
		}
211
		if(!empty($this->etag)) {
212
			HTTP::register_etag($this->etag);
213
		}
214
215
		if(!headers_sent()) {
216
			HTTP::add_cache_headers();
217
			$response->addHeader("Content-Type", "application/rss+xml; charset=utf-8");
218
		}
219
220
		Config::inst()->update('SSViewer', 'source_file_comments', $prevState);
221
222
		return $this->renderWith($this->getTemplates());
223
	}
224
225
	/**
226
	 * Set the name of the template to use. Actual template will be resolved
227
	 * via the standard template inclusion process.
228
	 *
229
	 * @param string
230
	 */
231
	public function setTemplate($template) {
232
		$this->template = $template;
233
	}
234
235
	/**
236
	 * Returns the name of the template to use.
237
	 *
238
	 * @return string
239
	 */
240
	public function getTemplate() {
241
		return $this->template;
242
	}
243
244
	/**
245
	 * Returns the ordered list of preferred templates for rendering this object.
246
	 * Will prioritise any custom template first, and then templates based on class hiearchy next.
247
	 *
248
	 * @return array
249
	 */
250
	public function getTemplates() {
251
		$templates = SSViewer::get_templates_by_class(get_class($this), '', __CLASS__);
252
		// Prefer any custom template
253
		if($this->getTemplate()) {
254
			array_unshift($templates, $this->getTemplate());
255
		}
256
		return $templates;
257
	}
258
}
259
260
/**
261
 * RSSFeed_Entry class
262
 *
263
 * This class is used for entries of an RSS feed.
264
 *
265
 * @see RSSFeed
266
 * @package framework
267
 * @subpackage integration
268
 */
269
class RSSFeed_Entry extends ViewableData {
270
	/**
271
	 * The object that represents the item, it contains all the data.
272
	 *
273
	 * @var mixed
274
	 */
275
	protected $failover;
276
277
	/**
278
	 * Name of the title field of feed entries
279
	 *
280
	 * @var string
281
	 */
282
	protected $titleField;
283
284
	/**
285
	 * Name of the description field of feed entries
286
	 *
287
	 * @var string
288
	 */
289
	protected $descriptionField;
290
291
	/**
292
	 * Name of the author field of feed entries
293
	 *
294
	 * @var string
295
	 */
296
	protected $authorField;
297
298
	/**
299
	 * Create a new RSSFeed entry.
300
	 * @param ViewableData $entry
301
	 * @param string $titleField
302
	 * @param string $descriptionField
303
	 * @param string $authorField
304
	 */
305
	public function __construct($entry, $titleField, $descriptionField, $authorField) {
306
		$this->failover = $entry;
307
		$this->titleField = $titleField;
308
		$this->descriptionField = $descriptionField;
309
		$this->authorField = $authorField;
310
311
		parent::__construct();
312
	}
313
314
	/**
315
	 * Get the description of this entry
316
	 *
317
	 * @return DBField Returns the description of the entry.
318
	 */
319
	public function Title() {
320
		return $this->rssField($this->titleField);
321
	}
322
323
	/**
324
	 * Get the description of this entry
325
	 *
326
	 * @return DBField Returns the description of the entry.
327
	 */
328
	public function Description() {
329
		$description = $this->rssField($this->descriptionField);
330
331
		// HTML fields need links re-written
332
		if($description instanceof DBHTMLText) {
333
			return $description->obj('AbsoluteLinks');
334
		}
335
336
		return $description;
337
	}
338
339
	/**
340
	 * Get the author of this entry
341
	 *
342
	 * @return DBField Returns the author of the entry.
343
	 */
344
	public function Author() {
345
		return $this->rssField($this->authorField);
346
	}
347
348
	/**
349
	 * Return the safely casted field
350
	 *
351
	 * @param string $fieldName Name of field
352
	 * @return DBField
353
	 */
354
	public function rssField($fieldName) {
355
		if($fieldName) {
356
			return $this->failover->obj($fieldName);
357
		}
358
		return null;
359
	}
360
361
	/**
362
	 * Get a link to this entry
363
	 *
364
	 * @return string Returns the URL of this entry
365
	 * @throws BadMethodCallException
366
	 */
367
	public function AbsoluteLink() {
368
		if($this->failover->hasMethod('AbsoluteLink')) {
369
			return $this->failover->AbsoluteLink();
370
		} else if($this->failover->hasMethod('Link')) {
371
			return Director::absoluteURL($this->failover->Link());
372
		}
373
374
		throw new BadMethodCallException(
375
			$this->failover->class .
376
			" object has neither an AbsoluteLink nor a Link method." .
377
			" Can't put a link in the RSS feed", E_USER_WARNING
378
		);
379
	}
380
}
381