diff --git a/_config/config.yml b/_config/config.yml index 170774e..b901cde 100644 --- a/_config/config.yml +++ b/_config/config.yml @@ -1,3 +1,9 @@ SiteTree: extensions: - SiteTreeContentReview +Group: + extensions: + - ContentReviewOwner +Member: + extensions: + - ContentReviewOwner \ No newline at end of file diff --git a/code/ContentReviewEmails.php b/code/ContentReviewEmails.php index 7bf9c45..90fe9c6 100644 --- a/code/ContentReviewEmails.php +++ b/code/ContentReviewEmails.php @@ -19,14 +19,23 @@ class ContentReviewEmails extends BuildTask { Subsite::$disable_subsite_filter = true; } - $pages = DataObject::get('Page', "\"SiteTree\".\"NextReviewDate\" = '".(class_exists('SS_Datetime') ? SS_Datetime::now()->URLDate() : SSDatetime::now()->URLDate())."' AND \"SiteTree\".\"ContentReviewOwnerID\" != 0"); + $now = class_exists('SS_Datetime') ? SS_Datetime::now()->URLDate() : SSDatetime::now()->URLDate(); + + $pages = Page::get('Page') + ->leftJoin('Group_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerGroups"."SiteTreeID"', 'OwnerGroups') + ->leftJoin('Member_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerUsers"."SiteTreeID"', "OwnerUsers") + ->where('"SiteTree"."NextReviewDate" <= \''.$now.'\' AND' .' ("OwnerGroups"."ID" IS NOT NULL OR "OwnerUsers"."ID" IS NOT NULL)') + ; + if ($pages && $pages->Count()) { foreach($pages as $page) { - $owner = $page->ContentReviewOwner(); - if ($owner) { - $sender = Security::findAnAdministrator(); - $recipient = $owner; - + $owners = $page->ContentReviewOwners(); + if(!$owners->count()) { + continue; + } + $sender = Security::findAnAdministrator(); + + foreach($owners as $recipient) { $subject = sprintf(_t('ContentReviewEmails.SUBJECT', 'Page %s due for content review'), $page->Title); $email = new Email(); diff --git a/code/SiteTreeContentReview.php b/code/SiteTreeContentReview.php index 6a3a508..3517751 100644 --- a/code/SiteTreeContentReview.php +++ b/code/SiteTreeContentReview.php @@ -23,18 +23,25 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider * * @var array */ - private static $has_one = array( - 'ContentReviewOwner' => 'Member', + private static $belongs_many_many = array( + 'ContentReviewGroups' => 'Group', + 'ContentReviewUsers' => 'Member' ); - + /** * * @return string */ - public function getOwnerName() { - if($this->owner->ContentReviewOwnerID && $this->owner->ContentReviewOwner()) { - return $this->owner->ContentReviewOwner()->FirstName . ' ' . $this->owner->ContentReviewOwner()->Surname; + public function getOwnerNames() { + $names = array(); + foreach($this->DirectGroups() as $group) { + $names[] = $group->Title; } + + foreach($this->DirectUsers() as $group) { + $names[] = $group->getName(); + } + return implode(', ', $names); } /** @@ -47,6 +54,53 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider } return NULL; } + + /** + * Get all Members that are Content Owners to this page + * + * This includes checking group hierarchy and adding any direct users + * + * @return \ArrayList + */ + public function ContentReviewOwners() { + + $contentReviewOwners = new ArrayList(); + + $toplevelGroups = $this->DirectGroups(); + if($toplevelGroups) { + $groupIDs = array(); + foreach($toplevelGroups as $group) { + $familyIDs = $group->collateFamilyIDs(); + if(is_array($familyIDs)) { + $groupIDs = array_merge($groupIDs, array_values($familyIDs)); + } + } + if(count($groupIDs)) { + $groupMembers = DataObject::get('Member')->where("\"Group\".\"ID\" IN (" . implode(",",$groupIDs) . ")") + ->leftJoin("Group_Members", "\"Member\".\"ID\" = \"Group_Members\".\"MemberID\"") + ->leftJoin("Group", "\"Group_Members\".\"GroupID\" = \"Group\".\"ID\""); + $contentReviewOwners->merge($groupMembers); + } + + } + $contentReviewOwners->merge($this->DirectUsers()); + $contentReviewOwners->removeDuplicates(); + return $contentReviewOwners; + } + + /** + * @return ManyManyList + */ + public function DirectGroups() { + return $this->owner->getManyManyComponents('ContentReviewGroups'); + } + + /** + * @return ManyManyList + */ + public function DirectUsers() { + return $this->owner->getManyManyComponents('ContentReviewUsers'); + } /** * @@ -54,21 +108,48 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider * @return void */ public function updateCMSFields(FieldList $fields) { - if(Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) { + if(!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) { return; } - $cmsUsers = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN")); + $users = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN")); + + $usersMap = array(); + foreach($users as $user) { + // Listboxfield values are escaped, use ASCII char instead of » + $usersMap[$user->ID] = $user->getTitle(); + } + asort($usersMap); + + $userField = ListboxField::create('DirectUsers', _t("ContentReview.PAGEOWNERUSERS", "Users")) + ->setMultiple(true) + ->setSource($usersMap) + ->setAttribute('data-placeholder', _t('ContentReview.ADDUSERS', 'Add users')) + ->setDescription(_t('ContentReview.OWNERUSERSDESCRIPTION', 'Page owners that are responsible for reviews')); - $fields->addFieldsToTab("Root.Review", array( - new HeaderField(_t('SiteTreeCMSWorkflow.REVIEWHEADER', "Content review"), 2), - new DropdownField("ContentReviewOwnerID", _t("SiteTreeCMSWorkflow.PAGEOWNER", - "Page owner (will be responsible for reviews)"), $cmsUsers->map('ID', 'Title', '(no owner)')), - DateField::create( - "NextReviewDate", - _t("SiteTreeCMSWorkflow.NEXTREVIEWDATE", "Next review date (leave blank for no review)") - )->setConfig('showcalendar', true)->setConfig('dateformat', 'yyyy-MM-dd')->setConfig('datavalueformat', 'yyyy-MM-dd'), - new DropdownField("ReviewPeriodDays", _t("SiteTreeCMSWorkflow.REVIEWFREQUENCY", - "Review frequency (the review date will be set to this far in the future whenever the page is published.)"), array( + $groupsMap = array(); + foreach(Group::get() as $group) { + // Listboxfield values are escaped, use ASCII char instead of » + $groupsMap[$group->ID] = $group->getBreadcrumbs(' > '); + } + asort($groupsMap); + $groupField = ListboxField::create('DirectGroups', _t("ContentReview.PAGEOWNERGROUPS", "Groups")) + ->setMultiple(true) + ->setSource($groupsMap) + ->setAttribute('data-placeholder', _t('ContentReview.ADDGROUP', 'Add groups')) + ->setDescription(_t('ContentReview.OWNERGROUPSDESCRIPTION', 'Page owners that are responsible for reviews')); + + $reviewDate = DateField::create( + "NextReviewDate", + _t("ContentReview.NEXTREVIEWDATE", "Next review date") + )->setConfig('showcalendar', true) + ->setConfig('dateformat', 'yyyy-MM-dd') + ->setConfig('datavalueformat', 'yyyy-MM-dd') + ->setDescription(_t('ContentReview.NEXTREVIEWDATADESCRIPTION', 'Leave blank for no review')); + + $reviewFrequency = DropdownField::create( + "ReviewPeriodDays", + _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), + array( 0 => "No automatic review date", 1 => "1 day", 7 => "1 week", @@ -79,7 +160,15 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider 152 => "5 months", 183 => "6 months", 365 => "12 months", - )), + ) + )->setDescription(_t('ContentReview.REVIEWFREQUENCYDESCRIPTION', 'The review date will be set to this far in the future whenever the page is published')); + + $fields->addFieldsToTab("Root.Review", array( + new HeaderField(_t('ContentReview.REVIEWHEADER', "Content review"), 2), + $userField, + $groupField, + $reviewDate, + $reviewFrequency, new TextareaField('ReviewNotes', 'Review Notes') )); } @@ -92,7 +181,7 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider $this->owner->NextReviewDate = date('Y-m-d', strtotime('+' . $this->owner->ReviewPeriodDays . ' days')); } $this->owner->LastEditedByName=$this->owner->getEditorName(); - $this->owner->OwnerNames = $this->owner->getOwnerName(); + $this->owner->OwnerNames = $this->owner->getOwnerNames(); } /** diff --git a/code/extensions/ContentReviewOwner.php b/code/extensions/ContentReviewOwner.php new file mode 100644 index 0000000..9cec84a --- /dev/null +++ b/code/extensions/ContentReviewOwner.php @@ -0,0 +1,16 @@ + "SiteTree" + ); +} diff --git a/tests/ContentReviewTest.php b/tests/ContentReviewTest.php index 1f90a79..69971bd 100644 --- a/tests/ContentReviewTest.php +++ b/tests/ContentReviewTest.php @@ -8,21 +8,21 @@ class ContentReviewTest extends FunctionalTest { */ public static $fixture_file = 'contentreview/tests/ContentReviewTest.yml'; - public function testPermissions() { - $editor = $this->objFromFixture('Member', 'editor'); - $author = $this->objFromFixture('Member', 'author'); - - // Assert the permission code exists + public function testPermissionsExists() { $perms = singleton('SiteTreeContentReview')->providePermissions(); $this->assertTrue(isset($perms['EDIT_CONTENT_REVIEW_FIELDS'])); - - // Check a user with permission can edit fields + } + + public function testUserWithPermissionCanEdit() { + $editor = $this->objFromFixture('Member', 'editor'); $this->logInAs($editor); $page = new Page(); $fields = $page->getCMSFields(); $this->assertNotNull($fields->fieldByName('Root.Review')); - - // Check a user without permission can see tab + } + + public function testUserWithoutPermissionCannotEdit() { + $author = $this->objFromFixture('Member', 'author'); $this->logInAs($author); $page = new Page(); $fields = $page->getCMSFields(); @@ -83,23 +83,23 @@ class ContentReviewTest extends FunctionalTest { SS_Datetime::clear_mock_now(); } - public function testOwnerName() { + public function testOwnerNames() { $editor = $this->objFromFixture('Member', 'editor'); $this->logInAs($editor); $page = new Page(); $page->ReviewPeriodDays = 10; - $page->ContentReviewOwnerID = $editor->ID; + $page->ContentReviewUsers()->push($editor); $page->write(); $this->assertTrue($page->doPublish()); - $this->assertEquals($page->OwnerName, "Test Editor"); + $this->assertEquals($page->OwnerNames, "Test Editor", 'Test Editor should be the owner'); $page = $this->objFromFixture('Page', 'about'); $page->ContentReviewOwnerID = 0; $page->write(); $this->assertTrue($page->doPublish()); - $this->assertNull($page->OwnerName); + $this->assertEquals('', $page->OwnerNames); } } diff --git a/tests/ContentReviewTest.yml b/tests/ContentReviewTest.yml index 9d0e471..6739d92 100644 --- a/tests/ContentReviewTest.yml +++ b/tests/ContentReviewTest.yml @@ -42,7 +42,7 @@ Page: staff: Title: Staff NextReviewDate: 2010-02-14 - ContentReviewOwner: =>Member.author + ContentReviewUsers: =>Member.author contact: Title: Contact Us NextReviewDate: 2010-02-21 \ No newline at end of file