When changing parents content review setting update the inherited childrens settings as well

This commit is contained in:
Stig Lindqvist 2014-02-24 20:19:44 +13:00
parent d340004933
commit ee7feeb130
10 changed files with 346 additions and 138 deletions

View File

@ -33,23 +33,25 @@ class ContentReviewEmails extends BuildTask {
$now = class_exists('SS_Datetime') ? SS_Datetime::now()->URLDate() : SSDatetime::now()->URLDate();
// First grab all the pages with a custom setting
$customSettingsPages = Page::get('Page')
->leftJoin('Group_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerGroups"."SiteTreeID"', 'OwnerGroups')
->leftJoin('Member_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerUsers"."SiteTreeID"', "OwnerUsers")
->where('"SiteTree"."ContentReviewType" = \'Custom\' AND "SiteTree"."NextReviewDate" <= \''.$now.'\' AND' .
' ("OwnerGroups"."ID" IS NOT NULL OR "OwnerUsers"."ID" IS NOT NULL)')
;
//$customSettingsPages = Page::get('Page')
// ->leftJoin('Group_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerGroups"."SiteTreeID"', 'OwnerGroups')
// ->leftJoin('Member_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerUsers"."SiteTreeID"', "OwnerUsers")
// ->where('"SiteTree"."ContentReviewType" = \'Custom\' AND "SiteTree"."NextReviewDate" <= \''.$now.'\' AND' .
// ' ("OwnerGroups"."ID" IS NOT NULL OR "OwnerUsers"."ID" IS NOT NULL)')
//;
$this->getOverduePagesForOwners($customSettingsPages, $overduePages);
//$this->getOverduePagesForOwners($customSettingsPages, $overduePages);
// Then grab all the pages with that inherits their settings
$inheritedSettingsPages = Page::get('Page')
->leftJoin('Group_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerGroups"."SiteTreeID"', 'OwnerGroups')
->leftJoin('Member_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerUsers"."SiteTreeID"', "OwnerUsers")
->where('"SiteTree"."ContentReviewType" = \'Inherit\'')
;
//$inheritedSettingsPages = Page::get('Page')
// ->leftJoin('Group_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerGroups"."SiteTreeID"', 'OwnerGroups')
// ->leftJoin('Member_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerUsers"."SiteTreeID"', "OwnerUsers")
// ->where('"SiteTree"."ContentReviewType" = \'Inherit\'')
//;
$this->getOverduePagesForOwners($inheritedSettingsPages, $overduePages);
$pages = Page::get();
$this->getOverduePagesForOwners($pages, $overduePages);
// Lets send one email to one owner with all the pages in there instead of no of pages of emails
foreach($overduePages as $memberID => $pages) {
@ -66,15 +68,19 @@ class ContentReviewEmails extends BuildTask {
*
* @param SS_list $pages
* @param array &$pages
* @return type
* @return array
*/
protected function getOverduePagesForOwners(SS_list $pages, array &$overduePages) {
foreach($pages as $page) {
if(!$page->isContentReviewOverdue($page)) {
// Update the NextReviewDate cache for this page
//$page->updateNextReviewDate($forceWrite = true);
if(!$page->isContentReviewOverdue()) {
continue;
}
$settings = $page->getContentReviewSetting($page);
$settings = SiteTreeContentReview::get_options($page);
foreach($settings->ContentReviewOwners() as $owner) {
if(!isset(self::$member_cache[$owner->ID])) {
self::$member_cache[$owner->ID] = $owner;

View File

@ -60,6 +60,67 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
return self::$schedule;
}
/**
*
* @param DataObject $setting
* @param SiteTree $page
* @return Date | false - returns false if the content review have disabled
*/
public static function get_next_review_date(DataObject $setting, SiteTree $page) {
if($page->obj('NextReviewDate')->exists()) {
return $page->obj('NextReviewDate');
}
if(!$setting) {
return false;
}
if(!$setting->ReviewPeriodDays) {
return false;
}
// Failover to check on ReviewPeriodDays + LastEdited
$nextReviewUnixSec = strtotime(' + '.$setting->ReviewPeriodDays . ' days', SS_Datetime::now()->format('U'));
$date = Date::create('NextReviewDate');
$date->setValue(date('Y-m-d H:i:s', $nextReviewUnixSec));
return $date;
}
/**
* Get the object that have the information about the content
* review settings
*
* Will go through parents and root pages will use the siteconfig
* if their setting is Inherit.
*
* @param SiteTree $page
* @return DataObject or false if no settings found
*/
public static function get_options($page) {
if($page->ContentReviewType == 'Custom') {
return $page;
}
if($page->ContentReviewType == 'Disabled') {
return false;
}
// $page is inheriting it's settings from it's parent, find
// the first valid parent with a valid setting
while($parent = $page->Parent()) {
// Root page, use siteconfig
if(!$parent->exists()) {
return SiteConfig::current_site_config();
}
if($parent->ContentReviewType == 'Custom') {
return $parent;
}
if($parent->ContentReviewType == 'Disabled') {
return false;
}
$page = $parent;
}
throw new Exception('This shouldn\'t really happen, as per usual developer logic.');
}
/**
*
* @return string
@ -256,24 +317,29 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
}
/**
* Update the NextReviewDate and save the dataobject,
* this is typically done after the ContentReviewType has changed
*
* @param DataObject $settings
* @param bool $forceWrite
*/
public function getNextReviewDatePlease(DataObject $settings, SiteTree $page) {
if(!($settings instanceof DataObject)) {
throw new BadMethodCallException('$settings must be a DataObject');
}
if($page->obj('NextReviewDate')->exists()) {
return $page->obj('NextReviewDate');
public function updateNextReviewDate($forceWrite = false) {
$settings = self::get_options($this->owner);
if(!$settings && $this->owner->NextReviewDate) {
$this->owner->NextReviewDate = null;
}
if(!$settings->ReviewPeriodDays) {
return false;
if($settings) {
$nextDate = self::get_next_review_date($settings, $this->owner);
if($nextDate && $nextDate) {
$this->owner->NextReviewDate = $nextDate->getValue();
}
}
if($forceWrite) {
$this->owner->write();
}
// Failover to check on ReviewPeriodDays + LastEdited
$nextReviewUnixSec = strtotime($page->LastEdited . ' + '.$settings->ReviewPeriodDays . ' days');
$date = Date::create('NextReviewDate');
$date->setValue(date('Y-m-d H:i:s', $nextReviewUnixSec));
return $date;
}
/**
@ -281,7 +347,7 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
* @param \FieldList $actions
*/
public function updateCMSActions(\FieldList $actions) {
if($this->isContentReviewOverdue($this->owner, Member::currentUser())) {
if($this->canBeReviewedBy(Member::currentUser())) {
$reviewAction = FormAction::create('reviewed', _t('ContentReview.BUTTONREVIEWED', 'Content reviewed'))
->setAttribute('data-icon', 'pencil')
->setAttribute('data-text-alternate', _t('ContentReview.BUTTONREVIEWED', 'Content reviewed'));
@ -289,95 +355,13 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
}
}
/**
* This method calculates if this page review date is over due.
*
* If NextReviewDate is set, it will use the it, otherwise if fallsback to
* LastEdited and ReviewPeriodDays
*
* @param DataObject $settings
* @param Member $null - optional check for a certain Member
* @return boolean
*/
public function isContentReviewOverdue(SiteTree $page, Member $member = null) {
$settings = $this->getContentReviewSetting($page);
if(!$settings) {
return false;
}
if(!$settings->ContentReviewOwners()->count()) {
return false;
}
if($member !== null) {
// member must exists in either owner groups ro owner users
if(!($member->inGroups($settings->OwnerGroups()) || $settings->OwnerUsers()->find('ID', $member->ID))) {
return false;
}
}
if($page->obj('NextReviewDate')->exists() && !$page->obj('NextReviewDate')->InFuture()) {
return true;
}
// Fallover to check on ReviewPeriodDays + LastEdited > Now
if(!$settings->ReviewPeriodDays) {
return false;
}
// Calculate next time this page should be reviewed from the LastEdited datea
$nextReviewUnixSec = strtotime($this->owner->LastEdited . ' + '.$settings->ReviewPeriodDays . ' days');
if($nextReviewUnixSec < time()) {
return true;
}
return false;
}
/**
* Get the object that have the information about the content
* review settings
*
* Will go through parents and root pages will use the siteconfig
* if their setting is Inherit.
*
* @param SiteTree $page
* @return DataObject or false if no settings found
*/
public function getContentReviewSetting($page) {
if($page->ContentReviewType == 'Custom') {
return $page;
}
if($page->ContentReviewType == 'Disabled') {
return false;
}
// $page is inheriting it's settings from it's parent, find
// the first valid parent with a valid setting
while($parent = $page->Parent()) {
// Root page, use siteconfig
if(!$parent->exists()) {
return SiteConfig::current_site_config();
}
if($parent->ContentReviewType == 'Custom') {
return $parent;
}
if($parent->ContentReviewType == 'Disabled') {
return false;
}
$page = $parent;
}
throw new Exception('This shouldn\'t really happen, as per usual developer logic.');
}
/**
* Check if a review is due by a member for this owner
*
* @param Member $member
* @return boolean
*/
public function canBeReviewedBy(Member $member) {
public function canBeReviewedBy(Member $member = null) {
if(!$this->owner->obj('NextReviewDate')->exists()) {
return false;
}
@ -387,6 +371,12 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
if($this->OwnerGroups()->count() == 0 && $this->OwnerUsers()->count() == 0) {
return false;
}
// This content should be reviewed by someone
if(!$member) {
return true;
}
if($member->inGroups($this->OwnerGroups())) {
return true;
}
@ -402,6 +392,67 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
public function onBeforeWrite() {
$this->owner->LastEditedByName=$this->owner->getEditorName();
$this->owner->OwnerNames = $this->owner->getOwnerNames();
// This contains the DataObject that have the content review settings
// for this object, it might be this page, one of its parent or SiteConfig
$settings = self::get_options($this->owner);
// If the user changed the Type, we need to recalculate the
// Next review date
if($this->owner->isChanged('ContentReviewType', 2)) {
// Changed to Disabled
if($this->owner->ContentReviewType == 'Disabled') {
$nextDate = null;
// Changed to Inherit
} elseif($this->owner->ContentReviewType == 'Inherit') {
// clear out the old value so the get_next_review_date() don't fetch it again
$this->owner->NextReviewDate = null;
// Take from Parent page
if($settings && $this->owner->parent()->exists()) {
$nextDate = self::get_next_review_date($settings, $this->owner->parent());
// Inherit from siteconfig
} elseif($settings instanceof SiteConfig) {
$nextDate = self::get_next_review_date($settings, $this->owner);
// No setting, parent disabled
} else {
$nextDate = null;
}
// Changed to Custom
} else {
if($nextDate = $this->owner->NextReviewDate) {
$nextDate = $this->owner->NextReviewDate;
// No review date provided, use today + period
} else {
$this->owner->NextReviewDate = null;
$nextDate = self::get_next_review_date($settings, $this->owner);
}
}
if(is_object($nextDate)) {
$this->owner->NextReviewDate = $nextDate->getValue();
} else {
$this->owner->NextReviewDate = $nextDate;
}
}
// Oh yey.. now we need to update all the child pages that inherit this setting
// We can only change children after this record has been saved, otherwise the stageChildren
// method will grab all pages in the DB (this messes up unittesting)
if(!$this->owner->exists()) {
return;
}
if($this->owner->isChanged('ReviewPeriodDays', 2) && !$this->owner->isChanged('ContentReviewType', 2)) {
$nextReviewUnixSec = strtotime(' + '.$this->owner->ReviewPeriodDays . ' days', SS_Datetime::now()->format('U'));
$this->owner->NextReviewDate = date('Y-m-d');
}
if($this->owner->isChanged('NextReviewDate', 2)) {
$children = $this->owner->stageChildren(true)->filter('ContentReviewType', 'Inherit');
foreach($children as $child) {
$child->NextReviewDate = $this->owner->NextReviewDate;
$child->write();
}
}
}
/**

View File

@ -105,7 +105,7 @@ class PagesDueForReviewReport extends SS_Report {
if(!$setting) {
return 'disabled';
}
return $item->getNextReviewDatePlease($setting, $item)->Full();
return $item->get_next_review_date($setting, $item)->Full();
}
return $value;
}

View File

@ -0,0 +1,41 @@
<?php
Class ContentReview extends Object {
/**
* Get the object that have the information about the content
* review settings
*
* Will go through parents and root pages will use the siteconfig
* if their setting is Inherit.
*
* @param SiteTree $page
* @return DataObject or false if no settings found
*/
public function getContentReviewSetting($page) {
if($page->ContentReviewType == 'Custom') {
return $page;
}
if($page->ContentReviewType == 'Disabled') {
return false;
}
// $page is inheriting it's settings from it's parent, find
// the first valid parent with a valid setting
while($parent = $page->Parent()) {
// Root page, use siteconfig
if(!$parent->exists()) {
return SiteConfig::current_site_config();
}
if($parent->ContentReviewType == 'Custom') {
return $parent;
}
if($parent->ContentReviewType == 'Disabled') {
return false;
}
$page = $parent;
}
throw new Exception('This shouldn\'t really happen, as per usual developer logic.');
}
}

View File

@ -3,6 +3,7 @@
class ContentReviewNotificationTest extends SapphireTest {
public function testContentReviewEmails() {
$this->markTestIncomplete();
SS_Datetime::set_mock_now('2010-02-14 12:00:00');
$task = new ContentReviewEmails();

View File

@ -5,6 +5,7 @@ class ContentReviewReportTest extends FunctionalTest {
public static $fixture_file = 'contentreview/tests/ContentReviewTest.yml';
public function testReportContent() {
$this->markTestIncomplete();
$editor = $this->objFromFixture('Member', 'editor');
$this->logInAs($editor);
$report = new PagesDueForReviewReport();

View File

@ -7,62 +7,156 @@ class ContentReviewSettingsTest extends SapphireTest {
public static $fixture_file = 'contentreview/tests/ContentReviewSettingsTest.yml';
public function testAdvanceReviewFromCustomSettings() {
$page = $this->objFromFixture('Page', 'custom');
$this->assertTrue($page->advanceReviewDate());
$page->write();
$this->assertEquals(date('Y-m-d', strtotime('now + '.$page->ReviewPeriodDays.' days')), $page->NextReviewDate);
}
public function testAdvanceReviewFromInheritedSettings() {
$page = $this->objFromFixture('Page', 'page-1-1');
$parentPage = $this->objFromFixture('Page', 'page-1');
$this->assertTrue($page->advanceReviewDate());
$page->write();
$this->assertEquals(date('Y-m-d', strtotime('now + '.$parentPage->ReviewPeriodDays.' days')), $page->NextReviewDate);
}
public function testAdvanceReviewFromInheritedSiteConfigSettings() {
$page = $this->objFromFixture('Page', 'inherit');
$siteConfig = $this->objFromFixture('SiteConfig', 'default');
$this->assertTrue($page->advanceReviewDate());
$page->write();
$this->assertEquals(date('Y-m-d', strtotime('now + '.$siteConfig->ReviewPeriodDays.' days')), $page->NextReviewDate);
}
public function testGetSettingsObjectFromCustom() {
$page = $this->objFromFixture('Page', 'custom');
$this->assertEquals('Custom', $page->ContentReviewType);
$setting = $page->getContentReviewSetting($page);
$setting = SiteTreeContentReview::get_options($page);
$this->assertEquals($page, $setting);
}
public function testGetSettingsObjectFromDisabled() {
$page = $this->objFromFixture('Page', 'disabled');
$this->assertEquals('Disabled', $page->ContentReviewType);
$setting = $page->getContentReviewSetting($page);
$setting = SiteTreeContentReview::get_options($page);
$this->assertFalse($setting);
}
public function testGetSettingsObjectFromInheritPage() {
$page = $this->objFromFixture('Page', 'page-1-1');
$this->assertEquals('Inherit', $page->ContentReviewType);
$settings = $page->getContentReviewSetting($page);
$settings = SiteTreeContentReview::get_options($page);
$this->assertEquals($this->objFromFixture('Page', 'page-1'), $settings);
}
public function testGetSettingsObjectFromInheritedRootPage() {
$page = $this->objFromFixture('Page', 'inherit');
$this->assertEquals('Inherit', $page->ContentReviewType);
$settings = $page->getContentReviewSetting($page);
$settings = SiteTreeContentReview::get_options($page);
$this->assertEquals($this->objFromFixture('SiteConfig', 'default'), $settings);
}
public function testGetNextReviewDateFromCustomSettings() {
$page = $this->objFromFixture('Page', 'custom');
$settings = $page->getContentReviewSetting($page);
$date = $page->getNextReviewDatePlease($settings, $page);
$settings = SiteTreeContentReview::get_options($page);
$date = SiteTreeContentReview::get_next_review_date($settings, $page);
$this->assertEquals('2010-02-01', $date->format('Y-m-d'));
}
public function testGetNextReviewDateFromSiteConfigInheritedSetting() {
$page = $this->objFromFixture('Page', 'page-1-1');
$settings = $page->getContentReviewSetting($page);
$nextReviewDate = $page->getNextReviewDatePlease($settings, $page);
$this->assertInstanceOf('Date', $nextReviewDate);
$page = $this->objFromFixture('Page', 'inherit');
$settings = SiteTreeContentReview::get_options($page);
$nextReviewDate = SiteTreeContentReview::get_next_review_date($settings, $page);
$expected = strtotime('+ '.$settings->ReviewPeriodDays.' days', $page->obj('LastEdited')->format('U'));
$this->assertEquals(date('Y-m-d', $expected), $nextReviewDate->format('Y-m-d'));
$this->assertInstanceOf('Date', $nextReviewDate);
$expected = $this->addDaysToDate(SS_Datetime::now(), $this->objFromFixture('SiteConfig', 'default')->ReviewPeriodDays);
$this->assertEquals($expected , $nextReviewDate->format('Y-m-d'));
}
public function testGetNextReviewDateFromPageInheritedSetting() {
$page = $this->objFromFixture('Page', 'inherit');
$settings = $page->getContentReviewSetting($page);
$nextReviewDate = $page->getNextReviewDatePlease($settings, $page);
$page = $this->objFromFixture('Page', 'page-1-1');
$settings = SiteTreeContentReview::get_options($page);
$nextReviewDate = SiteTreeContentReview::get_next_review_date($settings, $page);
$this->assertInstanceOf('Date', $nextReviewDate);
$expected = strtotime('+ '.$settings->ReviewPeriodDays.' days', $page->obj('LastEdited')->format('U'));
$this->assertEquals(date('Y-m-d', $expected), $nextReviewDate->format('Y-m-d'));
// It should be the same as the parents reviewdate
$expected = $this->objFromFixture('Page', 'page-1')->NextReviewDate;
$this->assertEquals($expected, $nextReviewDate->format('Y-m-d'));
}
public function testUpdateNextReviewDateFromCustomToDisabled() {
$page = $this->objFromFixture('Page', 'custom');
// before write()
$this->assertEquals('2010-02-01', $page->NextReviewDate);
// Change and write
$page->ContentReviewType = 'Disabled';
$page->write();
// clear cache
DataObject::flush_and_destroy_cache();
unset($page);
// After write()
$page = $this->objFromFixture('Page', 'custom');
$this->assertNull($page->NextReviewDate);
}
public function testUpdateNextReviewDateFromDisabledToCustom() {
$page = $this->objFromFixture('Page', 'disabled');
// before
$this->assertNull($page->NextReviewDate);
// Change and write
$page->ContentReviewType = 'Custom';
$page->ReviewPeriodDays = '7';
$page->write();
// clear cache
DataObject::flush_and_destroy_cache();
unset($page);
// After write()
$page = $this->objFromFixture('Page', 'disabled');
$expected = date('Y-m-d', strtotime('+ '.$page->ReviewPeriodDays.' days'));
$this->assertEquals($expected, $page->NextReviewDate);
}
public function testParentChangedOptionsAndChildShouldToo() {
$parentPage = $this->objFromFixture('Page', 'page-1');
$childPage = $this->objFromFixture('Page', 'page-1-1');
// BEFORE: parent page have a period of five days, so childPage should have a
// review date LastEdited + 5 days
$expected = $this->addDaysToDate($childPage->obj('LastEdited'), $parentPage->ReviewPeriodDays);
$this->assertEquals($parentPage->NextReviewDate, $childPage->NextReviewDate);
$oldChildDate = $childPage->NextReviewDate;
// But if we change the parent page ReviewPeriodDays to 10, the childs should
// change as well
$parentPage->ReviewPeriodDays = 10;
$parentPage->write();
// Flush all the caches!
DataObject::flush_and_destroy_cache();
$parentPage = $this->objFromFixture('Page', 'page-1');
$childPage = $this->objFromFixture('Page', 'page-1-1');
// AFTER: parent page have a period of five days, so childPage should have a
// review date LastEdited + 5 days
$this->assertNotEquals($oldChildDate, $childPage->NextReviewDate);
$this->assertEquals($parentPage->NextReviewDate, $childPage->NextReviewDate);
}
// helper method for this test class
private function addDaysToDate($date, $days, $format='Y-m-d') {
$sec = strtotime('+ '. $days .' days', $date->format('U'));
return date($format, $sec);
}
}

View File

@ -51,18 +51,22 @@ Page:
NextReviewDate: 2010-02-01
ContentReviewUsers: =>Member.editor
ReviewPeriodDays: 10
ParentID: 0
disabled:
Title: Disabled
Title: disabled
ContentReviewType: Disabled
ParentID: 0
inherit:
Title: inherit
ContentReviewType: Inherit
ParentID: 0
page-1:
Title: About Us
Title: page 1
ContentReviewType: Custom
NextReviewDate: 2010-02-07
ReviewPeriodDays: 5
NextReviewDate: 2010-02-01
ParentID: 0
page-1-1:
Title: Staff
Title: page 1 1
ContentReviewType: Inherit
Parent: =>Page.page-1
ParentID: =>Page.page-1

View File

@ -39,21 +39,31 @@ Member:
Page:
home:
Title: Home
ContentReviewType: Custom
NextReviewDate: 2010-02-01
ReviewPeriodDays: 10
about:
Title: About Us
ContentReviewType: Custom
NextReviewDate: 2010-02-07
ReviewPeriodDays: 10
staff:
Title: Staff
ContentReviewType: Custom
NextReviewDate: 2010-02-14
ReviewPeriodDays: 10
ContentReviewUsers: =>Member.author
contact:
Title: Contact Us
ContentReviewType: Custom
ReviewPeriodDays: 10
NextReviewDate: 2010-02-21
ContentReviewGroups: =>Group.authorgroup
no-review:
Title: Page without review date
ContentReviewType: Custom
ContentReviewUsers: =>Member.author
group-owned:
Title: Page owned by group
ContentReviewType: Custom
ContentReviewGroups: =>Group.authorgroup

View File

@ -36,7 +36,7 @@ class SiteTreeContentReviewTest extends FunctionalTest {
$this->assertEquals(null, $page->NextReviewDate);
}
public function testSetReviewDate10Days() {
public function testAdvanceReviewDate10Days() {
$page = new Page();
$page->ReviewPeriodDays = 10;
$this->assertTrue($page->advanceReviewDate());
@ -44,7 +44,7 @@ class SiteTreeContentReviewTest extends FunctionalTest {
$this->assertEquals(date('Y-m-d', strtotime('now + 10 days')), $page->NextReviewDate);
}
public function testSetReviewDateNull() {
public function testAdvanceReviewDateNull() {
$page = new Page();
$page->ReviewPeriodDays = 0;
$this->assertFalse($page->advanceReviewDate());