Completed
Push — master ( c3d26a...25c055 )
by Hamish
13s
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
    );
29
30
    /**
31
     * @var array
32
     */
33
    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...
34
        "ContentReviewType" => "Inherit",
35
    );
36
37
    /**
38
     * @var array
39
     */
40
    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...
41
        "ReviewLogs" => "ContentReviewLog",
42
    );
43
44
    /**
45
     * @var array
46
     */
47
    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...
48
        "ContentReviewGroups" => "Group",
49
        "ContentReviewUsers"  => "Member",
50
    );
51
52
    /**
53
     * @var array
54
     */
55
    private static $schedule = array(
56
        0   => "No automatic review date",
57
        1   => "1 day",
58
        7   => "1 week",
59
        30  => "1 month",
60
        60  => "2 months",
61
        91  => "3 months",
62
        121 => "4 months",
63
        152 => "5 months",
64
        183 => "6 months",
65
        365 => "12 months",
66
    );
67
68
    /**
69
     * @return array
70
     */
71
    public static function get_schedule()
72
    {
73
        return self::$schedule;
74
    }
75
76
    /**
77
     * Takes a list of groups and members and return a list of unique member.
78
     *
79
     * @param SS_List $groups
80
     * @param SS_List $members
81
     *
82
     * @return ArrayList
83
     */
84
    public static function merge_owners(SS_List $groups, SS_List $members)
85
    {
86
        $contentReviewOwners = new ArrayList();
87
88
        if ($groups->count()) {
89
            $groupIDs = array();
90
91
            foreach ($groups as $group) {
92
                $familyIDs = $group->collateFamilyIDs();
93
94
                if (is_array($familyIDs)) {
95
                    $groupIDs = array_merge($groupIDs, array_values($familyIDs));
96
                }
97
            }
98
99
            array_unique($groupIDs);
100
101
            if (count($groupIDs)) {
102
                $groupMembers = DataObject::get("Member")->where("\"Group\".\"ID\" IN (" . implode(",", $groupIDs) . ")")
103
                    ->leftJoin("Group_Members", "\"Member\".\"ID\" = \"Group_Members\".\"MemberID\"")
104
                    ->leftJoin("Group", "\"Group_Members\".\"GroupID\" = \"Group\".\"ID\"");
105
106
                $contentReviewOwners->merge($groupMembers);
107
            }
108
        }
109
110
        $contentReviewOwners->merge($members);
111
        $contentReviewOwners->removeDuplicates();
112
113
        return $contentReviewOwners;
114
    }
115
116
    /**
117
     * @param FieldList $actions
118
     */
119
    public function updateCMSActions(FieldList $actions)
120
    {
121
        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...
122
            Requirements::css("contentreview/css/contentreview.css");
123
124
            $reviewTitle = LiteralField::create(
125
                "ReviewContentNotesLabel",
126
                "<label class=\"left\" for=\"Form_EditForm_ReviewNotes\">" . _t("ContentReview.CONTENTREVIEW", "Content due for review") . "</label>"
127
            );
128
129
            $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>");
130
131
            $quickReviewAction = FormAction::create("savereview", _t("ContentReview.MARKREVIEWED", "Mark as reviewed"))
132
                ->setAttribute("data-icon", "pencil")
133
                ->setAttribute("data-text-alternate", _t("ContentReview.MARKREVIEWED", "Mark as reviewed"));
134
135
            $allFields = CompositeField::create($reviewTitle, $ReviewNotes, $quickReviewAction)
136
                ->addExtraClass('review-notes field');
137
138
            $reviewTab = Tab::create('ReviewContent', $allFields);
139
            $reviewTab->addExtraClass('contentreview-tab');
140
141
            $actions->fieldByName('ActionMenus')->insertBefore($reviewTab, 'MoreOptions');
142
        }
143
    }
144
145
    /**
146
     * Returns false if the content review have disabled.
147
     *
148
     * @param SiteTree $page
149
     *
150
     * @return bool|Date
151
     */
152
    public function getReviewDate(SiteTree $page = null)
153
    {
154
        if ($page === null) {
155
            $page = $this->owner;
156
        }
157
158
        if ($page->obj("NextReviewDate")->exists()) {
159
            return $page->obj("NextReviewDate");
160
        }
161
162
        $options = $this->owner->getOptions();
163
164
        if (!$options) {
165
            return false;
166
        }
167
168
        if (!$options->ReviewPeriodDays) {
169
            return false;
170
        }
171
172
        // Failover to check on ReviewPeriodDays + LastEdited
173
        $nextReviewUnixSec = strtotime(" + " . $options->ReviewPeriodDays . " days", SS_Datetime::now()->format("U"));
174
        $date = Date::create("NextReviewDate");
175
        $date->setValue(date("Y-m-d H:i:s", $nextReviewUnixSec));
176
177
        return $date;
178
    }
179
180
    /**
181
     * Get the object that have the information about the content review settings. Either:
182
     *
183
     *  - a SiteTreeContentReview decorated object
184
     *  - the default SiteTree config
185
     *  - false if this page have it's content review disabled
186
     *
187
     * Will go through parents and root pages will use the site config if their setting is Inherit.
188
     *
189
     * @return bool|DataObject
190
     *
191
     * @throws Exception
192
     */
193
    public function getOptions()
194
    {
195
        if ($this->owner->ContentReviewType == "Custom") {
196
            return $this->owner;
197
        }
198
199
        if ($this->owner->ContentReviewType == "Disabled") {
200
            return false;
201
        }
202
203
        $page = $this->owner;
204
205
        // $page is inheriting it's settings from it's parent, find
206
        // the first valid parent with a valid setting
207
        while ($parent = $page->Parent()) {
208
209
            // Root page, use site config
210
            if (!$parent->exists()) {
211
                return SiteConfig::current_site_config();
212
            }
213
214
            if ($parent->ContentReviewType == "Custom") {
215
                return $parent;
216
            }
217
218
            if ($parent->ContentReviewType == "Disabled") {
219
                return false;
220
            }
221
222
            $page = $parent;
223
        }
224
225
        throw new Exception("This shouldn't really happen, as per usual developer logic.");
226
    }
227
228
    /**
229
     * @return string
230
     */
231
    public function getOwnerNames()
232
    {
233
        $options = $this->getOptions();
234
235
        $names = array();
236
237
        if (!$options) {
238
            return "";
239
        }
240
241
        foreach ($options->OwnerGroups() as $group) {
242
            $names[] = $group->getBreadcrumbs(" > ");
243
        }
244
245
        foreach ($options->OwnerUsers() as $group) {
246
            $names[] = $group->getName();
247
        }
248
249
        return implode(", ", $names);
250
    }
251
252
    /**
253
     * @return null|string
254
     */
255
    public function getEditorName()
256
    {
257
        $member = Member::currentUser();
258
259
        if ($member) {
260
            return $member->getTitle();
261
        }
262
263
        return null;
264
    }
265
266
    /**
267
     * Get all Members that are Content Owners to this page. This includes checking group
268
     * hierarchy and adding any direct users.
269
     *
270
     * @return ArrayList
271
     */
272
    public function ContentReviewOwners()
273
    {
274
        return SiteTreeContentReview::merge_owners(
275
            $this->OwnerGroups(),
276
            $this->OwnerUsers()
277
        );
278
    }
279
280
    /**
281
     * @return ManyManyList
282
     */
283
    public function OwnerGroups()
284
    {
285
        return $this->owner->getManyManyComponents("ContentReviewGroups");
286
    }
287
288
    /**
289
     * @return ManyManyList
290
     */
291
    public function OwnerUsers()
292
    {
293
        return $this->owner->getManyManyComponents("ContentReviewUsers");
294
    }
295
296
    /**
297
     * @param FieldList $fields
298
     */
299
    public function updateSettingsFields(FieldList $fields)
300
    {
301
        Requirements::javascript("contentreview/javascript/contentreview.js");
302
303
        // Display read-only version only
304
        if (!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
305
            $schedule = self::get_schedule();
306
            $contentOwners = ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames());
307
            $nextReviewAt = DateField::create('RONextReviewDate', _t("ContentReview.NEXTREVIEWDATE", "Next review date"), $this->owner->NextReviewDate);
308
309
            if (!isset($schedule[$this->owner->ReviewPeriodDays])) {
310
                $reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[0]);
311
            } else {
312
                $reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[$this->owner->ReviewPeriodDays]);
313
            }
314
315
            $logConfig = GridFieldConfig::create()
316
                ->addComponent(new GridFieldSortableHeader())
317
                ->addComponent($logColumns = new GridFieldDataColumns());
318
319
            // Cast the value to the users preferred date format
320
            $logColumns->setFieldCasting(array(
321
                "Created" => "DateTimeField->value",
322
            ));
323
324
            $logs = GridField::create("ROReviewNotes", "Review Notes", $this->owner->ReviewLogs(), $logConfig);
325
326
327
            $optionsFrom = ReadonlyField::create("ROType", _t("ContentReview.SETTINGSFROM", "Options are"), $this->owner->ContentReviewType);
328
329
            $fields->addFieldsToTab("Root.ContentReview", array(
330
                $contentOwners,
331
                $nextReviewAt->performReadonlyTransformation(),
332
                $reviewFreq,
333
                $optionsFrom,
334
                $logs,
335
            ));
336
337
            return;
338
        }
339
340
        $options = array();
341
        $options["Disabled"] = _t("ContentReview.DISABLE", "Disable content review");
342
        $options["Inherit"] = _t("ContentReview.INHERIT", "Inherit from parent page");
343
        $options["Custom"] = _t("ContentReview.CUSTOM", "Custom settings");
344
345
        $viewersOptionsField = OptionsetField::create("ContentReviewType", _t("ContentReview.OPTIONS", "Options"), $options);
346
347
        $users = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN"));
348
349
        $usersMap = $users->map("ID", "Title")->toArray();
350
351
        asort($usersMap);
352
353
        $userField = ListboxField::create("OwnerUsers", _t("ContentReview.PAGEOWNERUSERS", "Users"), $usersMap)
354
            ->setMultiple(true)
355
            ->addExtraClass('custom-setting')
356
            ->setAttribute("data-placeholder", _t("ContentReview.ADDUSERS", "Add users"))
357
            ->setDescription(_t('ContentReview.OWNERUSERSDESCRIPTION', 'Page owners that are responsible for reviews'));
358
359
        $groupsMap = array();
360
361
        foreach (Group::get() as $group) {
362
            $groupsMap[$group->ID] = $group->getBreadcrumbs(" > ");
363
        }
364
        asort($groupsMap);
365
366
        $groupField = ListboxField::create("OwnerGroups", _t("ContentReview.PAGEOWNERGROUPS", "Groups"), $groupsMap)
367
            ->setMultiple(true)
368
            ->addExtraClass('custom-setting')
369
            ->setAttribute("data-placeholder", _t("ContentReview.ADDGROUP", "Add groups"))
370
            ->setDescription(_t("ContentReview.OWNERGROUPSDESCRIPTION", "Page owners that are responsible for reviews"));
371
372
        $reviewDate = DateField::create("NextReviewDate", _t("ContentReview.NEXTREVIEWDATE", "Next review date"))
373
            ->setConfig("showcalendar", true)
374
            ->setConfig("dateformat", "yyyy-MM-dd")
375
            ->setConfig("datavalueformat", "yyyy-MM-dd")
376
            ->setDescription(_t("ContentReview.NEXTREVIEWDATADESCRIPTION", "Leave blank for no review"));
377
378
        $reviewFrequency = DropdownField::create(
379
            "ReviewPeriodDays",
380
            _t("ContentReview.REVIEWFREQUENCY", "Review frequency"),
381
            self::get_schedule()
382
        )
383
            ->addExtraClass('custom-setting')
384
            ->setDescription(_t("ContentReview.REVIEWFREQUENCYDESCRIPTION", "The review date will be set to this far in the future whenever the page is published"));
385
386
        $notesField = GridField::create("ReviewNotes", "Review Notes", $this->owner->ReviewLogs(), GridFieldConfig_RecordEditor::create());
387
388
        $fields->addFieldsToTab("Root.ContentReview", array(
389
            new HeaderField(_t("ContentReview.REVIEWHEADER", "Content review"), 2),
390
            $viewersOptionsField,
391
            CompositeField::create(
392
                $userField,
393
                $groupField,
394
                $reviewDate,
395
                $reviewFrequency
396
            )->addExtraClass("review-settings"),
397
            ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames()),
398
            $notesField,
399
        ));
400
    }
401
402
    /**
403
     * Creates a ContentReviewLog and connects it to this Page.
404
     *
405
     * @param Member $reviewer
406
     * @param string $message
407
     */
408
    public function addReviewNote(Member $reviewer, $message)
409
    {
410
        $reviewLog = ContentReviewLog::create();
411
        $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...
412
        $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...
413
        $this->owner->ReviewLogs()->add($reviewLog);
414
    }
415
416
    /**
417
     * Advance review date to the next date based on review period or set it to null
418
     * if there is no schedule. Returns true if date was required and false is content
419
     * review is 'off'.
420
     *
421
     * @return bool
422
     */
423
    public function advanceReviewDate()
424
    {
425
        $nextDate = false;
426
        $options = $this->getOptions();
427
428
        if ($options && $options->ReviewPeriodDays) {
429
            $nextDate = date('Y-m-d', strtotime('+ ' . $options->ReviewPeriodDays . ' days', SS_Datetime::now()->format('U')));
430
431
            $this->owner->NextReviewDate = $nextDate;
432
            $this->owner->write();
433
        }
434
435
        return (bool) $nextDate;
436
    }
437
438
    /**
439
     * Check if a review is due by a member for this owner.
440
     *
441
     * @param Member $member
442
     *
443
     * @return bool
444
     */
445
    public function canBeReviewedBy(Member $member = null)
446
    {
447
        if (!$this->owner->obj("NextReviewDate")->exists()) {
448
            return false;
449
        }
450
451
        if ($this->owner->obj("NextReviewDate")->InFuture()) {
452
            return false;
453
        }
454
455
        $options = $this->getOptions();
456
457
        if ($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) {
458
            return false;
459
        }
460
461
        if (!$member) {
462
            return true;
463
        }
464
465
        if ($member->inGroups($options->OwnerGroups())) {
466
            return true;
467
        }
468
469
        if ($options->OwnerUsers()->find("ID", $member->ID)) {
470
            return true;
471
        }
472
473
        return false;
474
    }
475
476
    /**
477
     * Set the review data from the review period, if set.
478
     */
479
    public function onBeforeWrite()
480
    {
481
        // Only update if DB fields have been changed
482
        $changedFields = $this->owner->getChangedFields(true, 2);
483
        if($changedFields) {
484
            $this->owner->LastEditedByName = $this->owner->getEditorName();
485
            $this->owner->OwnerNames = $this->owner->getOwnerNames();
486
        }
487
488
        // If the user changed the type, we need to recalculate the review date.
489
        if ($this->owner->isChanged("ContentReviewType", 2)) {
490
            if ($this->owner->ContentReviewType == "Disabled") {
491
                $this->setDefaultReviewDateForDisabled();
492
            } elseif ($this->owner->ContentReviewType == "Custom") {
493
                $this->setDefaultReviewDateForCustom();
494
            } else {
495
                $this->setDefaultReviewDateForInherited();
496
            }
497
        }
498
499
        // Ensure that a inherited page always have a next review date
500
        if ($this->owner->ContentReviewType == "Inherit" && !$this->owner->NextReviewDate) {
501
            $this->setDefaultReviewDateForInherited();
502
        }
503
504
        // We need to update all the child pages that inherit this setting. We can only
505
        // change children after this record has been created, otherwise the stageChildren
506
        // method will grab all pages in the DB (this messes up unit testing)
507
        if (!$this->owner->exists()) {
508
            return;
509
        }
510
511
        // parent page change its review period
512
        // && !$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...
513
        if ($this->owner->isChanged("ReviewPeriodDays", 2)) {
514
            $nextReviewUnixSec = strtotime(" + " . $this->owner->ReviewPeriodDays . " days", SS_Datetime::now()->format("U"));
515
            $this->owner->NextReviewDate = date("Y-m-d", $nextReviewUnixSec);
516
        }
517
    }
518
519
    private function setDefaultReviewDateForDisabled()
520
    {
521
        $this->owner->NextReviewDate = null;
522
    }
523
524
    protected function setDefaultReviewDateForCustom()
525
    {
526
        // Don't overwrite existing value
527
        if ($this->owner->NextReviewDate) {
528
            return;
529
        }
530
531
        $this->owner->NextReviewDate = null;
532
        $nextDate = $this->getReviewDate();
533
534 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...
535
            $this->owner->NextReviewDate = $nextDate->getValue();
536
        } else {
537
            $this->owner->NextReviewDate = $nextDate;
538
        }
539
    }
540
541
    protected function setDefaultReviewDateForInherited()
542
    {
543
        // Don't overwrite existing value
544
        if ($this->owner->NextReviewDate) {
545
            return;
546
        }
547
548
        $options = $this->getOptions();
549
        $nextDate = null;
550
551
        if ($options instanceof SiteTree) {
552
            $nextDate = $this->getReviewDate($options);
553
        } elseif ($options instanceof SiteConfig) {
554
            $nextDate = $this->getReviewDate();
555
        }
556
557 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...
558
            $this->owner->NextReviewDate = $nextDate->getValue();
559
        } else {
560
            $this->owner->NextReviewDate = $nextDate;
561
        }
562
    }
563
564
    /**
565
     * Provide permissions to the CMS.
566
     *
567
     * @return array
568
     */
569
    public function providePermissions()
570
    {
571
        return array(
572
            "EDIT_CONTENT_REVIEW_FIELDS" => array(
573
                "name"     => "Set content owners and review dates",
574
                "category" => _t("Permissions.CONTENT_CATEGORY", "Content permissions"),
575
                "sort"     => 50,
576
            ),
577
        );
578
    }
579
580
    /**
581
     * If the queued jobs module is installed, queue up the first job for 9am tomorrow morning
582
     * (by default).
583
     */
584
    public function requireDefaultRecords()
585
    {
586
        if (class_exists("ContentReviewNotificationJob")) {
587
            // Ensure there is not already a job queued
588
            if (QueuedJobDescriptor::get()->filter("Implementation", "ContentReviewNotificationJob")->first()) {
589
                return;
590
            }
591
592
            $nextRun = new ContentReviewNotificationJob();
593
            $runHour = Config::inst()->get("ContentReviewNotificationJob", "first_run_hour");
594
            $firstRunTime = date("Y-m-d H:i:s", mktime($runHour, 0, 0, date("m"), date("d") + 1, date("y")));
595
596
            singleton("QueuedJobService")->queueJob(
597
                $nextRun,
598
                $firstRunTime
599
            );
600
601
            DB::alteration_message(sprintf("Added ContentReviewNotificationJob to run at %s", $firstRunTime));
602
        }
603
    }
604
}
605