WorkflowEmbargoExpiryExtension::getCMSValidator()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 5
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
/**
4
 * Adds embargo period and expiry dates to content items
5
 *
6
 * @author [email protected]
7
 * @license BSD License http://silverstripe.org/bsd-license/
8
 */
9
class WorkflowEmbargoExpiryExtension extends DataExtension {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
10
11
	private static $db = array(
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
12
		'DesiredPublishDate'	=> 'SS_Datetime',
13
		'DesiredUnPublishDate'	=> 'SS_Datetime',
14
		'PublishOnDate'			=> 'SS_Datetime',
15
		'UnPublishOnDate'		=> 'SS_Datetime',
16
	);
17
18
	private static $has_one = array(
0 ignored issues
show
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
19
		'PublishJob'			=> 'QueuedJobDescriptor',
20
		'UnPublishJob'			=> 'QueuedJobDescriptor',
21
	);
22
23
	private static $dependencies = array(
0 ignored issues
show
Unused Code introduced by
The property $dependencies is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
24
		'workflowService'		=> '%$WorkflowService',
25
	);
26
27
	// This "config" option, might better be handled in _config
28
	public static $showTimePicker = true;
29
30
	/**
31
	 * @var WorkflowService
32
	 */
33
	public $workflowService;
34
35
	/**
36
	 * Is a workflow in effect?
37
	 *
38
	 * @var bool
39
	 */
40
	public $isWorkflowInEffect = false;
41
42
	/**
43
	 * A basic extended validation routine method return format
44
	 *
45
	 * @var array
46
	 */
47
	public static $extendedMethodReturn = array(
48
		'fieldName'	=>null,
49
		'fieldField'=>null,
50
		'fieldMsg'	=>null,
51
		'fieldValid'=>true
52
	);
53
54
	/**
55
	 * @param FieldList $fields
56
	 */
57
	public function updateCMSFields(FieldList $fields) {
58
	    
59
	    // requirements
60
	    // ------------
61
	    
62
		Requirements::add_i18n_javascript(ADVANCED_WORKFLOW_DIR . '/javascript/lang');
63
64
		// Add timepicker functionality
65
		// @see https://github.com/trentrichardson/jQuery-Timepicker-Addon
66
		Requirements::css(
67
			ADVANCED_WORKFLOW_DIR . '/thirdparty/javascript/jquery-ui/timepicker/jquery-ui-timepicker-addon.css'
68
		);
69
		Requirements::css(ADVANCED_WORKFLOW_DIR . '/css/WorkflowCMS.css');
70
		Requirements::javascript(
71
			ADVANCED_WORKFLOW_DIR . '/thirdparty/javascript/jquery-ui/timepicker/jquery-ui-sliderAccess.js'
72
		);
73
		Requirements::javascript(
74
			ADVANCED_WORKFLOW_DIR . '/thirdparty/javascript/jquery-ui/timepicker/jquery-ui-timepicker-addon.js'
75
		);
76
		Requirements::javascript(ADVANCED_WORKFLOW_DIR . '/javascript/WorkflowField.js');
77
78
        // Fields
79
        // ------
80
81
		// we never show these explicitly in admin
82
		$fields->removeByName('PublishJobID');
83
		$fields->removeByName('UnPublishJobID');
84
85
		$this->setIsWorkflowInEffect();
86
87
		$fields->findOrMakeTab(
88
			'Root.PublishingSchedule',
89
			_t('WorkflowEmbargoExpiryExtension.TabTitle', 'Publishing Schedule')
90
		);
91
		if ($this->getIsWorkflowInEffect()) {
92
93
			// add fields we want in this context
94
			$fields->addFieldsToTab('Root.PublishingSchedule', array(
95
				HeaderField::create(
96
					'PublishDateHeader',
97
					_t('WorkflowEmbargoExpiryExtension.REQUESTED_PUBLISH_DATE_H3', 'Expiry and Embargo'),
98
					3
99
				),
100
				LiteralField::create('PublishDateIntro', $this->getIntroMessage('PublishDateIntro')),
101
				$dt = Datetimefield::create(
102
					'DesiredPublishDate',
103
					_t('WorkflowEmbargoExpiryExtension.REQUESTED_PUBLISH_DATE', 'Requested publish date')
104
				),
105
				$ut = Datetimefield::create(
106
					'DesiredUnPublishDate',
107
					_t('WorkflowEmbargoExpiryExtension.REQUESTED_UNPUBLISH_DATE', 'Requested un-publish date')
108
				),
109
				Datetimefield::create(
110
					'PublishOnDate',
111
					_t('WorkflowEmbargoExpiryExtension.PUBLISH_ON', 'Scheduled publish date')
112
				)->setDisabled(true),
113
				Datetimefield::create(
114
					'UnPublishOnDate',
115
					_t('WorkflowEmbargoExpiryExtension.UNPUBLISH_ON', 'Scheduled un-publish date')
116
				)->setDisabled(true)
117
			));
118
		} else {
119
		    
120
		    // remove fields that have been automatically added that we don't want
121
			$fields->removeByName('DesiredPublishDate');
122
			$fields->removeByName('DesiredUnPublishDate');
123
124
			// add fields we want in this context
125
			$fields->addFieldsToTab('Root.PublishingSchedule', array(
126
				HeaderField::create(
127
					'PublishDateHeader',
128
					_t('WorkflowEmbargoExpiryExtension.REQUESTED_PUBLISH_DATE_H3', 'Expiry and Embargo'),
129
					3
130
				),
131
				LiteralField::create('PublishDateIntro', $this->getIntroMessage('PublishDateIntro')),
132
				$dt = Datetimefield::create(
133
					'PublishOnDate',
134
					_t('WorkflowEmbargoExpiryExtension.PUBLISH_ON', 'Scheduled publish date')
135
				),
136
				$ut = Datetimefield::create(
137
					'UnPublishOnDate',
138
					_t('WorkflowEmbargoExpiryExtension.UNPUBLISH_ON', 'Scheduled un-publish date')
139
				),
140
			));
141
		}
142
143
		$dt->getDateField()->setConfig('showcalendar', true);
144
		$ut->getDateField()->setConfig('showcalendar', true);
145
		$dt->getTimeField()->setConfig('timeformat', 'HH:mm:ss');
146
		$ut->getTimeField()->setConfig('timeformat', 'HH:mm:ss');
147
148
		// Enable a jQuery-UI timepicker widget
149
		if(self::$showTimePicker) {
150
			$dt->getTimeField()->addExtraClass('hasTimePicker');
151
			$ut->getTimeField()->addExtraClass('hasTimePicker');
152
		}
153
	}
154
155
	/**
156
	 * Clears any existing publish job against this dataobject
157
	 */
158
	protected function clearPublishJob() {
159
		$job = $this->owner->PublishJob();
160
		if($job && $job->exists()) {
161
			$job->delete();
162
		}
163
		$this->owner->PublishJobID = 0;
164
	}
165
166
	/**
167
	 * Clears any existing unpublish job
168
	 */
169
	protected function clearUnPublishJob() {
170
		// Cancel any in-progress unpublish job
171
		$job = $this->owner->UnPublishJob();
172
		if ($job && $job->exists()) {
173
			$job->delete();
174
		}
175
		$this->owner->UnPublishJobID = 0;
176
	}
177
178
	/**
179
	 * Ensure the existence of a publish job at the specified time
180
	 *
181
	 * @param int $when Timestamp to start this job, or null to start immediately
182
	 */
183 View Code Duplication
	protected function ensurePublishJob($when) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
		// Check if there is a prior job
185
		if($this->owner->PublishJobID) {
186
			$job = $this->owner->PublishJob();
187
			// Use timestamp for sake of comparison.
188
			if($job && $job->exists() && strtotime($job->StartAfter) == $when) {
189
				return;
190
			}
191
			$this->clearPublishJob();
192
		}
193
194
		// Create a new job with the specified schedule
195
		$job = new WorkflowPublishTargetJob($this->owner, 'publish');
196
		$this->owner->PublishJobID = Injector::inst()->get('QueuedJobService')
197
				->queueJob($job, $when ? date('Y-m-d H:i:s', $when) : null);
198
	}
199
200
	/**
201
	 * Ensure the existence of an unpublish job at the specified time
202
	 *
203
	 * @param int $when Timestamp to start this job, or null to start immediately
204
	 */
205 View Code Duplication
	protected function ensureUnPublishJob($when) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
		// Check if there is a prior job
207
		if($this->owner->UnPublishJobID) {
208
			$job = $this->owner->UnPublishJob();
209
			// Use timestamp for sake of comparison.
210
			if($job && $job->exists() && strtotime($job->StartAfter) == $when) {
211
				return;
212
			}
213
			$this->clearUnPublishJob();
214
		}
215
216
		// Create a new job with the specified schedule
217
		$job = new WorkflowPublishTargetJob($this->owner, 'unpublish');
218
		$this->owner->UnPublishJobID = Injector::inst()->get('QueuedJobService')
219
			->queueJob($job, $when ? date('Y-m-d H:i:s', $when) : null);
220
	}
221
222
	/**
223
	 * {@see PublishItemWorkflowAction} for approval of requested publish dates
224
	 */
225
	public function onBeforeWrite() {
226
		parent::onBeforeWrite();
227
228
		// if we've been duplicated, the old job IDs will be hanging around, so explicitly clear
229
		if (!$this->owner->ID) {
230
			$this->owner->PublishJobID = 0;
231
			$this->owner->UnPublishJobID = 0;
232
		}
233
234
		// only operate on staging content for this extension; otherwise, you
235
		// need to publish the page to be able to set a 'future' publish...
236
		// while the same could be said for the unpublish, the 'publish' state
237
		// is the one that must be avoided so we allow setting the 'unpublish'
238
		// date for as-yet-not-published content.
239
		if (Versioned::current_stage() === 'Live') return;
240
241
		/*
242
		 * Without checking if there's actually a workflow in effect, simply saving
243
		 * as draft, would clear the Scheduled Publish & Unpublish date fields, which we obviously
244
		 * don't want during a workflow: These date fields should be treated as a content
245
		 * change that also requires approval (where such an approval step exists).
246
		 *
247
		 * - Check to see if we've got 'desired' publish/unpublish date(s).
248
		 * - Check if there's a workflow attached to this content
249
		 * - Reset values if it's safe to do so
250
		 */
251
		$resetPublishOnDate = $this->owner->DesiredPublishDate && $this->owner->PublishOnDate;
252
		if ($resetPublishOnDate && !$this->getIsWorkflowInEffect()) {
253
			$this->owner->PublishOnDate = '';
254
		}
255
256
		$resetUnPublishOnDate = $this->owner->DesiredUnPublishDate && $this->owner->UnPublishOnDate;
257
		if ($resetUnPublishOnDate && !$this->getIsWorkflowInEffect()) {
258
			$this->owner->UnPublishOnDate = '';
259
		}
260
261
		// Jobs can only be queued for records that already exist
262
		if(!$this->owner->ID) return;
263
264
		// Check requested dates of publish / unpublish, and whether the page should have already been unpublished
265
		$now = strtotime(SS_Datetime::now()->getValue());
266
		$publishTime = strtotime($this->owner->PublishOnDate);
267
		$unPublishTime = strtotime($this->owner->UnPublishOnDate);
268
269
		// We should have a publish job if:
270
		if($publishTime && ( // We have a date
271
			$unPublishTime < $publishTime // it occurs after an unpublish date (or there is no unpublish)
272
			|| $unPublishTime > $now // or the unpublish date hasn't passed
273
		)) {
274
			// Trigger time immediately if passed
275
			$this->ensurePublishJob($publishTime < $now ? null : $publishTime);
276
		} else {
277
			$this->clearPublishJob();
278
		}
279
280
		// We should have an unpublish job if:
281
		if($unPublishTime && ( // we have a date
282
			$publishTime < $unPublishTime // it occurs after a publish date (or there is no publish)
283
			|| $publishTime > $now // or the publish date hasn't passed
284
		)) {
285
			// Trigger time immediately if passed
286
			$this->ensureUnPublishJob($unPublishTime < $now ? null : $unPublishTime);
287
		} else {
288
			$this->clearUnPublishJob();
289
		}
290
	}
291
292
	/*
293
	 * Define an array of message-parts for use by {@link getIntroMessage()}
294
	 *
295
	 * @param string $key
296
	 * @return array
297
	 */
298
	public function getIntroMessageParts($key) {
299
		$parts = array(
300
			'PublishDateIntro' => array(
301
				'INTRO'=>_t('WorkflowEmbargoExpiryExtension.REQUESTED_PUBLISH_DATE_INTRO','Enter a date and/or time to specify embargo and expiry dates.'),
302
				'BULLET_1'=>_t('WorkflowEmbargoExpiryExtension.REQUESTED_PUBLISH_DATE_INTRO_BULLET_1','These settings won\'t take effect until any approval actions are run'),
303
				'BULLET_2'=>_t('WorkflowEmbargoExpiryExtension.REQUESTED_PUBLISH_DATE_INTRO_BULLET_2','If an embargo is already set, adding a new one prior to that date\'s passing will overwrite it')
304
			)
305
		);
306
		// If there's no effective workflow, no need for the first bullet-point
307
		if(!$this->getIsWorkflowInEffect()) {
308
			$parts['PublishDateIntro']['BULLET_1'] = false;
309
		}
310
		return $parts[$key];
311
	}
312
313
	/*
314
	 * Display some messages to the user, a little more complex that a simple one-liner
315
	 *
316
	 * @param string $key
317
	 * @return string
318
	 */
319
	public function getIntroMessage($key) {
320
		$msg = $this->getIntroMessageParts($key);
321
		$curr = Controller::curr();
322
		$msg = $curr->customise($msg)->renderWith('embargoIntro');
323
		return $msg;
324
	}
325
326
	/*
327
	 * Validate
328
	 */
329
	public function getCMSValidator() {
330
		$required = new AWRequiredFields();
331
		$required->setCaller($this);
332
		return $required;
333
	}
334
335
	/*
336
	 * Format a date according to member/user preferences
337
	 *
338
	 * @param string $date
339
	 * @return string $date
340
	 */
341
	public function getUserDate($date) {
342
		$date = new Zend_Date($date);
343
		$member = Member::currentUser();
344
		return $date->toString($member->getDateFormat().' '.$member->getTimeFormat());
345
	}
346
347
	/*
348
	 * Sets property as boolean true|false if an effective workflow is found or not
349
	 */
350
	public function setIsWorkflowInEffect() {
351
		// if there is a workflow applied, we can't set the publishing date directly, only the 'desired' publishing date
352
		$effective = $this->workflowService->getDefinitionFor($this->owner);
353
		$this->isWorkflowInEffect = $effective?true:false;
354
	}
355
356
	public function getIsWorkflowInEffect() {
357
		return $this->isWorkflowInEffect;
358
	}
359
}
360