Adding default setting to the SiteConfig, broken email and tests though.

This commit is contained in:
Stig Lindqvist 2014-02-20 18:05:14 +13:00
parent f673815eff
commit 78d84a57c6
5 changed files with 344 additions and 52 deletions

View File

@ -10,3 +10,6 @@ Member:
CMSPageEditController:
extensions:
- ContentReviewCMSExtension
SiteConfig:
extensions:
- ContentReviewDefaultSettings

View File

@ -4,10 +4,19 @@
* Daily task to send emails to the owners of content items
* when the review date rolls around
*
*
* @todo create a page cache for the inherited so that we dont unneccesary need to look up parent pages
* @package contentreview
*/
class ContentReviewEmails extends BuildTask {
/**
* Holds a cached array for looking up members via their ID
*
* @var array
*/
protected static $member_cache = array();
/**
*
* @param SS_HTTPRequest $request
@ -21,52 +30,160 @@ 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
$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)')
->where('"SiteTree"."ContentReviewType" != \'Custom\' AND "SiteTree"."NextReviewDate" <= \''.$now.'\' AND' .
' ("OwnerGroups"."ID" IS NOT NULL OR "OwnerUsers"."ID" IS NOT NULL)')
;
if ($pages && $pages->Count()) {
foreach($pages as $page) {
$owners = $page->ContentReviewOwners();
if(!$owners->count()) {
continue;
}
$sender = Security::findAnAdministrator();
$this->notify($pages);
foreach($owners as $recipient) {
$subject = sprintf(_t('ContentReviewEmails.SUBJECT', 'Page %s due for content review'), $page->Title);
// Then grab all the pages with that inherits their settings
$email = new Email();
$email->setTo($recipient->Email);
$email->setFrom(($sender->Email) ? $sender->Email : Email::getAdminEmail());
$email->setTemplate('ContentReviewEmails');
$email->setSubject($subject);
$email->populateTemplate(array(
"PageCMSLink" => "admin/pages/edit/show/".$page->ID,
"Recipient" => $recipient,
"Sender" => $sender,
"Page" => $page,
"StageSiteLink" => Controller::join_links($page->Link(), "?stage=Stage"),
"LiveSiteLink" => Controller::join_links($page->Link(), "?stage=Live"),
));
$email->send();
$pages = Page::get('Page')
->leftJoin('Group_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerGroups"."SiteTreeID"', 'OwnerGroups')
->leftJoin('Member_SiteTreeContentReview', '"SiteTree"."ID" = "OwnerUsers"."SiteTreeID"', "OwnerUsers")
->where('"SiteTree"."ContentReviewType" = \'Inherit\'')
;
$message = '<strong>'._t('ContentReviewEmails.EMAIL_HEADING','Page due for review').'</strong><br/>'.
'The page "'.$page->Title.'" is due for review today by you.<br/>
<a href="admin/pages/edit/show/'.$page->ID.'">'. _t('ContentReviewEmails.REVIEWPAGELINK','Review the page in the CMS') .'</a> &mdash;
<a href="#">'. _t('ContentReviewEmails.VIEWPUBLISHEDLINK','View this page on the website') .'</a>';
if(class_exists('Notification')) {
Notification::notify($recipient, $message);
}
}
}
$overduePages = $this->findInheritedSettings($pages);
// 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) {
$this->notify_user($memberID, $pages);
}
// Revert subsite filter (if installed)
if (ClassInfo::exists('Subsite')) {
if(ClassInfo::exists('Subsite')) {
Subsite::$disable_subsite_filter = $oldSubsiteState;
}
}
/**
*
* @param SS_list $pages
* @return type
*/
protected function findInheritedSettings(SS_list $pages) {
$overduePages = array();
foreach($pages as $page) {
$settings = $this->findContentSettingFor($page);
// This page has a parent with the 'Disabled' option
if(!$settings) {
continue;
}
$owners = $settings->ContentReviewOwners();
if(!$owners->count()) {
continue;
}
if(!$settings->ReviewPeriodDays) {
continue;
}
// Calculate next time this page should be reviewed from the LastEdited datea
$nextReviewUnixSec = strtotime($page->LastEdited . ' + '.$settings->ReviewPeriodDays . ' days');
if($nextReviewUnixSec > time()) {
continue;
}
foreach($owners as $owner) {
if(!isset(self::$member_cache[$owner->ID])) {
self::$member_cache[$owner->ID] = $owner;
}
if(!isset($overduePages[$owner->ID])) {
$overduePages[$owner->ID] = array();
}
$overduePages[$owner->ID][] = $page;
}
}
return $overduePages;
}
/**
*
* @param SiteTree $page
* @return DataObject or false if no settings found
*/
protected function findContentSettingFor($page) {
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 shouldnt really happen, as usual.');
}
/**
*
* @param int $owner
* @param array $pages
*/
protected function notify_user($ownerID, array $pages) {
$owner = self::$member_cache[$ownerID];
echo $owner->Email.PHP_EOL;
foreach($pages as $page) {
echo $page->Title.PHP_EOL;
}
}
/**
*
* @param SS_List $pages
* @return void
*/
protected function notify(SS_List $pages) {
if(!$pages) {
return;
}
if(!$pages->Count()) {
return;
}
foreach($pages as $page) {
// Resolve the content owner groups and members to a single list of members
$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();
$email->setTo($recipient->Email);
$email->setFrom(($sender->Email) ? $sender->Email : Email::getAdminEmail());
$email->setTemplate('ContentReviewEmails');
$email->setSubject($subject);
$email->populateTemplate(array(
"PageCMSLink" => "admin/pages/edit/show/".$page->ID,
"Recipient" => $recipient,
"Sender" => $sender,
"Page" => $page,
"StageSiteLink" => Controller::join_links($page->Link(), "?stage=Stage"),
"LiveSiteLink" => Controller::join_links($page->Link(), "?stage=Live"),
));
//$email->send();
$message = '<strong>'._t('ContentReviewEmails.EMAIL_HEADING','Page due for review').'</strong><br/>'.
'The page "'.$page->Title.'" is due for review today by you.<br/>
<a href="admin/pages/edit/show/'.$page->ID.'">'. _t('ContentReviewEmails.REVIEWPAGELINK','Review the page in the CMS') .'</a> &mdash;
<a href="#">'. _t('ContentReviewEmails.VIEWPUBLISHEDLINK','View this page on the website') .'</a>';
if(class_exists('Notification')) {
// Notification::notify($recipient, $message);
}
// echo $page->Title.' - '.$recipient->Email.PHP_EOL;
}
}
}
}

View File

@ -0,0 +1,110 @@
<?php
/**
* This extensions add a default schema for new pages and pages without a content review setting
*
*/
class ContentReviewDefaultSettings extends DataExtension {
/**
*
* @var array
*/
private static $db = array(
"ReviewPeriodDays" => "Int",
);
/**
*
* @var array
*/
private static $many_many = array(
'ContentReviewGroups' => 'Group',
'ContentReviewUsers' => 'Member'
);
/**
* @return ManyManyList
*/
public function OwnerGroups() {
return $this->owner->getManyManyComponents('ContentReviewGroups');
}
/**
* @return ManyManyList
*/
public function OwnerUsers() {
return $this->owner->getManyManyComponents('ContentReviewUsers');
}
/**
*
* @param \FieldList $fields
*/
public function updateCMSFields(\FieldList $fields) {
$helpText = LiteralField::create('ContentReviewHelp', _t('ContentReview.DEFAULTSETTINGSHELP', 'These content review '
. 'settings will apply to all pages that does not have specific Content Review schedule.'));
$fields->addFieldToTab('Root.ContentReview', $helpText);
$reviewFrequency = DropdownField::create("ReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), SiteTreeContentReview::get_schedule())
->setDescription(_t('ContentReview.REVIEWFREQUENCYDESCRIPTION', 'The review date will be set to this far in the future whenever the page is published'));
$fields->addFieldToTab('Root.ContentReview', $reviewFrequency);
$users = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN"));
$usersMap = $users->map('ID', 'Title')->toArray();
asort($usersMap);
$userField = ListboxField::create('OwnerUsers', _t("ContentReview.PAGEOWNERUSERS", "Users"), $usersMap)
->setMultiple(true)
->setAttribute('data-placeholder', _t('ContentReview.ADDUSERS', 'Add users'))
->setDescription(_t('ContentReview.OWNERUSERSDESCRIPTION', 'Page owners that are responsible for reviews'));
$fields->addFieldToTab('Root.ContentReview', $userField);
$groupsMap = array();
foreach(Group::get() as $group) {
// Listboxfield values are escaped, use ASCII char instead of &raquo;
$groupsMap[$group->ID] = $group->getBreadcrumbs(' > ');
}
asort($groupsMap);
$groupField = ListboxField::create('OwnerGroups', _t("ContentReview.PAGEOWNERGROUPS", "Groups"), $groupsMap)
->setMultiple(true)
->setAttribute('data-placeholder', _t('ContentReview.ADDGROUP', 'Add groups'))
->setDescription(_t('ContentReview.OWNERGROUPSDESCRIPTION', 'Page owners that are responsible for reviews'));
$fields->addFieldToTab('Root.ContentReview', $groupField);
}
/**
* Get all Members that are default Content Owners
*
* This includes checking group hierarchy and adding any direct users
*
* @return \ArrayList
*/
public function ContentReviewOwners() {
$contentReviewOwners = new ArrayList();
$toplevelGroups = $this->OwnerGroups();
if($toplevelGroups->count()) {
$groupIDs = array();
foreach($toplevelGroups as $group) {
$familyIDs = $group->collateFamilyIDs();
if(is_array($familyIDs)) {
$groupIDs = array_merge($groupIDs, array_values($familyIDs));
}
}
array_unique($groupIDs);
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->OwnerUsers());
$contentReviewOwners->removeDuplicates();
return $contentReviewOwners;
}
}

View File

@ -12,6 +12,7 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
* @var array
*/
private static $db = array(
"ContentReviewType" => "Enum('Inherit, Disabled, Custom', 'Inherit')",
"ReviewPeriodDays" => "Int",
"NextReviewDate" => "Date",
'LastEditedByName' => 'Varchar(255)',
@ -39,7 +40,7 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
*
* @var array
*/
private $daysToString = array(
private static $schedule = array(
0 => "No automatic review date",
1 => "1 day",
7 => "1 week",
@ -110,6 +111,13 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
return $contentReviewOwners;
}
/**
* @return array
*/
public static function get_schedule() {
return self::$schedule;
}
/**
* @return ManyManyList
*/
@ -130,17 +138,19 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
* @return void
*/
public function updateSettingsFields(FieldList $fields) {
Requirements::javascript('contentreview/javascript/contentreview.js');
$crFields = new FieldList();
// Display read-only version only
if(!Permission::check("EDIT_CONTENT_REVIEW_FIELDS")) {
$schedule = self::get_schedule();
$contentOwners = ReadonlyField::create('ROContentOwners', _t('ContentReview.CONTENTOWNERS', 'Content Owners'), $this->getOwnerNames());
$nextReviewAt = DateField::create('RONextReviewDate', _t("ContentReview.NEXTREVIEWDATE", "Next review date"), $this->owner->NextReviewDate);
if(!isset($this->daysToString[$this->owner->ReviewPeriodDays])) {
$reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $this->daysToString[0]);
if(!isset($schedule[$this->owner->ReviewPeriodDays])) {
$reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[0]);
} else {
$reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $this->daysToString[$this->owner->ReviewPeriodDays]);
$reviewFreq = ReadonlyField::create("ROReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $schedule[$this->owner->ReviewPeriodDays]);
}
$logConfig = GridFieldConfig::create()
@ -162,6 +172,17 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
return;
}
$options = array();
$options["Disabled"] = _t('ContentReview.DISABLE', "Disable content review");
$options["Inherit"] = _t('ContentReview.INHERIT', "Inherit from parent page");
$options["Custom"] = _t('ContentReview.CUSTOM', "Custom settings");
$viewersOptionsField = OptionsetField::create("ContentReviewType", _t('ContentReview.OPTIONS', "Options"), $options);
//$viewersOptionsField->setValue($this->owner->ContentReviewType);
#var_dump($this->owner->ContentReviewType);
#die();
$users = Permission::get_members_by_permission(array("CMS_ACCESS_CMSMain", "ADMIN"));
$usersMap = $users->map('ID', 'Title')->toArray();
@ -190,19 +211,22 @@ class SiteTreeContentReview extends DataExtension implements PermissionProvider
->setConfig('datavalueformat', 'yyyy-MM-dd')
->setDescription(_t('ContentReview.NEXTREVIEWDATADESCRIPTION', 'Leave blank for no review'));
$reviewFrequency = DropdownField::create("ReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), $this->daysToString)
$reviewFrequency = DropdownField::create("ReviewPeriodDays", _t("ContentReview.REVIEWFREQUENCY", "Review frequency"), self::get_schedule())
->setDescription(_t('ContentReview.REVIEWFREQUENCYDESCRIPTION', 'The review date will be set to this far in the future whenever the page is published'));
$notesField = GridField::create('ReviewNotes', 'Review Notes', $this->owner->ReviewLogs(), GridFieldConfig_RecordEditor::create());
$crFields->add(new HeaderField(_t('ContentReview.REVIEWHEADER', "Content review"), 2));
$crFields->add($userField);
$crFields->add($groupField);
$crFields->add($reviewDate);
$crFields->add($reviewFrequency);
$crFields->add($notesField);
$fields->addFieldsToTab("Root.ContentReview", $crFields);
$fields->addFieldsToTab("Root.ContentReview", array(
new HeaderField(_t('ContentReview.REVIEWHEADER', "Content review"), 2),
$viewersOptionsField,
CompositeField::create(
$userField,
$groupField,
$reviewDate,
$reviewFrequency
)->addExtraClass('contentReviewSettings'),
$notesField
));
}
/**

View File

@ -4,5 +4,43 @@ jQuery(function($) {
"use strict";
$.entwine('ss', function($) {
/**
* Class: .cms-edit-form #ContentReviewType
*
* Toggle display of group dropdown in "access" tab,
* based on selection of radiobuttons.
*/
$('.cms-edit-form #ContentReviewType').entwine({
// Constructor: onmatch
onmatch: function() {
// TODO Decouple
var dropdown;
if(this.attr('id') == 'ContentReviewType') dropdown = $('.contentReviewSettings');
this.find('.optionset :input').bind('change', function(e) {
var wrapper = $(this).closest('.middleColumn').parent('div');
if(e.target.value == 'Custom') {
wrapper.addClass('remove-splitter');
dropdown['show']();
}
else {
wrapper.removeClass('remove-splitter');
dropdown['hide']();
}
});
// initial state
var currentVal = this.find('input[name=' + this.attr('id') + ']:checked').val();
dropdown[currentVal == 'Custom' ? 'show' : 'hide']();
this._super();
},
onunmatch: function() {
this._super();
}
});
});
});