Passed
Push — 1.8 ( 8b2854...6b63e4 )
by Robbie
15:17 queued 11:24
created

DatedUpdateHolder   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 22
c 0
b 0
f 0
dl 0
loc 204
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A Updates() 0 3 1
B AllUpdates() 0 54 8
C ExtractMonths() 0 62 9
A getSubscriptionTitle() 0 2 1
A UpdateTags() 0 10 1
A getDefaultAtomLink() 0 2 1
A getDefaultRSSLink() 0 2 1
1
<?php
2
3
class DatedUpdateHolder extends Page {
0 ignored issues
show
Bug introduced by
The type Page was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
4
5
	// Meant as an abstract base class.
6
	private static $hide_ancestor = 'DatedUpdateHolder';
0 ignored issues
show
introduced by
The private property $hide_ancestor is not used, and could be removed.
Loading history...
7
8
	private static $update_name = 'Updates';
0 ignored issues
show
introduced by
The private property $update_name is not used, and could be removed.
Loading history...
9
10
	private static $update_class = 'DatedUpdatePage';
0 ignored issues
show
introduced by
The private property $update_class is not used, and could be removed.
Loading history...
11
12
	private static $singular_name = 'Dated Update Holder';
0 ignored issues
show
introduced by
The private property $singular_name is not used, and could be removed.
Loading history...
13
14
	private static $plural_name = 'Dated Update Holders';
0 ignored issues
show
introduced by
The private property $plural_name is not used, and could be removed.
Loading history...
15
16
	/**
17
	 * Find all distinct tags (TaxonomyTerms) associated with the DatedUpdatePages under this holder.
18
	 */
19
	public function UpdateTags() {
20
		$tags = TaxonomyTerm::get()
21
			->innerJoin('BasePage_Terms', '"TaxonomyTerm"."ID"="BasePage_Terms"."TaxonomyTermID"')
22
			->innerJoin(
23
				'SiteTree',
24
				sprintf('"SiteTree"."ID" = "BasePage_Terms"."BasePageID" AND "SiteTree"."ParentID" = \'%d\'', $this->ID)
25
			)
26
			->sort('Name');
27
28
		return $tags;
29
	}
30
31
	/**
32
	 * Wrapper to find all updates belonging to this holder, based on some filters.
33
	 */
34
	public function Updates($tagID = null, $dateFrom = null, $dateTo = null, $year = null, $monthNumber = null) {
35
		$className = Config::inst()->get($this->ClassName, 'update_class');
36
		return static::AllUpdates($className, $this->ID, $tagID, $dateFrom, $dateTo, $year, $monthNumber);
0 ignored issues
show
Bug introduced by
It seems like $className can also be of type array; however, parameter $className of DatedUpdateHolder::AllUpdates() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

36
		return static::AllUpdates(/** @scrutinizer ignore-type */ $className, $this->ID, $tagID, $dateFrom, $dateTo, $year, $monthNumber);
Loading history...
37
	}
38
39
	/**
40
	 * Find all site's updates, based on some filters.
41
	 * Omitting parameters will prevent relevant filters from being applied. The filters are ANDed together.
42
	 *
43
	 * @param string $className The name of the class to fetch.
44
	 * @param int|null $parentID The ID of the holder to extract the updates from.
45
	 * @param int|null $tagID The ID of the tag to filter the updates by.
46
	 * @param string|null $dateFrom The beginning of a date filter range.
47
	 * @param string|null $dateTo The end of the date filter range. If empty, only one day will be searched for.
48
	 * @param int|null $year Numeric value of the year to show.
49
	 * @param int|null $monthNumber Numeric value of the month to show.
50
	 *
51
	 * @returns DataList | PaginatedList
52
	 */
53
	public static function AllUpdates($className = 'DatedUpdatePage', $parentID = null, $tagID = null, $dateFrom = null,
54
			$dateTo = null, $year = null, $monthNumber = null) {
55
56
		$items = $className::get();
57
		$dbTableName = ClassInfo::table_for_object_field($className, 'Date');
58
		if (!$dbTableName) {
59
			$dbTableName = 'DatedUpdatePage';
60
		}
61
62
		// Filter by parent holder.
63
		if (isset($parentID)) {
64
			$items = $items->filter(array('ParentID'=>$parentID));
65
		}
66
67
		// Filter down to a single tag.
68
		if (isset($tagID)) {
69
			$items = $items->innerJoin(
70
				'BasePage_Terms',
71
				sprintf('"%s"."ID" = "BasePage_Terms"."BasePageID"', $className)
72
			)->innerJoin(
73
				'TaxonomyTerm',
74
				sprintf('"BasePage_Terms"."TaxonomyTermID" = "TaxonomyTerm"."ID" AND "TaxonomyTerm"."ID" = \'%d\'', $tagID)
75
			);
76
		}
77
78
		// Filter by date
79
		if (isset($dateFrom)) {
80
81
			if (!isset($dateTo)) {
82
				$dateTo = $dateFrom;
83
			}
84
85
			$items = $items->where(array(
86
				sprintf('"%s"."Date" >= \'%s\'', $dbTableName, Convert::raw2sql("$dateFrom 00:00:00")),
87
				sprintf('"%s"."Date" <= \'%s\'', $dbTableName, Convert::raw2sql("$dateTo 23:59:59"))
88
			));
89
		}
90
91
		// Filter down to single month.
92
		if (isset($year) && isset($monthNumber)) {
93
			$year = (int)$year;
94
			$monthNumber = (int)$monthNumber;
95
96
			$beginDate = sprintf("%04d-%02d-01 00:00:00", $year, $monthNumber);
97
			$endDate = date('Y-m-d H:i:s', strtotime("{$beginDate} +1 month"));
98
99
			$items = $items->where(array(
100
				sprintf('"%s"."Date" >= \'%s\'', $dbTableName, Convert::raw2sql($beginDate)),
101
				sprintf('"%s"."Date" < \'%s\'', $dbTableName, Convert::raw2sql($endDate))
102
			));
103
		}
104
105
		// Unpaginated DataList.
106
		return $items;
107
	}
108
109
	/**
110
	 * Produce an ArrayList of available months out of the updates contained in the DataList.
111
	 *
112
	 * Here is an example of the returned structure:
113
	 * ArrayList:
114
	 *   ArrayData:
115
	 *     YearName => 2013
116
	 *     Months => ArrayList:
117
	 *       MonthName => Jan
118
	 *       MonthNumber => 1
119
	 *       MonthLink => (page URL)year=2012&month=1
0 ignored issues
show
Bug introduced by
The type Link was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
120
	 *       Active => true
121
	 *   ArrayData:
122
	 *     YearName => 2012
123
	 *     Months => ArrayList:
124
	 *     ...
125
	 *
126
	 * @param $updates DataList DataList to extract months from.
127
	 * @param $link Link used as abase to construct the MonthLink.
128
	 * @param $currentYear Currently selected year, for computing the link active state.
0 ignored issues
show
Bug introduced by
The type Currently was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
129
	 * @param $currentMonthNumber Currently selected month, for computing the link active state.
130
	 *
131
	 * @returns ArrayList
132
	 */
133
	public static function ExtractMonths(DataList $updates, $link = null, $currentYear = null, $currentMonthNumber = null) {
134
		// Set the link to current URL in the same way the HTTP::setGetVar does it.
135
		if (!isset($link)) {
136
			$link = Director::makeRelative($_SERVER['REQUEST_URI']);
137
		}
138
139
		$dates = $updates->dataQuery()
140
			->groupby('YEAR("Date")')
141
			->groupby('MONTH("Date")')
142
			->sort('Date', 'DESC')
143
			->query()
144
			->setSelect(array(
145
				'Year' => 'YEAR("Date")',
146
				'Month' => 'MONTH("Date")',
147
			))
148
			->addWhere('"Date" IS NOT NULL')
149
			->execute();
150
151
		$years = array();
152
		foreach ($dates as $date) {
153
			$monthNumber = $date['Month'];
154
			$year = $date['Year'];
155
			$dateObj = new Datetime(implode('-', array($year, $monthNumber, 1)));
156
			$monthName = $dateObj->Format('M');
157
158
			// Set up the relevant year array, if not yet available.
159
			if (!isset($years[$year])) {
160
				$years[$year] = array('YearName'=>$year, 'Months'=>array());
161
			}
162
163
			// Check if the currently processed month is the one that is selected via GET params.
164
			$active = false;
165
			if (isset($year) && isset($monthNumber)) {
166
				$active = (((int)$currentYear)==$year && ((int)$currentMonthNumber)==$monthNumber);
167
			}
168
169
			// Build the link - keep the tag and date filter, but reset the pagination.
170
			if ($active) {
171
				// Allow clicking to deselect the month.
172
				$link = HTTP::setGetVar('month', null, $link, '&');
173
				$link = HTTP::setGetVar('year', null, $link, '&');
174
			} else {
175
				$link = HTTP::setGetVar('month', $monthNumber, $link, '&');
176
				$link = HTTP::setGetVar('year', $year, $link, '&');
177
			}
178
			$link = HTTP::setGetVar('start', null, $link, '&');
179
180
			$years[$year]['Months'][$monthNumber] = array(
181
				'MonthName'=>$monthName,
182
				'MonthNumber'=>$monthNumber,
183
				'MonthLink'=>$link,
184
				'Active'=>$active
185
			);
186
		}
187
188
		// ArrayList will not recursively walk through the supplied array, so manually build nested ArrayLists.
189
		foreach ($years as &$year) {
190
			$year['Months'] = new ArrayList($year['Months']);
191
		}
192
193
		// Reverse the list so the most recent years appear first.
194
		return new ArrayList($years);
195
	}
196
197
	public function getDefaultRSSLink() {
198
		return $this->Link('rss');
199
	}
200
201
	public function getDefaultAtomLink() {
202
		return $this->Link('atom');
203
	}
204
205
	public function getSubscriptionTitle() {
206
		return $this->Title;
207
	}
208
209
}
210
211
/**
212
 * The parameters apply in the following preference order:
213
 *  - Highest priority: Tag & date (or date range)
214
 *  - Month (and Year)
215
 *  - Pagination
216
 *
217
 * So, when the user click on a tag link, the pagination, and month will be reset, but not the date filter. Also,
218
 * changing the date will not affect the tag, but will reset the month and pagination.
219
 *
220
 * When the user clicks on a month, pagination will be reset, but tags retained. Pagination retains all other
221
 * parameters.
222
 */
223
class DatedUpdateHolder_Controller extends Page_Controller {
0 ignored issues
show
Bug introduced by
The type Page_Controller was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
224
225
	private static $allowed_actions = array(
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
226
		'rss',
227
		'atom',
228
		'DateRangeForm'
229
	);
230
231
232
	private static $casting = array(
0 ignored issues
show
introduced by
The private property $casting is not used, and could be removed.
Loading history...
233
		'MetaTitle' => 'Text',
234
		'FilterDescription' => 'Text'
235
	);
236
237
	/**
238
	 * Get the meta title for the current action
239
	 *
240
	 * @return string
241
	 */
242
	public function getMetaTitle() {
243
		$title = $this->data()->getTitle();
244
		$filter = $this->FilterDescription();
245
		if($filter) {
246
			$title = "{$title} - {$filter}";
247
		}
248
249
		$this->extend('updateMetaTitle', $title);
250
		return $title;
251
	}
252
253
	/**
254
	 * Returns a description of the current filter
255
	 *
256
	 * @return string
257
	 */
258
	public function FilterDescription() {
259
		$params = $this->parseParams();
260
261
		$filters = array();
262
		if ($params['tag']) {
263
			$term = TaxonomyTerm::get_by_id('TaxonomyTerm', $params['tag']);
264
			if ($term) {
265
				$filters[] = _t('DatedUpdateHolder.FILTER_WITHIN', 'within') . ' "' . $term->Name . '"';
266
			}
267
		}
268
269
		if ($params['from'] || $params['to']) {
270
			if ($params['from']) {
271
				$from = strtotime($params['from']);
272
				if ($params['to']) {
273
					$to = strtotime($params['to']);
274
					$filters[] = _t('DatedUpdateHolder.FILTER_BETWEEN', 'between') . ' '
275
						. date('j/m/Y', $from) . ' and ' . date('j/m/Y', $to);
276
				} else {
277
					$filters[] = _t('DatedUpdateHolder.FILTER_ON', 'on') . ' ' . date('j/m/Y', $from);
278
				}
279
			} else {
280
				$to = strtotime($params['to']);
281
				$filters[] = _t('DatedUpdateHolder.FILTER_ON', 'on') . ' ' . date('j/m/Y', $to);
282
			}
283
		}
284
285
		if ($params['year'] && $params['month']) {
286
			$timestamp = mktime(1, 1, 1, $params['month'], 1, $params['year']);
287
			$filters[] = _t('DatedUpdateHolder.FILTER_IN', 'in') . ' ' . date('F', $timestamp) . ' ' . $params['year'];
288
		}
289
290
		if ($filters) {
291
			return $this->getUpdateName() . ' ' . implode(' ', $filters);
292
		}
293
	}
294
295
	public function getUpdateName() {
296
		return Config::inst()->get($this->data()->ClassName, 'update_name');
297
	}
298
299
	public function init() {
300
		parent::init();
301
		RSSFeed::linkToFeed($this->Link() . 'rss', $this->getSubscriptionTitle());
302
	}
303
304
	/**
305
	 * Parse URL parameters.
306
	 *
307
	 * @param $produceErrorMessages Set to false to omit session messages.
0 ignored issues
show
Bug introduced by
The type Set was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
308
	 */
309
	public function parseParams($produceErrorMessages = true) {
310
		$tag = $this->request->getVar('tag');
311
		$from = $this->request->getVar('from');
312
		$to = $this->request->getVar('to');
313
		$year = $this->request->getVar('year');
314
		$month = $this->request->getVar('month');
315
316
		if ($tag=='') $tag = null;
317
		if ($from=='') $from = null;
318
		if ($to=='') $to = null;
319
		if ($year=='') $year = null;
320
		if ($month=='') $month = null;
321
322
		if (isset($tag)) $tag = (int)$tag;
323
		if (isset($from)) {
324
			$from = urldecode($from);
325
			$parser = new SS_Datetime;
326
			$parser->setValue($from);
327
			$from = $parser->Format('Y-m-d');
328
		}
329
		if (isset($to)) {
330
			$to = urldecode($to);
331
			$parser = new SS_Datetime;
332
			$parser->setValue($to);
333
			$to = $parser->Format('Y-m-d');
334
		}
335
		if (isset($year)) $year = (int)$year;
336
		if (isset($month)) $month = (int)$month;
337
338
		// If only "To" has been provided filter by single date. Normalise by swapping with "From".
339
		if (isset($to) && !isset($from)) {
340
			list($to, $from) = array($from, $to);
341
		}
342
343
		// Flip the dates if the order is wrong.
344
		if (isset($to) && isset($from) && strtotime($from)>strtotime($to)) {
345
			list($to, $from) = array($from, $to);
346
347
			if ($produceErrorMessages) {
348
				Session::setFormMessage(
349
					'Form_DateRangeForm',
350
					_t('DateUpdateHolder.FilterAppliedMessage','Filter has been applied with the dates reversed.'),
351
					'warning'
352
				);
353
			}
354
		}
355
356
		// Notify the user that filtering by single date is taking place.
357
		if (isset($from) && !isset($to)) {
358
			if ($produceErrorMessages) {
359
				Session::setFormMessage(
360
					'Form_DateRangeForm',
361
					_t('DateUpdateHolder.DateRangeFilterMessage','Filtered by a single date.'),
362
					'warning'
363
				);
364
			}
365
		}
366
367
		return array(
368
			'tag' => $tag,
369
			'from' => $from,
370
			'to' => $to,
371
			'year' => $year,
372
			'month' => $month
373
		);
374
	}
375
376
	/**
377
	 * Build the link - keep the date range, reset the rest.
378
	 */
379
	public function AllTagsLink() {
380
		$link = HTTP::setGetVar('tag', null, null, '&');
381
		$link = HTTP::setGetVar('month', null, $link, '&');
382
		$link = HTTP::setGetVar('year', null, $link, '&');
383
		$link = HTTP::setGetVar('start', null, $link, '&');
384
385
		return $link;
386
	}
387
388
	/**
389
	 * List tags and attach links.
390
	 */
391
	public function UpdateTagsWithLinks() {
392
		$tags = $this->UpdateTags();
393
394
		$processed = new ArrayList();
395
396
		foreach ($tags as $tag) {
397
			// Build the link - keep the tag, and date range, but reset month, year and pagination.
398
			$link = HTTP::setGetVar('tag', $tag->ID, null, '&');
399
			$link = HTTP::setGetVar('month', null, $link, '&');
400
			$link = HTTP::setGetVar('year', null, $link, '&');
401
			$link = HTTP::setGetVar('start', null, $link, '&');
402
403
			$tag->Link = $link;
404
			$processed->push($tag);
405
		}
406
407
		return $processed;
408
	}
409
410
	/**
411
	 * Get the TaxonomyTerm related to the current tag GET parameter.
412
	 */
413
	public function CurrentTag() {
414
		$tagID = $this->request->getVar('tag');
415
416
		if (isset($tagID)) {
417
			return TaxonomyTerm::get_by_id('TaxonomyTerm', (int)$tagID);
418
		}
419
	}
420
421
	/**
422
	 * Extract the available months based on the current query.
423
	 * Only tag is respected. Pagination and months are ignored.
424
	 */
425
	public function AvailableMonths() {
426
		$params = $this->parseParams();
427
428
		return DatedUpdateHolder::ExtractMonths(
429
			$this->Updates($params['tag'], $params['from'], $params['to']),
430
			Director::makeRelative($_SERVER['REQUEST_URI']),
431
			$params['year'],
432
			$params['month']
433
		);
434
	}
435
436
	/**
437
	 * Get the updates based on the current query.
438
	 */
439
	public function FilteredUpdates($pageSize = 20) {
440
		$params = $this->parseParams();
441
442
		$items = $this->Updates(
443
			$params['tag'],
444
			$params['from'],
445
			$params['to'],
446
			$params['year'],
447
			$params['month']
448
		);
449
450
		// Apply pagination
451
		$list = new PaginatedList($items, $this->request);
452
		$list->setPageLength($pageSize);
453
		return $list;
454
	}
455
456
	/**
457
	 * @return Form
458
	 */
459
	public function DateRangeForm() {
460
		$dateFromTitle = DBField::create_field('HTMLText', sprintf(
461
			'%s <span class="field-note">%s</span>',
462
			_t('DatedUpdateHolder.FROM_DATE', 'From date'),
463
			_t('DatedUpdateHolder.DATE_EXAMPLE', '(example: 2017/12/30)')
464
		));
465
		$dateToTitle = DBField::create_field('HTMLText', sprintf(
466
			'%s <span class="field-note">%s</span>',
467
			_t('DatedUpdateHolder.TO_DATE', 'To date'),
468
			_t('DatedUpdateHolder.DATE_EXAMPLE', '(example: 2017/12/30)')
469
		));
470
471
		$fields = new FieldList(
472
			DateField::create('from', $dateFromTitle)
473
				->setConfig('showcalendar', true),
474
			DateField::create('to', $dateToTitle)
475
				->setConfig('showcalendar', true),
476
			HiddenField::create('tag')
477
		);
478
479
		$actions = new FieldList(
480
			FormAction::create("doDateFilter")->setTitle("Filter")->addExtraClass('btn btn-primary primary'),
481
			FormAction::create("doDateReset")->setTitle("Clear")->addExtraClass('btn')
482
		);
483
484
		$form = new Form($this, 'DateRangeForm', $fields, $actions);
485
		$form->loadDataFrom($this->request->getVars());
486
		$form->setFormMethod('get');
487
488
		// Manually extract the message so we can clear it.
489
		$form->ErrorMessage = $form->Message();
490
		$form->ErrorMessageType = $form->MessageType();
491
		$form->clearMessage();
492
493
		return $form;
494
	}
495
496
	public function doDateFilter() {
497
		$params = $this->parseParams();
498
499
		// Build the link - keep the tag, but reset month, year and pagination.
500
		$link = HTTP::setGetVar('from', $params['from'], $this->AbsoluteLink(), '&');
501
		$link = HTTP::setGetVar('to', $params['to'], $link, '&');
502
		if (isset($params['tag'])) $link = HTTP::setGetVar('tag', $params['tag'], $link, '&');
503
504
		$this->redirect($link);
505
	}
506
507
	public function doDateReset() {
508
		$params = $this->parseParams(false);
509
510
		// Reset the link - only include the tag.
511
		$link = $this->AbsoluteLink();
512
		if (isset($params['tag'])) $link = HTTP::setGetVar('tag', $params['tag'], $link, '&');
513
514
		$this->redirect($link);
515
	}
516
517
	public function rss() {
518
		$rss = new RSSFeed(
519
			$this->Updates()->sort('Created DESC')->limit(20),
520
			$this->Link('rss'),
521
			$this->getSubscriptionTitle()
522
		);
523
		return $rss->outputToBrowser();
524
	}
525
526
	public function atom() {
527
		$atom = new CwpAtomFeed(
528
			$this->Updates()->sort('Created DESC')->limit(20),
529
			$this->Link('atom'),
530
			$this->getSubscriptionTitle()
531
		);
532
		return $atom->outputToBrowser();
533
	}
534
}
535
536