Completed
Pull Request — master (#49)
by
unknown
02:21
created

SiteTreeContentReview::onBeforeWrite()   D

Complexity

Conditions 9
Paths 48

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 13
Bugs 1 Features 0
Metric Value
c 13
b 1
f 0
dl 0
loc 39
rs 4.909
cc 9
eloc 19
nc 48
nop 0
1
<?php
2
3
/**
4
 * Set dates at which content needs to be reviewed and provide a report and emails to alert
5
 * to content needing review.
6
 *
7
 * @property string $ContentReviewType
8
 * @property int    $ReviewPeriodDays
9
 * @property Date   $NextReviewDate
10
 * @property string $LastEditedByName
11
 * @property string $OwnerNames
12
 *
13
 * @method DataList ReviewLogs()
14
 * @method DataList ContentReviewGroups()
15
 * @method DataList ContentReviewUsers()
16
 */
17
class SiteTreeContentReview extends DataExtension implements PermissionProvider
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...
18
{
19
    /**
20
     * @var array
21
     */
22
    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...
23
        "ContentReviewType" => "Enum('Inherit, Disabled, Custom', 'Inherit')",
24
        "ReviewPeriodDays"  => "Int",
25
        "NextReviewDate"    => "Date",
26
        "LastEditedByName"  => "Varchar(255)",
27
        "OwnerNames"        => "Varchar(255)",
28
        "ReviewInfo"        => "Text"
29
    );
30
31
    /**
32
     * @var array
33
     */
34
    private static $defaults = array(
0 ignored issues
show
Unused Code introduced by
The property $defaults 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...
35
        "ContentReviewType" => "Inherit",
36
    );
37
38
    /**
39
     * @var array
40
     */
41
    private static $has_many = array(
0 ignored issues
show
Unused Code introduced by
The property $has_many 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...
42
        "ReviewLogs" => "ContentReviewLog",
43
    );
44
45
    /**
46
     * @var array
47
     */
48
    private static $belongs_many_many = array(
0 ignored issues
show
Unused Code introduced by
The property $belongs_many_many 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...
49
        "ContentReviewGroups" => "Group",
50
        "ContentReviewUsers"  => "Member",
51
    );
52
53
    /**
54
     * @var array
55
     */
56
    private static $schedule = array(
57
        0   => "No automatic review date",
58
        1   => "1 day",
59
        7   => "1 week",
60
        30  => "1 month",
61
        60  => "2 months",
62
        91  => "3 months",
63
        121 => "4 months",
64
        152 => "5 months",
65
        183 => "6 months",
66
        365 => "12 months",
67
    );
68
69
    /**
70
     * @return array
71
     */
72
    public static function get_schedule()
73
    {
74
        return self::$schedule;
75
    }
76
77
    /**
78
     * Takes a list of groups and members and return a list of unique member.
79
     *
80
     * @param SS_List $groups
81
     * @param SS_List $members
82
     *
83
     * @return ArrayList
84
     */
85
    public static function merge_owners(SS_List $groups, SS_List $members)
86
    {
87
        $contentReviewOwners = new ArrayList();
88
89
        if ($groups->count()) {
90
            $groupIDs = array();
91
92
            foreach ($groups as $group) {
93
                $familyIDs = $group->collateFamilyIDs();
94
95
                if (is_array($familyIDs)) {
96
                    $groupIDs = array_merge($groupIDs, array_values($familyIDs));
97
                }
98
            }
99
100
            array_unique($groupIDs);
101
102
            if (count($groupIDs)) {
103
                $groupMembers = DataObject::get("Member")->where("\"Group\".\"ID\" IN (" . implode(",", $groupIDs) . ")")
104
                    ->leftJoin("Group_Members", "\"Member\".\"ID\" = \"Group_Members\".\"MemberID\"")
105
                    ->leftJoin("Group", "\"Group_Members\".\"GroupID\" = \"Group\".\"ID\"");
106
107
                $contentReviewOwners->merge($groupMembers);
108
            }
109
        }
110
111
        $contentReviewOwners->merge($members);
112
        $contentReviewOwners->removeDuplicates();
113
114
        return $contentReviewOwners;
115
    }
116
117
    /**
118
     * @param FieldList $actions
119
     */
120
    public function updateCMSActions(FieldList $actions)
121
    {
122
        if ($this->canBeReviewedBy(Member::currentUser())) {
0 ignored issues
show
Bug introduced by
It seems like \Member::currentUser() targeting Member::currentUser() can also be of type object<DataObject>; however, SiteTreeContentReview::canBeReviewedBy() does only seem to accept null|object<Member>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
123
            Requirements::css("contentreview/css/contentreview.css");
124
125
            $reviewTitle = LiteralField::create(
126
                "ReviewContentNotesLabel",
127
                "<label class=\"left\" for=\"Form_EditForm_ReviewNotes\">" . _t("ContentReview.CONTENTREVIEW", "Content due for review") . "</label>"
128
            );
129
130
            if (strlen($this->owner->ReviewInfo) > 0) {
131
                $reviewInfo = LiteralField::create(
132
                    "ReviewContentInfo",
133
                    "<p class=\"quick-info\">" . $this->owner->ReviewInfo . "</p>"
134
                );
135
            } else {
136
                $reviewInfo = '';
137
            }
138
139
            $ReviewNotes = LiteralField::create("ReviewNotes", "<textarea class=\"no-change-track\" id=\"Form_EditForm_ReviewNotes\" name=\"ReviewNotes\" placeholder=\"" . _t("ContentReview.COMMENTS", "(optional) Add comments...") . "\" class=\"text\"></textarea>");
140
141
            $quickReviewAction = FormAction::create("savereview", _t("ContentReview.MARKREVIEWED", "Mark as reviewed"))
142
                ->setAttribute("data-icon", "pencil")
143
                ->setAttribute("data-text-alternate", _t("ContentReview.MARKREVIEWED", "Mark as reviewed"));
144
145
            $allFields = CompositeField::create($reviewTitle, $reviewInfo, $ReviewNotes, $quickReviewAction)
146
                ->addExtraClass('review-notes field');
147
148
            $reviewTab = Tab::create('ReviewContent', $allFields);
149
            $reviewTab->addExtraClass('contentreview-tab');
150
151
            $actions->fieldByName('ActionMenus')->insertBefore($reviewTab, 'MoreOptions');
152
        }
153
    }
154
155
    /**
156
     * Returns false if the content review have disabled.
157
     *
158
     * @param SiteTree $page
159
     *
160
     * @return bool|Date
161
     */
162
    public function getReviewDate(SiteTree $page = null)
163
    {
164
        if ($page === null) {
165
            $page = $this->owner;
166
        }
167
168
        if ($page->obj("NextReviewDate")->exists()) {
169
            return $page->obj("NextReviewDate");
170
        }
171
172
        $options = $this->owner->getOptions();
173
174
        if (!$options) {
175
            return false;
176
        }
177
178
        if (!$options->ReviewPeriodDays) {
179
            return false;
180
        }
181
182
        // Failover to check on ReviewPeriodDays + LastEdited
183
        $nextReviewUnixSec = strtotime(" + " . $options->ReviewPeriodDays . " days", SS_Datetime::now()->format("U"));
184
        $date = Date::create("NextReviewDate");
185
        $date->setValue(date("Y-m-d H:i:s", $nextReviewUnixSec));
186
187
        return $date;
188
    }
189
190
    /**
191
     * Get the object that have the information about the content review settings. Either:
192
     *
193
     *  - a SiteTreeContentReview decorated object
194
     *  - the default SiteTree config
195
     *  - false if this page have it's content review disabled
196
     *
197
     * Will go through parents and root pages will use the site config if their setting is Inherit.
198
     *
199
     * @return bool|DataObject
200
     *
201
     * @throws Exception
202
     */
203
    public function getOptions()
204
    {
205
        if ($this->owner->ContentReviewType == "Custom") {
206
            return $this->owner;
207
        }
208
209
        if ($this->owner->ContentReviewType == "Disabled") {
210
            return false;
211
        }
212
213
        $page = $this->owner;
214
215
        // $page is inheriting it's settings from it's parent, find
216
        // the first valid parent with a valid setting
217
        while ($parent = $page->Parent()) {
218
219
            // Root page, use site config
220
            if (!$parent->exists()) {
221
                return SiteConfig::current_site_config();
222
            }
223
224
            if ($parent->ContentReviewType == "Custom") {
225
                return $parent;
226
            }
227
228
            if ($parent->ContentReviewType == "Disabled") {
229
                return false;
230
            }
231
232
            $page = $parent;
233
        }
234
235
        throw new Exception("This shouldn't really happen, as per usual developer logic.");
236
    }
237
238
    /**
239
     * @return string
240
     */
241
    public function getOwnerNames()
242
    {
243
        $options = $this->getOptions();
244
245
        $names = array();
246
247
        if (!$options) {
248
            return "";
249
        }
250
251
        foreach ($options->OwnerGroups() as $group) {
252
            $names[] = $group->getBreadcrumbs(" > ");
253
        }
254
255
        foreach ($options->OwnerUsers() as $group) {
256
            $names[] = $group->getName();
257
        }
258
259
        return implode(", ", $names);
260
    }
261
262
    /**
263
     * @return null|string
264
     */
265
    public function getEditorName()
266
    {
267
        $member = Member::currentUser();
268
269
        if ($member) {
270
            return $member->getTitle();
271
        }
272
273
        return null;
274
    }
275
276
    /**
277
     * Get all Members that are Content Owners to this page. This includes checking group
278
     * hierarchy and adding any direct users.
279
     *
280
     * @return ArrayList
281
     */
282
    public function ContentReviewOwners()
283
    {
284
        return SiteTreeContentReview::merge_owners(
285
            $this->OwnerGroups(),
286
            $this->OwnerUsers()
287
        );
288
    }
289
290
    /**
291
     * @return ManyManyList
292
     */
293
    public function OwnerGroups()
294
    {
295
        return $this->owner->getManyManyComponents("ContentReviewGroups");
296
    }
297
298
    /**
299
     * @return ManyManyList
300
     */
301
    public function OwnerUsers()
302
    {
303
        return $this->owner->getManyManyComponents("ContentReviewUsers");
304
    }
305
306
    /**
307
     * @param FieldList $fields
308
     */
309
    public function updateSettingsFields(FieldList $fields)
310
    {
311
        Requirements::javascript("contentreview/javascript/contentreview.js");
312
313
        // Display read-only version only
314
        if (!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
315
            $schedule = self::get_schedule();
316
            $contentOwners = ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames());
317
            $nextReviewAt = DateField::create('RONextReviewDate', _t("ContentReview.NEXTREVIEWDATE", "Next review date"), $this->owner->NextReviewDate);
318
319
            if (!isset($schedule[$this->owner->ReviewPeriodDays])) {
320
                $reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[0]);
321
            } else {
322
                $reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[$this->owner->ReviewPeriodDays]);
323
            }
324
325
            $logConfig = GridFieldConfig::create()
326
                ->addComponent(new GridFieldSortableHeader())
327
                ->addComponent($logColumns = new GridFieldDataColumns());
328
329
            // Cast the value to the users preferred date format
330
            $logColumns->setFieldCasting(array(
331
                "Created" => "DateTimeField->value",
332
            ));
333
334
            $logs = GridField::create("ROReviewNotes", "Review Notes", $this->owner->ReviewLogs(), $logConfig);
335
336
337
            $optionsFrom = ReadonlyField::create("ROType", _t("ContentReview.SETTINGSFROM", "Options are"), $this->owner->ContentReviewType);
338
339
            $fields->addFieldsToTab("Root.ContentReview", array(
340
                $contentOwners,
341
                $nextReviewAt->performReadonlyTransformation(),
342
                $reviewFreq,
343
                $optionsFrom,
344
                $logs,
345
            ));
346
347
            return;
348
        }
349
350
        $options = array();
351
        $options["Disabled"] = _t("ContentReview.DISABLE", "Disable content review");
352
        $options["Inherit"] = _t("ContentReview.INHERIT", "Inherit from parent page");
353
        $options["Custom"] = _t("ContentReview.CUSTOM", "Custom settings");
354
355
        $viewersOptionsField = OptionsetField::create("ContentReviewType", _t("ContentReview.OPTIONS", "Options"), $options);
356
357
        $users = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN"));
358
359
        $usersMap = $users->map("ID", "Title")->toArray();
0 ignored issues
show
Bug introduced by
The method toArray cannot be called on $users->map('ID', 'Title') (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
360
361
        asort($usersMap);
362
363
        $userField = ListboxField::create("OwnerUsers", _t("ContentReview.PAGEOWNERUSERS", "Users"), $usersMap)
364
            ->setMultiple(true)
365
            ->addExtraClass('custom-setting')
366
            ->setAttribute("data-placeholder", _t("ContentReview.ADDUSERS", "Add users"))
367
            ->setDescription(_t('ContentReview.OWNERUSERSDESCRIPTION', 'Page owners that are responsible for reviews'));
368
369
        $groupsMap = array();
370
371
        foreach (Group::get() as $group) {
372
            $groupsMap[$group->ID] = $group->getBreadcrumbs(" > ");
373
        }
374
        asort($groupsMap);
375
376
        $groupField = ListboxField::create("OwnerGroups", _t("ContentReview.PAGEOWNERGROUPS", "Groups"), $groupsMap)
377
            ->setMultiple(true)
378
            ->addExtraClass('custom-setting')
379
            ->setAttribute("data-placeholder", _t("ContentReview.ADDGROUP", "Add groups"))
380
            ->setDescription(_t("ContentReview.OWNERGROUPSDESCRIPTION", "Page owners that are responsible for reviews"));
381
382
        $reviewDate = DateField::create("NextReviewDate", _t("ContentReview.NEXTREVIEWDATE", "Next review date"))
383
            ->setConfig("showcalendar", true)
384
            ->setConfig("dateformat", "yyyy-MM-dd")
385
            ->setConfig("datavalueformat", "yyyy-MM-dd")
386
            ->setDescription(_t("ContentReview.NEXTREVIEWDATADESCRIPTION", "Leave blank for no review"));
387
388
        $reviewFrequency = DropdownField::create(
389
            "ReviewPeriodDays",
390
            _t("ContentReview.REVIEWFREQUENCY", "Review frequency"),
391
            self::get_schedule()
392
        )
393
            ->addExtraClass('custom-setting')
394
            ->setDescription(_t("ContentReview.REVIEWFREQUENCYDESCRIPTION", "The review date will be set to this far in the future whenever the page is published"));
395
396
        $reviewInfoField = TextareaField::create("ReviewInfo", _t("ContentReview.REVIEWINFO", "Review information"));
397
398
        $notesField = GridField::create("ReviewNotes", "Review Notes", $this->owner->ReviewLogs(), GridFieldConfig_RecordEditor::create());
399
400
        $fields->addFieldsToTab("Root.ContentReview", array(
401
            new HeaderField(_t("ContentReview.REVIEWHEADER", "Content review"), 2),
402
            $viewersOptionsField,
403
            CompositeField::create(
404
                $userField,
405
                $groupField,
406
                $reviewDate,
407
                $reviewFrequency
408
            )->addExtraClass("review-settings"),
409
            ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames()),
410
            $reviewInfoField,
411
            $notesField,
412
        ));
413
    }
414
415
    /**
416
     * Creates a ContentReviewLog and connects it to this Page.
417
     *
418
     * @param Member $reviewer
419
     * @param string $message
420
     */
421
    public function addReviewNote(Member $reviewer, $message, $reviewInfo = null)
422
    {
423
        $reviewLog = ContentReviewLog::create();
424
        $reviewLog->Note = $message;
0 ignored issues
show
Documentation introduced by
The property Note does not exist on object<ContentReviewLog>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
425
        $reviewLog->ReviewerID = $reviewer->ID;
0 ignored issues
show
Documentation introduced by
The property ReviewerID does not exist on object<ContentReviewLog>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
426
        if ($reviewInfo) {
427
            $reviewLog->ReviewInfo = $reviewInfo;
0 ignored issues
show
Documentation introduced by
The property ReviewInfo does not exist on object<ContentReviewLog>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
428
        }
429
        $this->owner->ReviewLogs()->add($reviewLog);
430
    }
431
432
    /**
433
     * Advance review date to the next date based on review period or set it to null
434
     * if there is no schedule. Returns true if date was required and false is content
435
     * review is 'off'.
436
     *
437
     * @return bool
438
     */
439
    public function advanceReviewDate()
440
    {
441
        $nextDate = false;
442
        $options = $this->getOptions();
443
444
        if ($options && $options->ReviewPeriodDays > 0) {
445
            $nextDate = date('Y-m-d', strtotime('+ ' . $options->ReviewPeriodDays . ' days', SS_Datetime::now()->format('U')));
446
447
            $this->owner->NextReviewDate = $nextDate;
448
            $this->owner->write();
449
        } else {
450
            $this->owner->NextReviewDate = null;
451
            $this->owner->write();
452
        }
453
454
        return (bool) $nextDate;
455
    }
456
    
457
    public function canRemind(Member $member = null) {
458
        if (!$this->owner->obj("NextReviewDate")->exists()) {
459
            return false;
460
        }
461
462
        // If today is not the date of the first reminder, return false
463
        $config = SiteConfig::current_site_config();
464
        $firstReview = $config->FirstReviewDaysBefore;
465
        $now = SS_Datetime::now();
466
        $notifyDate1 = date('Y-m-d', strtotime($this->owner->NextReviewDate . ' -' . $firstReview . ' days'));
467
468
        // If today is not the first reminder date
469
        if (!$notifyDate1 == $now->URLDate()) {
470
            return false;
471
        }
472
        
473
        $options = $this->getOptions();
474
475 View Code Duplication
        if (!$options) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
476
            return false;
477
        } elseif ($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) {
478
            return false;
479
        }
480
481
        if (!$member) {
482
            return true;
483
        }
484
485
        if ($member->inGroups($options->OwnerGroups())) {
486
            return true;
487
        }
488
489
        if ($options->OwnerUsers()->find("ID", $member->ID)) {
490
            return true;
491
        }
492
493
        return false;
494
    }
495
    
496
    /**
497
     * Check if a review is due by a member for this owner.
498
     *
499
     * @param Member $member
500
     *
501
     * @return bool
502
     */
503
    public function canBeReviewedBy(Member $member = null)
504
    {
505
        if (!$this->owner->obj("NextReviewDate")->exists()) {
506
            return false;
507
        }
508
509
        if ($this->owner->obj("NextReviewDate")->InFuture()) {
510
            return false;
511
        }
512
513
        $options = $this->getOptions();
514
515 View Code Duplication
        if (!$options) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
516
            return false;
517
        } elseif ($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) {
518
			return false;
519
        }
520
521
        if (!$member) {
522
            return true;
523
        }
524
525
        if ($member->inGroups($options->OwnerGroups())) {
526
            return true;
527
        }
528
529
        if ($options->OwnerUsers()->find("ID", $member->ID)) {
530
            return true;
531
        }
532
533
        return false;
534
    }
535
536
    /**
537
     * Set the review data from the review period, if set.
538
     */
539
    public function onBeforeWrite()
540
    {
541
        // Only update if DB fields have been changed
542
        $changedFields = $this->owner->getChangedFields(true, 2);
543
        if($changedFields) {
544
            $this->owner->LastEditedByName = $this->owner->getEditorName();
545
            $this->owner->OwnerNames = $this->owner->getOwnerNames();
546
        }
547
548
        // If the user changed the type, we need to recalculate the review date.
549
        if ($this->owner->isChanged("ContentReviewType", 2)) {
550
            if ($this->owner->ContentReviewType == "Disabled") {
551
                $this->setDefaultReviewDateForDisabled();
552
            } elseif ($this->owner->ContentReviewType == "Custom") {
553
                $this->setDefaultReviewDateForCustom();
554
            } else {
555
                $this->setDefaultReviewDateForInherited();
556
            }
557
        }
558
559
        // Ensure that a inherited page always have a next review date
560
        if ($this->owner->ContentReviewType == "Inherit" && !$this->owner->NextReviewDate) {
561
            $this->setDefaultReviewDateForInherited();
562
        }
563
564
        // We need to update all the child pages that inherit this setting. We can only
565
        // change children after this record has been created, otherwise the stageChildren
566
        // method will grab all pages in the DB (this messes up unit testing)
567
        if (!$this->owner->exists()) {
568
            return;
569
        }
570
571
        // parent page change its review period
572
        // && !$this->owner->isChanged('ContentReviewType', 2)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
573
        if ($this->owner->isChanged("ReviewPeriodDays", 2)) {
574
            $nextReviewUnixSec = strtotime(" + " . $this->owner->ReviewPeriodDays . " days", SS_Datetime::now()->format("U"));
575
            $this->owner->NextReviewDate = date("Y-m-d", $nextReviewUnixSec);
576
        }
577
    }
578
579
    private function setDefaultReviewDateForDisabled()
580
    {
581
        $this->owner->NextReviewDate = null;
582
    }
583
584
    protected function setDefaultReviewDateForCustom()
585
    {
586
        // Don't overwrite existing value
587
        if ($this->owner->NextReviewDate) {
588
            return;
589
        }
590
591
        $this->owner->NextReviewDate = null;
592
        $nextDate = $this->getReviewDate();
593
594 View Code Duplication
        if (is_object($nextDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
595
            $this->owner->NextReviewDate = $nextDate->getValue();
596
        } else {
597
            $this->owner->NextReviewDate = $nextDate;
598
        }
599
    }
600
601
    protected function setDefaultReviewDateForInherited()
602
    {
603
        // Don't overwrite existing value
604
        if ($this->owner->NextReviewDate) {
605
            return;
606
        }
607
608
        $options = $this->getOptions();
609
        $nextDate = null;
610
611
        if ($options instanceof SiteTree) {
612
            $nextDate = $this->getReviewDate($options);
613
        } elseif ($options instanceof SiteConfig) {
614
            $nextDate = $this->getReviewDate();
615
        }
616
617 View Code Duplication
        if (is_object($nextDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
618
            $this->owner->NextReviewDate = $nextDate->getValue();
619
        } else {
620
            $this->owner->NextReviewDate = $nextDate;
621
        }
622
    }
623
624
    /**
625
     * Provide permissions to the CMS.
626
     *
627
     * @return array
628
     */
629
    public function providePermissions()
630
    {
631
        return array(
632
            "EDIT_CONTENT_REVIEW_FIELDS" => array(
633
                "name"     => "Set content owners and review dates",
634
                "category" => _t("Permissions.CONTENT_CATEGORY", "Content permissions"),
635
                "sort"     => 50,
636
            ),
637
        );
638
    }
639
640
    /**
641
     * If the queued jobs module is installed, queue up the first job for 9am tomorrow morning
642
     * (by default).
643
     */
644
    public function requireDefaultRecords()
645
    {
646
        if (class_exists("ContentReviewNotificationJob")) {
647
            // Ensure there is not already a job queued
648
            if (QueuedJobDescriptor::get()->filter("Implementation", "ContentReviewNotificationJob")->first()) {
649
                return;
650
            }
651
652
            $nextRun = new ContentReviewNotificationJob();
653
            $runHour = Config::inst()->get("ContentReviewNotificationJob", "first_run_hour");
654
            $firstRunTime = date("Y-m-d H:i:s", mktime($runHour, 0, 0, date("m"), date("d") + 1, date("y")));
655
656
            singleton("QueuedJobService")->queueJob(
657
                $nextRun,
658
                $firstRunTime
659
            );
660
661
            DB::alteration_message(sprintf("Added ContentReviewNotificationJob to run at %s", $firstRunTime));
662
        }
663
    }
664
}
665