SiteTreeContentReview   F
last analyzed

Complexity

Total Complexity 71

Size/Duplication

Total Lines 589
Duplicated Lines 1.7 %

Coupling/Cohesion

Components 1
Dependencies 32

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 71
c 1
b 0
f 0
lcom 1
cbo 32
dl 10
loc 589
rs 1.3043

20 Methods

Rating   Name   Duplication   Size   Complexity  
A get_schedule() 0 4 1
B merge_owners() 0 32 5
A updateCMSActions() 0 13 2
B getReviewDate() 0 27 5
C getOptions() 0 33 7
A getOwnerNames() 0 20 4
A getEditorName() 0 10 2
A ContentReviewOwners() 0 7 1
A OwnerGroups() 0 4 1
A OwnerUsers() 0 4 1
B updateSettingsFields() 0 98 4
A addReviewNote() 0 7 1
A advanceReviewDate() 0 17 3
C canBeReviewedBy() 0 41 12
D onBeforeWrite() 0 42 9
A setDefaultReviewDateForDisabled() 0 4 1
A setDefaultReviewDateForCustom() 5 16 3
B setDefaultReviewDateForInherited() 5 22 5
A providePermissions() 0 10 1
A requireDefaultRecords() 0 20 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SiteTreeContentReview often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SiteTreeContentReview, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\ContentReview\Extensions;
4
5
use Exception;
6
use SilverStripe\CMS\Model\SiteTree;
7
use SilverStripe\ContentReview\Jobs\ContentReviewNotificationJob;
8
use SilverStripe\ContentReview\Models\ContentReviewLog;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Core\Injector\Injector;
11
use SilverStripe\Core\Manifest\ModuleLoader;
12
use SilverStripe\Forms\FieldList;
13
use SilverStripe\Forms\LiteralField;
14
use SilverStripe\Forms\FormAction;
15
use SilverStripe\Forms\CompositeField;
16
use SilverStripe\Forms\Tab;
17
use SilverStripe\Forms\DateField;
18
use SilverStripe\Forms\DateTimeField;
19
use SilverStripe\Forms\DropdownField;
20
use SilverStripe\Forms\GridField\GridField;
21
use SilverStripe\Forms\GridField\GridFieldConfig;
22
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
23
use SilverStripe\Forms\GridField\GridFieldDataColumns;
24
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
25
use SilverStripe\Forms\ListboxField;
26
use SilverStripe\Forms\OptionsetField;
27
use SilverStripe\Forms\ReadonlyField;
28
use SilverStripe\Forms\HeaderField;
29
use SilverStripe\ORM\ArrayList;
30
use SilverStripe\ORM\DataExtension;
31
use SilverStripe\ORM\DataObject;
32
use SilverStripe\ORM\DB;
33
use SilverStripe\ORM\FieldType\DBDatetime;
34
use SilverStripe\ORM\FieldType\DBDate;
35
use SilverStripe\ORM\SS_List;
36
use SilverStripe\Security\Group;
37
use SilverStripe\Security\Member;
38
use SilverStripe\Security\Permission;
39
use SilverStripe\Security\PermissionProvider;
40
use SilverStripe\Security\Security;
41
use SilverStripe\SiteConfig\SiteConfig;
42
use SilverStripe\View\Requirements;
43
use Symbiote\QueuedJobs\DataObjects\QueuedJobDescriptor;
44
use Symbiote\QueuedJobs\Services\QueuedJobService;
45
46
/**
47
 * Set dates at which content needs to be reviewed and provide a report and emails to alert
48
 * to content needing review.
49
 *
50
 * @property string $ContentReviewType
51
 * @property int    $ReviewPeriodDays
52
 * @property Date   $NextReviewDate
53
 * @property string $LastEditedByName
54
 * @property string $OwnerNames
55
 *
56
 * @method DataList ReviewLogs()
57
 * @method DataList ContentReviewGroups()
58
 * @method DataList ContentReviewUsers()
59
 */
60
class SiteTreeContentReview extends DataExtension implements PermissionProvider
61
{
62
    /**
63
     * @var array
64
     */
65
    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...
66
        "ContentReviewType" => "Enum('Inherit, Disabled, Custom', 'Inherit')",
67
        "ReviewPeriodDays"  => "Int",
68
        "NextReviewDate"    => "Date",
69
        "LastEditedByName"  => "Varchar(255)",
70
        "OwnerNames"        => "Varchar(255)",
71
    );
72
73
    /**
74
     * @var array
75
     */
76
    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...
77
        "ContentReviewType" => "Inherit",
78
    );
79
80
    /**
81
     * @var array
82
     */
83
    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...
84
        "ReviewLogs" => ContentReviewLog::class,
85
    );
86
87
    /**
88
     * @var array
89
     */
90
    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...
91
        "ContentReviewGroups" => Group::class,
92
        "ContentReviewUsers"  => Member::class,
93
    );
94
95
    /**
96
     * @var array
97
     */
98
    private static $schedule = array(
0 ignored issues
show
Unused Code introduced by
The property $schedule 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...
99
        0   => "No automatic review date",
100
        1   => "1 day",
101
        7   => "1 week",
102
        30  => "1 month",
103
        60  => "2 months",
104
        91  => "3 months",
105
        121 => "4 months",
106
        152 => "5 months",
107
        183 => "6 months",
108
        365 => "12 months",
109
    );
110
111
    /**
112
     * @return array
113
     */
114
    public static function get_schedule()
115
    {
116
        return Config::inst()->get(static::class, 'schedule');
117
    }
118
119
    /**
120
     * Takes a list of groups and members and return a list of unique member.
121
     *
122
     * @param SS_List $groups
123
     * @param SS_List $members
124
     *
125
     * @return ArrayList
126
     */
127
    public static function merge_owners(SS_List $groups, SS_List $members)
128
    {
129
        $contentReviewOwners = new ArrayList();
130
131
        if ($groups->count()) {
132
            $groupIDs = array();
133
134
            foreach ($groups as $group) {
135
                $familyIDs = $group->collateFamilyIDs();
136
137
                if (is_array($familyIDs)) {
138
                    $groupIDs = array_merge($groupIDs, array_values($familyIDs));
139
                }
140
            }
141
142
            array_unique($groupIDs);
143
144
            if (count($groupIDs)) {
145
                $groupMembers = DataObject::get(Member::class)->where("\"Group\".\"ID\" IN (" . implode(",", $groupIDs) . ")")
146
                    ->leftJoin("Group_Members", "\"Member\".\"ID\" = \"Group_Members\".\"MemberID\"")
147
                    /** @skipUpgrade */
148
                    ->leftJoin('Group', "\"Group_Members\".\"GroupID\" = \"Group\".\"ID\"");
149
150
                $contentReviewOwners->merge($groupMembers);
151
            }
152
        }
153
154
        $contentReviewOwners->merge($members);
155
        $contentReviewOwners->removeDuplicates();
156
157
        return $contentReviewOwners;
158
    }
159
160
    /**
161
     * @param FieldList $actions
162
     */
163
    public function updateCMSActions(FieldList $actions)
164
    {
165
        if (!$this->canBeReviewedBy(Security::getCurrentUser())) {
166
            return;
167
        }
168
169
        $module = ModuleLoader::getModule('silverstripe/contentreview');
170
        Requirements::css($module->getRelativeResourcePath('client/dist/styles/contentreview.css'));
171
        Requirements::javascript($module->getRelativeResourcePath('client/dist/js/contentreview.js'));
172
173
        $reviewTab = LiteralField::create('ContentReviewButton', $this->owner->renderWith(__CLASS__ . '_button'));
174
        $actions->insertAfter('MajorActions', $reviewTab);
175
    }
176
177
    /**
178
     * Returns false if the content review have disabled.
179
     *
180
     * @param SiteTree $page
181
     *
182
     * @return bool|Date
183
     */
184
    public function getReviewDate(SiteTree $page = null)
185
    {
186
        if ($page === null) {
187
            $page = $this->owner;
188
        }
189
190
        if ($page->obj('NextReviewDate')->exists()) {
191
            return $page->obj('NextReviewDate');
192
        }
193
194
        $options = $this->owner->getOptions();
195
196
        if (!$options) {
197
            return false;
198
        }
199
200
        if (!$options->ReviewPeriodDays) {
201
            return false;
202
        }
203
204
        // Failover to check on ReviewPeriodDays + LastEdited
205
        $nextReviewUnixSec = strtotime(' + ' . $options->ReviewPeriodDays . ' days', DBDatetime::now()->getTimestamp());
206
        $date = DBDate::create('NextReviewDate');
207
        $date->setValue($nextReviewUnixSec);
208
209
        return $date;
210
    }
211
212
    /**
213
     * Get the object that have the information about the content review settings. Either:
214
     *
215
     *  - a SiteTreeContentReview decorated object
216
     *  - the default SiteTree config
217
     *  - false if this page have it's content review disabled
218
     *
219
     * Will go through parents and root pages will use the site config if their setting is Inherit.
220
     *
221
     * @return bool|DataObject
222
     *
223
     * @throws Exception
224
     */
225
    public function getOptions()
226
    {
227
        if ($this->owner->ContentReviewType == "Custom") {
228
            return $this->owner;
229
        }
230
231
        if ($this->owner->ContentReviewType == "Disabled") {
232
            return false;
233
        }
234
235
        $page = $this->owner;
236
237
        // $page is inheriting it's settings from it's parent, find
238
        // the first valid parent with a valid setting
239
        while ($parent = $page->Parent()) {
240
            // Root page, use site config
241
            if (!$parent->exists()) {
242
                return SiteConfig::current_site_config();
243
            }
244
245
            if ($parent->ContentReviewType == "Custom") {
246
                return $parent;
247
            }
248
249
            if ($parent->ContentReviewType == "Disabled") {
250
                return false;
251
            }
252
253
            $page = $parent;
254
        }
255
256
        throw new Exception("This shouldn't really happen, as per usual developer logic.");
257
    }
258
259
    /**
260
     * @return string
261
     */
262
    public function getOwnerNames()
263
    {
264
        $options = $this->getOptions();
265
266
        $names = array();
267
268
        if (!$options) {
269
            return "";
270
        }
271
272
        foreach ($options->OwnerGroups() as $group) {
273
            $names[] = $group->getBreadcrumbs(" > ");
274
        }
275
276
        foreach ($options->OwnerUsers() as $group) {
277
            $names[] = $group->getName();
278
        }
279
280
        return implode(", ", $names);
281
    }
282
283
    /**
284
     * @return null|string
285
     */
286
    public function getEditorName()
287
    {
288
        $member = Security::getCurrentUser();
289
290
        if ($member) {
291
            return $member->getTitle();
292
        }
293
294
        return null;
295
    }
296
297
    /**
298
     * Get all Members that are Content Owners to this page. This includes checking group
299
     * hierarchy and adding any direct users.
300
     *
301
     * @return ArrayList
302
     */
303
    public function ContentReviewOwners()
304
    {
305
        return SiteTreeContentReview::merge_owners(
306
            $this->OwnerGroups(),
307
            $this->OwnerUsers()
308
        );
309
    }
310
311
    /**
312
     * @return ManyManyList
313
     */
314
    public function OwnerGroups()
315
    {
316
        return $this->owner->getManyManyComponents("ContentReviewGroups");
317
    }
318
319
    /**
320
     * @return ManyManyList
321
     */
322
    public function OwnerUsers()
323
    {
324
        return $this->owner->getManyManyComponents("ContentReviewUsers");
325
    }
326
327
    /**
328
     * @param FieldList $fields
329
     */
330
    public function updateSettingsFields(FieldList $fields)
331
    {
332
        $module = ModuleLoader::getModule('silverstripe/contentreview');
333
        Requirements::javascript($module->getRelativeResourcePath('client/dist/js/contentreview.js'));
334
335
        // Display read-only version only
336
        if (!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
337
            $schedule = self::get_schedule();
338
            $contentOwners = ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames());
339
            $nextReviewAt = DateField::create('RONextReviewDate', _t("ContentReview.NEXTREVIEWDATE", "Next review date"), $this->owner->NextReviewDate);
340
341
            if (!isset($schedule[$this->owner->ReviewPeriodDays])) {
342
                $reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[0]);
343
            } else {
344
                $reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[$this->owner->ReviewPeriodDays]);
345
            }
346
347
            $logConfig = GridFieldConfig::create()
348
                ->addComponent(Injector::inst()->create(GridFieldSortableHeader::class))
349
                ->addComponent($logColumns = Injector::inst()->create(GridFieldDataColumns::class));
350
351
            // Cast the value to the users preferred date format
352
            $logColumns->setFieldCasting(array(
353
                'Created' => DateTimeField::class . '->value',
354
            ));
355
356
            $logs = GridField::create("ROReviewNotes", "Review Notes", $this->owner->ReviewLogs(), $logConfig);
357
358
359
            $optionsFrom = ReadonlyField::create("ROType", _t("ContentReview.SETTINGSFROM", "Options are"), $this->owner->ContentReviewType);
360
361
            $fields->addFieldsToTab("Root.ContentReview", array(
362
                $contentOwners,
363
                $nextReviewAt->performReadonlyTransformation(),
364
                $reviewFreq,
365
                $optionsFrom,
366
                $logs,
367
            ));
368
369
            return;
370
        }
371
372
        $options = array();
373
        $options["Disabled"] = _t("ContentReview.DISABLE", "Disable content review");
374
        $options["Inherit"] = _t("ContentReview.INHERIT", "Inherit from parent page");
375
        $options["Custom"] = _t("ContentReview.CUSTOM", "Custom settings");
376
377
        $viewersOptionsField = OptionsetField::create("ContentReviewType", _t("ContentReview.OPTIONS", "Options"), $options);
378
379
        $users = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN"));
380
381
        $usersMap = $users->map("ID", "Title")->toArray();
382
383
        asort($usersMap);
384
385
        $userField = ListboxField::create("OwnerUsers", _t("ContentReview.PAGEOWNERUSERS", "Users"), $usersMap)
386
            ->addExtraClass('custom-setting')
387
            ->setAttribute("data-placeholder", _t("ContentReview.ADDUSERS", "Add users"))
388
            ->setDescription(_t('ContentReview.OWNERUSERSDESCRIPTION', 'Page owners that are responsible for reviews'));
389
390
        $groupsMap = array();
391
392
        foreach (Group::get() as $group) {
393
            $groupsMap[$group->ID] = $group->getBreadcrumbs(" > ");
394
        }
395
        asort($groupsMap);
396
397
        $groupField = ListboxField::create("OwnerGroups", _t("ContentReview.PAGEOWNERGROUPS", "Groups"), $groupsMap)
398
            ->addExtraClass('custom-setting')
399
            ->setAttribute("data-placeholder", _t("ContentReview.ADDGROUP", "Add groups"))
400
            ->setDescription(_t("ContentReview.OWNERGROUPSDESCRIPTION", "Page owners that are responsible for reviews"));
401
402
        $reviewDate = DateField::create("NextReviewDate", _t("ContentReview.NEXTREVIEWDATE", "Next review date"))
403
            ->setDescription(_t("ContentReview.NEXTREVIEWDATADESCRIPTION", "Leave blank for no review"));
404
405
        $reviewFrequency = DropdownField::create(
406
            "ReviewPeriodDays",
407
            _t("ContentReview.REVIEWFREQUENCY", "Review frequency"),
408
            self::get_schedule()
409
        )
410
            ->addExtraClass('custom-setting')
411
            ->setDescription(_t("ContentReview.REVIEWFREQUENCYDESCRIPTION", "The review date will be set to this far in the future whenever the page is published"));
412
413
        $notesField = GridField::create("ReviewNotes", "Review Notes", $this->owner->ReviewLogs(), GridFieldConfig_RecordEditor::create());
414
415
        $fields->addFieldsToTab("Root.ContentReview", array(
416
            HeaderField::create('ContentReviewHeader', _t("ContentReview.REVIEWHEADER", "Content review"), 2),
417
            $viewersOptionsField,
418
            CompositeField::create(
419
                $userField,
420
                $groupField,
421
                $reviewDate,
422
                $reviewFrequency
423
            )->addExtraClass("review-settings"),
424
            ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames()),
425
            $notesField,
426
        ));
427
    }
428
429
    /**
430
     * Creates a ContentReviewLog and connects it to this Page.
431
     *
432
     * @param Member $reviewer
433
     * @param string $message
434
     */
435
    public function addReviewNote(Member $reviewer, $message)
436
    {
437
        $reviewLog = ContentReviewLog::create();
438
        $reviewLog->Note = $message;
0 ignored issues
show
Documentation introduced by
The property Note does not exist on object<SilverStripe\Cont...odels\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...
439
        $reviewLog->ReviewerID = $reviewer->ID;
0 ignored issues
show
Documentation introduced by
The property ReviewerID does not exist on object<SilverStripe\Cont...odels\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...
440
        $this->owner->ReviewLogs()->add($reviewLog);
441
    }
442
443
    /**
444
     * Advance review date to the next date based on review period or set it to null
445
     * if there is no schedule. Returns true if date was required and false is content
446
     * review is 'off'.
447
     *
448
     * @return bool
449
     */
450
    public function advanceReviewDate()
451
    {
452
        $nextDateTimestamp = false;
453
        $options = $this->getOptions();
454
455
        if ($options && $options->ReviewPeriodDays) {
456
            $nextDateTimestamp = strtotime(
457
                ' + ' . $options->ReviewPeriodDays . ' days',
458
                DBDatetime::now()->getTimestamp()
459
            );
460
461
            $this->owner->NextReviewDate = DBDate::create()->setValue($nextDateTimestamp)->Format('y-MM-dd');
462
            $this->owner->write();
463
        }
464
465
        return (bool) $nextDateTimestamp;
466
    }
467
468
    /**
469
     * Check if a review is due by a member for this owner.
470
     *
471
     * @param Member $member
472
     *
473
     * @return bool
474
     */
475
    public function canBeReviewedBy(Member $member = null)
476
    {
477
        if (!$this->owner->obj("NextReviewDate")->exists()) {
478
            return false;
479
        }
480
481
        if ($this->owner->obj("NextReviewDate")->InFuture()) {
482
            return false;
483
        }
484
485
        $options = $this->getOptions();
486
487
        if (!$options) {
488
            return false;
489
        }
490
491
        if (!$options
492
            // Options can be a SiteConfig with different extension applied
493
            || (!$options->hasExtension(__CLASS__) && !$options->hasExtension(ContentReviewDefaultSettings::class))
494
        ) {
495
            return false;
496
        }
497
498
        if ($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) {
499
            return false;
500
        }
501
502
        if (!$member) {
503
            return true;
504
        }
505
506
        if ($member->inGroups($options->OwnerGroups())) {
507
            return true;
508
        }
509
510
        if ($options->OwnerUsers()->find("ID", $member->ID)) {
511
            return true;
512
        }
513
514
        return false;
515
    }
516
517
    /**
518
     * Set the review data from the review period, if set.
519
     */
520
    public function onBeforeWrite()
521
    {
522
        // Only update if DB fields have been changed
523
        $changedFields = $this->owner->getChangedFields(true, 2);
524
        if ($changedFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $changedFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
525
            $this->owner->LastEditedByName = $this->owner->getEditorName();
0 ignored issues
show
Bug introduced by
The property LastEditedByName does not seem to exist. Did you mean LastEdited?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
526
            $this->owner->OwnerNames = $this->owner->getOwnerNames();
527
        }
528
529
        // If the user changed the type, we need to recalculate the review date.
530
        if ($this->owner->isChanged("ContentReviewType", 2)) {
531
            if ($this->owner->ContentReviewType == "Disabled") {
532
                $this->setDefaultReviewDateForDisabled();
533
            } elseif ($this->owner->ContentReviewType == "Custom") {
534
                $this->setDefaultReviewDateForCustom();
535
            } else {
536
                $this->setDefaultReviewDateForInherited();
537
            }
538
        }
539
540
        // Ensure that a inherited page always have a next review date
541
        if ($this->owner->ContentReviewType == "Inherit" && !$this->owner->NextReviewDate) {
542
            $this->setDefaultReviewDateForInherited();
543
        }
544
545
        // We need to update all the child pages that inherit this setting. We can only
546
        // change children after this record has been created, otherwise the stageChildren
547
        // method will grab all pages in the DB (this messes up unit testing)
548
        if (!$this->owner->exists()) {
549
            return;
550
        }
551
552
        // parent page change its review period
553
        // && !$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...
554
        if ($this->owner->isChanged('ReviewPeriodDays', 2)) {
555
            $nextReviewUnixSec = strtotime(
556
                ' + ' . $this->owner->ReviewPeriodDays . ' days',
557
                DBDatetime::now()->getTimestamp()
558
            );
559
            $this->owner->NextReviewDate = DBDate::create()->setValue($nextReviewUnixSec)->Format('y-MM-dd');
560
        }
561
    }
562
563
    private function setDefaultReviewDateForDisabled()
564
    {
565
        $this->owner->NextReviewDate = null;
566
    }
567
568
    protected function setDefaultReviewDateForCustom()
569
    {
570
        // Don't overwrite existing value
571
        if ($this->owner->NextReviewDate) {
572
            return;
573
        }
574
575
        $this->owner->NextReviewDate = null;
576
        $nextDate = $this->getReviewDate();
577
578 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...
579
            $this->owner->NextReviewDate = $nextDate->getValue();
580
        } else {
581
            $this->owner->NextReviewDate = $nextDate;
582
        }
583
    }
584
585
    protected function setDefaultReviewDateForInherited()
586
    {
587
        // Don't overwrite existing value
588
        if ($this->owner->NextReviewDate) {
589
            return;
590
        }
591
592
        $options = $this->getOptions();
593
        $nextDate = null;
594
595
        if ($options instanceof SiteTree) {
596
            $nextDate = $this->getReviewDate($options);
597
        } elseif ($options instanceof SiteConfig) {
598
            $nextDate = $this->getReviewDate();
599
        }
600
601 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...
602
            $this->owner->NextReviewDate = $nextDate->getValue();
603
        } else {
604
            $this->owner->NextReviewDate = $nextDate;
605
        }
606
    }
607
608
    /**
609
     * Provide permissions to the CMS.
610
     *
611
     * @return array
612
     */
613
    public function providePermissions()
614
    {
615
        return array(
616
            "EDIT_CONTENT_REVIEW_FIELDS" => array(
617
                "name"     => "Set content owners and review dates",
618
                "category" => _t("Permissions.CONTENT_CATEGORY", "Content permissions"),
619
                "sort"     => 50,
620
            ),
621
        );
622
    }
623
624
    /**
625
     * If the queued jobs module is installed, queue up the first job for 9am tomorrow morning
626
     * (by default).
627
     */
628
    public function requireDefaultRecords()
629
    {
630
        if (class_exists(ContentReviewNotificationJob::class)) {
631
            // Ensure there is not already a job queued
632
            if (QueuedJobDescriptor::get()->filter("Implementation", ContentReviewNotificationJob::class)->first()) {
633
                return;
634
            }
635
636
            $nextRun = Injector::inst()->create(ContentReviewNotificationJob::class);
637
            $runHour = Config::inst()->get(ContentReviewNotificationJob::class, "first_run_hour");
638
            $firstRunTime = date("Y-m-d H:i:s", mktime($runHour, 0, 0, date("m"), date("d") + 1, date("y")));
639
640
            singleton(QueuedJobService::class)->queueJob(
641
                $nextRun,
642
                $firstRunTime
643
            );
644
645
            DB::alteration_message(sprintf("Added ContentReviewNotificationJob to run at %s", $firstRunTime));
646
        }
647
    }
648
}
649