Allow users and/or groups to be content owners

This commit is contained in:
Stig Lindqvist 2014-02-13 16:35:13 +13:00
parent d28a0eefa0
commit 131d643a8c
6 changed files with 160 additions and 40 deletions

View File

@ -1,3 +1,9 @@
SiteTree: SiteTree:
extensions: extensions:
- SiteTreeContentReview - SiteTreeContentReview
Group:
extensions:
- ContentReviewOwner
Member:
extensions:
- ContentReviewOwner

View File

@ -19,14 +19,23 @@ class ContentReviewEmails extends BuildTask {
Subsite::$disable_subsite_filter = true; 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()) { if ($pages && $pages->Count()) {
foreach($pages as $page) { foreach($pages as $page) {
$owner = $page->ContentReviewOwner(); $owners = $page->ContentReviewOwners();
if ($owner) { if(!$owners->count()) {
$sender = Security::findAnAdministrator(); continue;
$recipient = $owner; }
$sender = Security::findAnAdministrator();
foreach($owners as $recipient) {
$subject = sprintf(_t('ContentReviewEmails.SUBJECT', 'Page %s due for content review'), $page->Title); $subject = sprintf(_t('ContentReviewEmails.SUBJECT', 'Page %s due for content review'), $page->Title);
$email = new Email(); $email = new Email();

View File

@ -23,18 +23,25 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
* *
* @var array * @var array
*/ */
private static $has_one = array( private static $belongs_many_many = array(
'ContentReviewOwner' => 'Member', 'ContentReviewGroups' => 'Group',
'ContentReviewUsers' => 'Member'
); );
/** /**
* *
* @return string * @return string
*/ */
public function getOwnerName() { public function getOwnerNames() {
if($this->owner->ContentReviewOwnerID && $this->owner->ContentReviewOwner()) { $names = array();
return $this->owner->ContentReviewOwner()->FirstName . ' ' . $this->owner->ContentReviewOwner()->Surname; 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; 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 * @return void
*/ */
public function updateCMSFields(FieldList $fields) { public function updateCMSFields(FieldList $fields) {
if(Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) { if(!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
return; 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 &raquo;
$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( $groupsMap = array();
new HeaderField(_t('SiteTreeCMSWorkflow.REVIEWHEADER', "Content review"), 2), foreach(Group::get() as $group) {
new DropdownField("ContentReviewOwnerID", _t("SiteTreeCMSWorkflow.PAGEOWNER", // Listboxfield values are escaped, use ASCII char instead of &raquo;
"Page owner (will be responsible for reviews)"), $cmsUsers->map('ID', 'Title', '(no owner)')), $groupsMap[$group->ID] = $group->getBreadcrumbs(' > ');
DateField::create( }
"NextReviewDate", asort($groupsMap);
_t("SiteTreeCMSWorkflow.NEXTREVIEWDATE", "Next review date (leave blank for no review)") $groupField = ListboxField::create('DirectGroups', _t("ContentReview.PAGEOWNERGROUPS", "Groups"))
)->setConfig('showcalendar', true)->setConfig('dateformat', 'yyyy-MM-dd')->setConfig('datavalueformat', 'yyyy-MM-dd'), ->setMultiple(true)
new DropdownField("ReviewPeriodDays", _t("SiteTreeCMSWorkflow.REVIEWFREQUENCY", ->setSource($groupsMap)
"Review frequency (the review date will be set to this far in the future whenever the page is published.)"), array( ->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", 0 => "No automatic review date",
1 => "1 day", 1 => "1 day",
7 => "1 week", 7 => "1 week",
@ -79,7 +160,15 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
152 => "5 months", 152 => "5 months",
183 => "6 months", 183 => "6 months",
365 => "12 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') 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->NextReviewDate = date('Y-m-d', strtotime('+' . $this->owner->ReviewPeriodDays . ' days'));
} }
$this->owner->LastEditedByName=$this->owner->getEditorName(); $this->owner->LastEditedByName=$this->owner->getEditorName();
$this->owner->OwnerNames = $this->owner->getOwnerName(); $this->owner->OwnerNames = $this->owner->getOwnerNames();
} }
/** /**

View File

@ -0,0 +1,16 @@
<?php
/**
* Description of GroupContentReview
*
*/
class ContentReviewOwner extends DataExtension {
/**
*
* @var array
*/
private static $many_many = array(
"SiteTreeContentReview" => "SiteTree"
);
}

View File

@ -8,21 +8,21 @@ class ContentReviewTest extends FunctionalTest {
*/ */
public static $fixture_file = 'contentreview/tests/ContentReviewTest.yml'; public static $fixture_file = 'contentreview/tests/ContentReviewTest.yml';
public function testPermissions() { public function testPermissionsExists() {
$editor = $this->objFromFixture('Member', 'editor');
$author = $this->objFromFixture('Member', 'author');
// Assert the permission code exists
$perms = singleton('SiteTreeContentReview')->providePermissions(); $perms = singleton('SiteTreeContentReview')->providePermissions();
$this->assertTrue(isset($perms['EDIT_CONTENT_REVIEW_FIELDS'])); $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); $this->logInAs($editor);
$page = new Page(); $page = new Page();
$fields = $page->getCMSFields(); $fields = $page->getCMSFields();
$this->assertNotNull($fields->fieldByName('Root.Review')); $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); $this->logInAs($author);
$page = new Page(); $page = new Page();
$fields = $page->getCMSFields(); $fields = $page->getCMSFields();
@ -83,23 +83,23 @@ class ContentReviewTest extends FunctionalTest {
SS_Datetime::clear_mock_now(); SS_Datetime::clear_mock_now();
} }
public function testOwnerName() { public function testOwnerNames() {
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'editor');
$this->logInAs($editor); $this->logInAs($editor);
$page = new Page(); $page = new Page();
$page->ReviewPeriodDays = 10; $page->ReviewPeriodDays = 10;
$page->ContentReviewOwnerID = $editor->ID; $page->ContentReviewUsers()->push($editor);
$page->write(); $page->write();
$this->assertTrue($page->doPublish()); $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 = $this->objFromFixture('Page', 'about');
$page->ContentReviewOwnerID = 0; $page->ContentReviewOwnerID = 0;
$page->write(); $page->write();
$this->assertTrue($page->doPublish()); $this->assertTrue($page->doPublish());
$this->assertNull($page->OwnerName); $this->assertEquals('', $page->OwnerNames);
} }
} }

View File

@ -42,7 +42,7 @@ Page:
staff: staff:
Title: Staff Title: Staff
NextReviewDate: 2010-02-14 NextReviewDate: 2010-02-14
ContentReviewOwner: =>Member.author ContentReviewUsers: =>Member.author
contact: contact:
Title: Contact Us Title: Contact Us
NextReviewDate: 2010-02-21 NextReviewDate: 2010-02-21