mirror of
https://github.com/silverstripe/silverstripe-contentreview
synced 2024-10-22 17:05:47 +02:00
Merge pull request #35 from open-sausages/feature/custom-templates
API CMS Editable notification templates
This commit is contained in:
commit
ddf5052503
@ -6,7 +6,6 @@
|
||||
*/
|
||||
class ContentReviewCMSExtension extends LeftAndMainExtension
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
@ -9,23 +9,49 @@
|
||||
class ContentReviewDefaultSettings extends DataExtension
|
||||
{
|
||||
/**
|
||||
* @config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
"ReviewPeriodDays" => "Int",
|
||||
'ReviewPeriodDays' => 'Int',
|
||||
'ReviewFrom' => 'Varchar(255)',
|
||||
'ReviewSubject' => 'Varchar(255)',
|
||||
'ReviewBody' => 'HTMLText',
|
||||
);
|
||||
|
||||
/**
|
||||
* @config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = array(
|
||||
'ReviewSubject' => 'Page(s) are due for content review',
|
||||
'ReviewBody' => '<h2>Page(s) due for review</h2><p>There are $PagesCount pages that are due for review today by you.</p>',
|
||||
);
|
||||
|
||||
/**
|
||||
* @config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $many_many = array(
|
||||
"ContentReviewGroups" => "Group",
|
||||
"ContentReviewUsers" => "Member",
|
||||
'ContentReviewGroups' => 'Group',
|
||||
'ContentReviewUsers' => 'Member',
|
||||
);
|
||||
|
||||
/**
|
||||
* Template to use for content review emails.
|
||||
*
|
||||
* This should contain an $EmailBody variable as a placeholder for the user-defined copy
|
||||
*
|
||||
* @config
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $content_review_template = 'ContentReviewEmail';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOwnerNames()
|
||||
@ -33,14 +59,14 @@ class ContentReviewDefaultSettings extends DataExtension
|
||||
$names = array();
|
||||
|
||||
foreach ($this->OwnerGroups() as $group) {
|
||||
$names[] = $group->getBreadcrumbs(" > ");
|
||||
$names[] = $group->getBreadcrumbs(' > ');
|
||||
}
|
||||
|
||||
foreach ($this->OwnerUsers() as $group) {
|
||||
$names[] = $group->getName();
|
||||
}
|
||||
|
||||
return implode(", ", $names);
|
||||
return implode(', ', $names);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +74,7 @@ class ContentReviewDefaultSettings extends DataExtension
|
||||
*/
|
||||
public function OwnerGroups()
|
||||
{
|
||||
return $this->owner->getManyManyComponents("ContentReviewGroups");
|
||||
return $this->owner->getManyManyComponents('ContentReviewGroups');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,54 +82,78 @@ class ContentReviewDefaultSettings extends DataExtension
|
||||
*/
|
||||
public function OwnerUsers()
|
||||
{
|
||||
return $this->owner->getManyManyComponents("ContentReviewUsers");
|
||||
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."));
|
||||
$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);
|
||||
$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",
|
||||
$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'
|
||||
));
|
||||
|
||||
$usersMap = $users->map("ID", "Title")->toArray();
|
||||
$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)
|
||||
$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"));
|
||||
->setAttribute('data-placeholder', _t('ContentReview.ADDUSERS', 'Add users'))
|
||||
->setDescription(_t('ContentReview.OWNERUSERSDESCRIPTION', 'Page owners that are responsible for reviews'));
|
||||
|
||||
$fields->addFieldToTab("Root.ContentReview", $userField);
|
||||
$fields->addFieldToTab('Root.ContentReview', $userField);
|
||||
|
||||
$groupsMap = array();
|
||||
|
||||
foreach (Group::get() as $group) {
|
||||
// Listboxfield values are escaped, use ASCII char instead of »
|
||||
$groupsMap[$group->ID] = $group->getBreadcrumbs(" > ");
|
||||
$groupsMap[$group->ID] = $group->getBreadcrumbs(' > ');
|
||||
}
|
||||
|
||||
asort($groupsMap);
|
||||
|
||||
$groupField = ListboxField::create("OwnerGroups", _t("ContentReview.PAGEOWNERGROUPS", "Groups"), $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"));
|
||||
->setAttribute('data-placeholder', _t('ContentReview.ADDGROUP', 'Add groups'))
|
||||
->setDescription(_t('ContentReview.OWNERGROUPSDESCRIPTION', 'Page owners that are responsible for reviews'));
|
||||
|
||||
$fields->addFieldToTab("Root.ContentReview", $groupField);
|
||||
$fields->addFieldToTab('Root.ContentReview', $groupField);
|
||||
|
||||
// Email content
|
||||
$fields->addFieldsToTab(
|
||||
'Root.ContentReview',
|
||||
array(
|
||||
TextField::create('ReviewFrom', _t('ContentReview.EMAILFROM', 'From email address'))
|
||||
->setRightTitle(_t('Review.EMAILFROM_RIGHTTITLE', 'e.g: do-not-reply@site.com')),
|
||||
TextField::create('ReviewSubject', _t('ContentReview.EMAILSUBJECT', 'Subject line')),
|
||||
TextAreaField::create('ReviewBody', _t('ContentReview.EMAILTEMPLATE', 'Email template')),
|
||||
LiteralField::create('TemplateHelp', $this->owner->renderWith('ContentReviewAdminHelp')),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,7 +162,64 @@ class ContentReviewDefaultSettings extends DataExtension
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function ContentReviewOwners() {
|
||||
public function ContentReviewOwners()
|
||||
{
|
||||
return SiteTreeContentReview::merge_owners($this->OwnerGroups(), $this->OwnerUsers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the review body, falling back to the default if left blank.
|
||||
*
|
||||
* @return string HTML text
|
||||
*/
|
||||
public function getReviewBody()
|
||||
{
|
||||
return $this->getWithDefault('ReviewBody');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the review subject line, falling back to the default if left blank.
|
||||
*
|
||||
* @return string plain text value
|
||||
*/
|
||||
public function getReviewSubject()
|
||||
{
|
||||
return $this->getWithDefault('ReviewSubject');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "from" field for review emails.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReviewFrom()
|
||||
{
|
||||
$from = $this->owner->getField('ReviewFrom');
|
||||
if ($from) {
|
||||
return $from;
|
||||
}
|
||||
|
||||
// Fall back to admin email
|
||||
return Config::inst()->get('Email', 'admin_email');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a user-configured field, falling back to the default if left blank.
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getWithDefault($field)
|
||||
{
|
||||
$value = $this->owner->getField($field);
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
// fallback to default value
|
||||
$defaults = $this->owner->config()->defaults;
|
||||
if (isset($defaults[$field])) {
|
||||
return $defaults[$field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,8 @@ class PagesDueForReviewReport extends SS_Report
|
||||
* @param array $params
|
||||
*
|
||||
* @return SS_List
|
||||
*/public function sourceRecords($params = array())
|
||||
*/
|
||||
public function sourceRecords($params = array())
|
||||
{
|
||||
Versioned::reading_stage("Stage");
|
||||
|
||||
|
@ -5,13 +5,6 @@
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -19,10 +12,9 @@ class ContentReviewEmails extends BuildTask
|
||||
{
|
||||
$compatibility = ContentReviewCompatability::start();
|
||||
|
||||
$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")->where("\"SiteTree\".\"NextReviewDate\" <= '{$now}'");
|
||||
$pages = Page::get()
|
||||
->filter('NextReviewDate:LessThanOrEqual', SS_Datetime::now()->URLDate());
|
||||
|
||||
$overduePages = $this->getOverduePagesForOwners($pages);
|
||||
|
||||
@ -52,10 +44,6 @@ class ContentReviewEmails extends BuildTask
|
||||
$option = $page->getOptions();
|
||||
|
||||
foreach ($option->ContentReviewOwners() as $owner) {
|
||||
if (!isset(self::$member_cache[$owner->ID])) {
|
||||
self::$member_cache[$owner->ID] = $owner;
|
||||
}
|
||||
|
||||
if (!isset($overduePages[$owner->ID])) {
|
||||
$overduePages[$owner->ID] = new ArrayList();
|
||||
}
|
||||
@ -73,22 +61,69 @@ class ContentReviewEmails extends BuildTask
|
||||
*/
|
||||
protected function notifyOwner($ownerID, SS_List $pages)
|
||||
{
|
||||
$owner = self::$member_cache[$ownerID];
|
||||
$sender = Security::findAnAdministrator();
|
||||
$senderEmail = ($sender->Email) ? $sender->Email : Config::inst()->get("Email", "admin_email");
|
||||
// Prepare variables
|
||||
$siteConfig = SiteConfig::current_site_config();
|
||||
$owner = Member::get()->byID($ownerID);
|
||||
$templateVariables = $this->getTemplateVariables($owner, $siteConfig, $pages);
|
||||
|
||||
$subject = _t("ContentReviewEmails.SUBJECT", "Page(s) are due for content review");
|
||||
// Build email
|
||||
$email = new Email();
|
||||
$email->setTo($owner->Email);
|
||||
$email->setFrom($senderEmail);
|
||||
$email->setTemplate("ContentReviewEmail");
|
||||
$email->setSubject($subject);
|
||||
$email->populateTemplate(array(
|
||||
"Recipient" => $owner,
|
||||
"Sender" => $sender,
|
||||
"Pages" => $pages,
|
||||
));
|
||||
$email->setFrom($siteConfig->ReviewFrom);
|
||||
$email->setSubject($siteConfig->ReviewSubject);
|
||||
|
||||
// Get user-editable body
|
||||
$body = $this->getEmailBody($siteConfig, $templateVariables);
|
||||
|
||||
// Populate mail body with fixed template
|
||||
$email->setTemplate($siteConfig->config()->content_review_template);
|
||||
$email->populateTemplate($templateVariables);
|
||||
$email->populateTemplate(array(
|
||||
'EmailBody' => $body,
|
||||
'Recipient' => $owner,
|
||||
'Pages' => $pages,
|
||||
));
|
||||
$email->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string value of HTML body with all variable evaluated.
|
||||
*
|
||||
* @param SiteConfig $config
|
||||
* @param array List of safe template variables to expose to this template
|
||||
*
|
||||
* @return HTMLText
|
||||
*/
|
||||
protected function getEmailBody($config, $variables)
|
||||
{
|
||||
$template = SSViewer::fromString($config->ReviewBody);
|
||||
$value = $template->process(new ArrayData($variables));
|
||||
|
||||
// Cast to HTML
|
||||
return DBField::create_field('HTMLText', (string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of safe template variables and their values which can be used
|
||||
* in both the static and editable templates.
|
||||
*
|
||||
* {@see ContentReviewAdminHelp.ss}
|
||||
*
|
||||
* @param Member $recipient
|
||||
* @param SiteConfig $config
|
||||
* @param SS_List $pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTemplateVariables($recipient, $config, $pages)
|
||||
{
|
||||
return array(
|
||||
'Subject' => $config->ReviewSubject,
|
||||
'PagesCount' => $pages->count(),
|
||||
'FromEmail' => $config->ReviewFrom,
|
||||
'ToFirstName' => $recipient->FirstName,
|
||||
'ToSurname' => $recipient->Surname,
|
||||
'ToEmail' => $recipient->Email,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
BIN
docs/.DS_Store
vendored
BIN
docs/.DS_Store
vendored
Binary file not shown.
BIN
docs/en/.DS_Store
vendored
BIN
docs/en/.DS_Store
vendored
Binary file not shown.
BIN
docs/en/images/content-review-siteconfig-settings.png
Normal file
BIN
docs/en/images/content-review-siteconfig-settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
@ -49,6 +49,11 @@ In order for the contentreview module to send emails, you need to *either*:
|
||||
* Setup the DailyTask script to run daily via cron. See framework/tasks/ScheduledTask.php for more information on setup.
|
||||
* Install the queuedjobs module, and follow the configuration steps to create a cron job for that module. Once installed, you can just run dev/build to have a job created, which will run at 9am every day by default.
|
||||
|
||||
Global settings can be configured via the global settings admin in the CMS under the "Content Review" tab.
|
||||
This includes global groups, users, as well as a template editor that supports a limited number of variables.
|
||||
|
||||
![settings](docs/en/images/content-review-siteconfig-settings.png)
|
||||
|
||||
## Usage
|
||||
|
||||
To set up content review schedules you need to log in as a user with the 'Set content owners and review dates' permission. This can either
|
||||
|
10
templates/ContentReviewAdminHelp.ss
Normal file
10
templates/ContentReviewAdminHelp.ss
Normal file
@ -0,0 +1,10 @@
|
||||
<p>This is a list of dynamic variables that you can use in the email templates.</p>
|
||||
|
||||
<ul>
|
||||
<li>$Subject - Email subject line</li>
|
||||
<li>$PagesCount - Number of pages pending review</li>
|
||||
<li>$FromEmail - Sender email address</li>
|
||||
<li>$ToFirstName - The email receivers first name</li>
|
||||
<li>$ToSurname - The email receivers surname</li>
|
||||
<li>$ToEmail - The email receivers email</li>
|
||||
</ul>
|
@ -6,8 +6,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td scope="row" colspan="2" class="typography">
|
||||
<h2><% _t('ContentReviewEmails.EMAIL_HEADING','Page(s) due for review') %></h2>
|
||||
<p>There are $Pages.Count pages that are due for review today by you.</p>
|
||||
$EmailBody
|
||||
</td>
|
||||
</tr>
|
||||
<% loop Pages %>
|
||||
|
@ -6,7 +6,6 @@
|
||||
*/
|
||||
abstract class ContentReviewBaseTest extends FunctionalTest
|
||||
{
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
@ -8,39 +8,57 @@ class ContentReviewNotificationTest extends SapphireTest
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = "contentreview/tests/ContentReviewTest.yml";
|
||||
public static $fixture_file = 'contentreview/tests/ContentReviewTest.yml';
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Hack to ensure only desired siteconfig is scaffolded
|
||||
$desiredID = $this->idFromFixture('SiteConfig', 'mysiteconfig');
|
||||
foreach (SiteConfig::get()->exclude('ID', $desiredID) as $config) {
|
||||
$config->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $requiredExtensions = array(
|
||||
"SiteTree" => array("SiteTreeContentReview"),
|
||||
"Group" => array("ContentReviewOwner"),
|
||||
"Member" => array("ContentReviewOwner"),
|
||||
"CMSPageEditController" => array("ContentReviewCMSExtension"),
|
||||
"SiteConfig" => array("ContentReviewDefaultSettings"),
|
||||
'SiteTree' => array('SiteTreeContentReview'),
|
||||
'Group' => array('ContentReviewOwner'),
|
||||
'Member' => array('ContentReviewOwner'),
|
||||
'CMSPageEditController' => array('ContentReviewCMSExtension'),
|
||||
'SiteConfig' => array('ContentReviewDefaultSettings'),
|
||||
);
|
||||
|
||||
public function testContentReviewEmails()
|
||||
{
|
||||
SS_Datetime::set_mock_now("2010-02-24 12:00:00");
|
||||
SS_Datetime::set_mock_now('2010-02-24 12:00:00');
|
||||
|
||||
/** @var Page|SiteTreeContentReview $childParentPage */
|
||||
$childParentPage = $this->objFromFixture("Page", "contact");
|
||||
$childParentPage->NextReviewDate = "2010-02-23";
|
||||
$childParentPage = $this->objFromFixture('Page', 'contact');
|
||||
$childParentPage->NextReviewDate = '2010-02-23';
|
||||
$childParentPage->write();
|
||||
|
||||
$task = new ContentReviewEmails();
|
||||
$task->run(new SS_HTTPRequest("GET", "/dev/tasks/ContentReviewEmails"));
|
||||
$task->run(new SS_HTTPRequest('GET', '/dev/tasks/ContentReviewEmails'));
|
||||
|
||||
$expectedSubject = _t("ContentReviewEmails.SUBJECT", "Page(s) are due for content review");
|
||||
$email = $this->findEmail("author@example.com", null, $expectedSubject);
|
||||
// Set template variables (as per variable case)
|
||||
$ToEmail = 'author@example.com';
|
||||
$Subject = 'Please log in to review some content!';
|
||||
$PagesCount = 3;
|
||||
$ToFirstName = 'Test';
|
||||
|
||||
$email = $this->findEmail($ToEmail, null, $Subject);
|
||||
$this->assertNotNull($email, "Email haven't been sent.");
|
||||
$this->assertContains("There are 3 pages that are due for review today by you.", $email["htmlContent"]);
|
||||
$this->assertContains("Staff", $email["htmlContent"]);
|
||||
$this->assertContains("Contact Us", $email["htmlContent"]);
|
||||
$this->assertContains("Contact Us Child", $email["htmlContent"]);
|
||||
$this->assertContains(
|
||||
"<h1>$Subject</h1><p>There are $PagesCount pages that are due for review today by you, $ToFirstName.</p><p>This email was sent to $ToEmail</p>",
|
||||
$email['htmlContent']
|
||||
);
|
||||
$this->assertContains('Staff', $email['htmlContent']);
|
||||
$this->assertContains('Contact Us', $email['htmlContent']);
|
||||
$this->assertContains('Contact Us Child', $email['htmlContent']);
|
||||
|
||||
SS_Datetime::clear_mock_now();
|
||||
}
|
||||
|
@ -1,3 +1,9 @@
|
||||
SiteConfig:
|
||||
mysiteconfig:
|
||||
ReviewFrom: sender@silverstripe.com
|
||||
ReviewSubject: 'Please log in to review some content!'
|
||||
ReviewBody: '<h1>$Subject</h1><p>There are $PagesCount pages that are due for review today by you, $ToFirstName.</p><p>This email was sent to $ToEmail</p>'
|
||||
|
||||
Permission:
|
||||
cmsmain1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
|
Loading…
Reference in New Issue
Block a user