From 61fd586533d2036c755719fcab102b7ec5cf74b0 Mon Sep 17 00:00:00 2001 From: Christopher Pitt Date: Mon, 2 Nov 2015 12:27:42 +1300 Subject: [PATCH] Fixed code style --- .../ContentReviewCompatability.php | 88 +- code/extensions/ContentReviewCMSExtension.php | 166 +-- .../ContentReviewDefaultSettings.php | 218 ++-- code/extensions/ContentReviewOwner.php | 21 +- code/extensions/SiteTreeContentReview.php | 1091 +++++++++-------- code/jobs/ContentReviewNotificationJob.php | 148 ++- code/models/ContentReviewLog.php | 82 +- code/reports/PagesDueForReviewReport.php | 297 ++--- .../PagesWithoutReviewScheduleReport.php | 262 ++-- code/tasks/ContentReviewEmails.php | 172 +-- .../tasks/ContentReviewOwnerMigrationTask.php | 46 +- tests/ContentReviewBaseTest.php | 72 +- ...ContentReviewCMSPageEditControllerTest.php | 162 ++- tests/ContentReviewNotificationTest.php | 79 +- tests/ContentReviewReportTest.php | 140 ++- tests/ContentReviewSettingsTest.php | 478 +++++--- tests/SiteTreeContentReviewTest.php | 451 ++++--- 17 files changed, 2177 insertions(+), 1796 deletions(-) diff --git a/code/compatability/ContentReviewCompatability.php b/code/compatability/ContentReviewCompatability.php index 3de15af..8c9cc92 100644 --- a/code/compatability/ContentReviewCompatability.php +++ b/code/compatability/ContentReviewCompatability.php @@ -1,46 +1,54 @@ - null, - self::TRANSLATABLE => null - ); - if(ClassInfo::exists('Subsite')){ - $compat[self::SUBSITES] = Subsite::$disable_subsite_filter; - Subsite::disable_subsite_filter(true); - } - if(ClassInfo::exists('Translatable')){ - $compat[self::TRANSLATABLE] = Translatable::locale_filter_enabled(); - Translatable::disable_locale_filter(); - } - return $compat; - } - - /** - * @param array $compat - see start() - */ - public static function done(array $compat){ - if(class_exists('Subsite')){ - Subsite::$disable_subsite_filter = $compat[self::SUBSITES]; - } - if(class_exists('Translatable')){ - Translatable::enable_locale_filter($compat[self::TRANSLATABLE]); - } - } - -} \ No newline at end of file + const TRANSLATABLE = 1; + + /** + * Returns the state of other modules before compatibility mode is started. + * + * @return array + */ + public static function start() + { + $compatibility = array( + self::SUBSITES => null, + self::TRANSLATABLE => null, + ); + + if (ClassInfo::exists("Subsite")) { + $compatibility[self::SUBSITES] = Subsite::$disable_subsite_filter; + Subsite::disable_subsite_filter(true); + } + + if (ClassInfo::exists("Translatable")) { + $compatibility[self::TRANSLATABLE] = Translatable::locale_filter_enabled(); + Translatable::disable_locale_filter(); + } + + return $compatibility; + } + + /** + * @param array $compatibility + */ + public static function done(array $compatibility) + { + if (class_exists("Subsite")) { + Subsite::$disable_subsite_filter = $compatibility[self::SUBSITES]; + } + + if (class_exists("Translatable")) { + Translatable::enable_locale_filter($compatibility[self::TRANSLATABLE]); + } + } +} diff --git a/code/extensions/ContentReviewCMSExtension.php b/code/extensions/ContentReviewCMSExtension.php index 5d63a46..e1f1751 100644 --- a/code/extensions/ContentReviewCMSExtension.php +++ b/code/extensions/ContentReviewCMSExtension.php @@ -1,84 +1,94 @@ byID($SQL_id); - - if(!$record || !$record->ID) { - throw new SS_HTTPResponse_Exception("Bad record ID #$SQL_id", 404); - } - - if(!$record->canEdit()) { - return Security::permissionFailure($this->owner); - } - - $fields = new FieldList(); - $fields->push(HiddenField::create('ID', 'ID', $SQL_id)); - $fields->push(TextareaField::create('ReviewNotes', 'Review notes')); - - $actions = new FieldList( - FormAction::create('save_review', 'Save') - ); - - $form = CMSForm::create($this->owner, "EditForm", $fields, $actions)->setHTMLID('Form_EditForm'); - $form->setResponseNegotiator($this->owner->getResponseNegotiator()); - $form->loadDataFrom($record); - $form->disableDefaultAction(); - - // TODO Can't merge $FormAttributes in template at the moment - $form->setTemplate($this->owner->getTemplatesWithSuffix('LeftAndMain_EditForm')); - return $form->forTemplate(); - } - - /** - * Save the review notes and redirect back to the page edit form - * - * @param array $data - * @param Form $form - * @return string - html - */ - public function save_review($data, Form $form) { - if(!isset($data['ID'])) { - throw new SS_HTTPResponse_Exception("No record ID", 404); - } - $SQL_id = (int) $data['ID']; - $page = SiteTree::get()->byID($SQL_id); - if($page && !$page->canEdit()) { - return Security::permissionFailure(); - } - if(!$page || !$page->ID) { - throw new SS_HTTPResponse_Exception("Bad record ID #$SQL_id", 404); - } - - $page->addReviewNote(Member::currentUser(), $data['ReviewNotes']); - $page->advanceReviewDate(); - - return $this->owner->redirect($this->owner->Link('show/'.$page->ID)); - } +class ContentReviewCMSExtension extends LeftAndMainExtension +{ + /** + * @var array + */ + private static $allowed_actions = array( + "reviewed", + "save_review", + ); + + /** + * Shows a form with review notes. + * + * @param array $data + * @param Form $form + * + * @return SS_HTTPResponse + * @throws SS_HTTPResponse_Exception + */ + public function reviewed($data, Form $form) + { + if (!isset($data["ID"])) { + throw new SS_HTTPResponse_Exception('No record ID', 404); + } + + $id = (int) $data["ID"]; + $record = SiteTree::get()->byID($id); + + if (!$record || !$record->ID) { + throw new SS_HTTPResponse_Exception("Bad record ID #{$id}", 404); + } + + if (!$record->canEdit()) { + return Security::permissionFailure($this->owner); + } + + $fields = new FieldList(); + $fields->push(HiddenField::create("ID", "ID", $id)); + $fields->push(TextareaField::create("ReviewNotes", "Review notes")); + + $actions = new FieldList( + FormAction::create("save_review", "Save") + ); + + $form = CMSForm::create($this->owner, "EditForm", $fields, $actions)->setHTMLID("Form_EditForm"); + $form->setResponseNegotiator($this->owner->getResponseNegotiator()); + $form->loadDataFrom($record); + $form->disableDefaultAction(); + + // TODO Can't merge $FormAttributes in template at the moment + $form->setTemplate($this->owner->getTemplatesWithSuffix("LeftAndMain_EditForm")); + + return $form->forTemplate(); + } + + /** + * Save the review notes and redirect back to the page edit form. + * + * @param array $data + * @param Form $form + * + * @return string + * + * @throws SS_HTTPResponse_Exception + */ + public function save_review($data, Form $form) + { + if (!isset($data["ID"])) { + throw new SS_HTTPResponse_Exception("No record ID", 404); + } + + $id = (int) $data["ID"]; + $page = SiteTree::get()->byID($id); + + if ($page && !$page->canEdit()) { + return Security::permissionFailure(); + } + + if (!$page || !$page->ID) { + throw new SS_HTTPResponse_Exception("Bad record ID #{$id}", 404); + } + + $page->addReviewNote(Member::currentUser(), $data["ReviewNotes"]); + $page->advanceReviewDate(); + + return $this->owner->redirect($this->owner->Link("show/" . $page->ID)); + } } diff --git a/code/extensions/ContentReviewDefaultSettings.php b/code/extensions/ContentReviewDefaultSettings.php index f5712d8..49a1351 100644 --- a/code/extensions/ContentReviewDefaultSettings.php +++ b/code/extensions/ContentReviewDefaultSettings.php @@ -1,107 +1,121 @@ "Int", - ); - - /** - * - * @var array - */ - private static $many_many = array( - 'ContentReviewGroups' => 'Group', - 'ContentReviewUsers' => 'Member' - ); - - /** - * - * @return string - */ - public function getOwnerNames() { - $names = array(); - foreach($this->OwnerGroups() as $group) { - $names[] = $group->getBreadcrumbs(' > '); - } - - foreach($this->OwnerUsers() as $group) { - $names[] = $group->getName(); - } - return implode(', ', $names); - } - - /** - * @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 » - $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() { - return new ArrayList(); - return SiteTreeContentReview::merge_owners($this->OwnerGroups(), $this->OwnerUsers()); - } +class ContentReviewDefaultSettings extends DataExtension +{ + /** + * @var array + */ + private static $db = array( + "ReviewPeriodDays" => "Int", + ); + + /** + * + * @var array + */ + private static $many_many = array( + "ContentReviewGroups" => "Group", + "ContentReviewUsers" => "Member", + ); + + /** + * + * @return string + */ + public function getOwnerNames() + { + $names = array(); + + foreach ($this->OwnerGroups() as $group) { + $names[] = $group->getBreadcrumbs(" > "); + } + + foreach ($this->OwnerUsers() as $group) { + $names[] = $group->getName(); + } + + return implode(", ", $names); + } + + /** + * @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 » + $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() + { + return new ArrayList(); + + return SiteTreeContentReview::merge_owners($this->OwnerGroups(), $this->OwnerUsers()); + } } diff --git a/code/extensions/ContentReviewOwner.php b/code/extensions/ContentReviewOwner.php index dbf6167..bbf31ba 100644 --- a/code/extensions/ContentReviewOwner.php +++ b/code/extensions/ContentReviewOwner.php @@ -1,17 +1,14 @@ "SiteTree" - ); +class ContentReviewOwner extends DataExtension +{ + /** + * @var array + */ + private static $many_many = array( + "SiteTreeContentReview" => "SiteTree", + ); } diff --git a/code/extensions/SiteTreeContentReview.php b/code/extensions/SiteTreeContentReview.php index 4cd00e4..7d053cb 100644 --- a/code/extensions/SiteTreeContentReview.php +++ b/code/extensions/SiteTreeContentReview.php @@ -1,537 +1,586 @@ "Enum('Inherit, Disabled, Custom', 'Inherit')", + "ReviewPeriodDays" => "Int", + "NextReviewDate" => "Date", + "LastEditedByName" => "Varchar(255)", + "OwnerNames" => "Varchar(255)", + ); - /** - * - * @var array - */ - private static $db = array( - "ContentReviewType" => "Enum('Inherit, Disabled, Custom', 'Inherit')", - "ReviewPeriodDays" => "Int", - "NextReviewDate" => "Date", - 'LastEditedByName' => 'Varchar(255)', - 'OwnerNames' => 'Varchar(255)' - ); + /** + * @var array + */ + private static $defaults = array( + "ContentReviewType" => "Inherit", + ); - - /** - * - * @var array - */ - private static $defaults = array( - 'ContentReviewType' => 'Inherit' - ); - - /** - * - * @var array - */ - private static $has_many = array( - 'ReviewLogs' => 'ContentReviewLog' - ); + /** + * @var array + */ + private static $has_many = array( + "ReviewLogs" => "ContentReviewLog", + ); - /** - * - * @var array - */ - private static $belongs_many_many = array( - 'ContentReviewGroups' => 'Group', - 'ContentReviewUsers' => 'Member' - ); - - /** - * - * @var array - */ - private static $schedule = array( - 0 => "No automatic review date", - 1 => "1 day", - 7 => "1 week", - 30 => "1 month", - 60 => "2 months", - 91 => "3 months", - 121 => "4 months", - 152 => "5 months", - 183 => "6 months", - 365 => "12 months", - ); - - /** - * @return array - */ - public static function get_schedule() { - return self::$schedule; - } - - /** - * Takes a list of groups and members and return a list of unique member - * - * @param SS_List $groups - * @param SS_List $members - * @return ArrayList - */ - public static function merge_owners(SS_List $groups, SS_List $members) { - $contentReviewOwners = new ArrayList(); - if($groups->count()) { - $groupIDs = array(); - foreach($groups 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($members); - $contentReviewOwners->removeDuplicates(); - return $contentReviewOwners; - } - - /** - * - * @param \FieldList $actions - */ - public function updateCMSActions(\FieldList $actions) { - 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')); - $actions->push($reviewAction); - } - } - - /** - * - * @param SiteTree $page - * @return Date | false - returns false if the content review have disabled - */ - public function getReviewDate(SiteTree $page=null) { - if($page === null) { - $page = $this->owner; - } - if($page->obj('NextReviewDate')->exists()) { - return $page->obj('NextReviewDate'); - } - - $options = $this->owner->getOptions(); - - if(!$options) { - return false; - } - if(!$options->ReviewPeriodDays) { - return false; - } - // Failover to check on ReviewPeriodDays + LastEdited - $nextReviewUnixSec = strtotime(' + '.$options->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. Either - * - a SiteTreeContentReview decorated object - * - the default SiteTree config - * - false if this page have it's content review disabled - * - * Will go through parents and root pages will use the siteconfig - * if their setting is Inherit. - * - * @return DataObject or false if no settings found - */ - public function getOptions() { - if($this->owner->ContentReviewType == 'Custom') { - return $this->owner; - } - if($this->owner->ContentReviewType == 'Disabled') { - return false; - } - - $page = $this->owner; - // $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 - */ - public function getOwnerNames() { - $options = $this->getOptions(); - $names = array(); - if(!$options) { - return ''; - } - foreach($options->OwnerGroups() as $group) { - $names[] = $group->getBreadcrumbs(' > '); - } - - foreach($options->OwnerUsers() as $group) { - $names[] = $group->getName(); - } - return implode(', ', $names); - } + /** + * @var array + */ + private static $belongs_many_many = array( + "ContentReviewGroups" => "Group", + "ContentReviewUsers" => "Member", + ); - /** - * - * @return string - */ - public function getEditorName() { - $member = Member::currentUser(); - if($member) { - return $member->FirstName .' '. $member->Surname; - } - 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() { - return SiteTreeContentReview::merge_owners($this->OwnerGroups(), $this->OwnerUsers()); - } - - /** - * @return ManyManyList - */ - public function OwnerGroups() { - return $this->owner->getManyManyComponents('ContentReviewGroups'); - } - - /** - * @return ManyManyList - */ - public function OwnerUsers() { - return $this->owner->getManyManyComponents('ContentReviewUsers'); - } + /** + * @var array + */ + private static $schedule = array( + 0 => "No automatic review date", + 1 => "1 day", + 7 => "1 week", + 30 => "1 month", + 60 => "2 months", + 91 => "3 months", + 121 => "4 months", + 152 => "5 months", + 183 => "6 months", + 365 => "12 months", + ); - /** - * - * @param FieldList $fields - * @return void - */ - public function updateSettingsFields(FieldList $fields) { - - Requirements::javascript('contentreview/javascript/contentreview.js'); - - // 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($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"), $schedule[$this->owner->ReviewPeriodDays]); - } - - $logConfig = GridFieldConfig::create() - ->addComponent(new GridFieldSortableHeader()) - ->addComponent($logColumns = new GridFieldDataColumns()); - // Cast the value to the users prefered date format - $logColumns->setFieldCasting(array( - 'Created' => 'DateTimeField->value' - )); - $logs = GridField::create('ROReviewNotes', 'Review Notes', $this->owner->ReviewLogs(), $logConfig); - - - $optionsFrom = ReadonlyField::create('ROType', _t('ContentReview.SETTINGSFROM', "Options are"), $this->owner->ContentReviewType); - - $fields->addFieldsToTab("Root.ContentReview", array( - $contentOwners, - $nextReviewAt->performReadonlyTransformation(), - $reviewFreq, - $optionsFrom, - $logs - )); - 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); - - $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')); + /** + * @return array + */ + public static function get_schedule() + { + return self::$schedule; + } - $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('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')); - - $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"), 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()); - - $schedule = self::get_schedule(); - - $fields->addFieldsToTab("Root.ContentReview", array( - new HeaderField(_t('ContentReview.REVIEWHEADER', "Content review"), 2), - $viewersOptionsField, - CompositeField::create( - $userField, - $groupField, - $reviewDate, - $reviewFrequency - )->addExtraClass('custom-settings'), - ReadonlyField::create('ROContentOwners', _t('ContentReview.CONTENTOWNERS', 'Content Owners'), $this->getOwnerNames()), - ReadonlyField::create('RONextReviewDate', _t("ContentReview.NEXTREVIEWDATE", "Next review date"), $this->owner->NextReviewDate), - $notesField - )); - } - - /** - * Creates a ContentReviewLog and connects it to this Page - * - * @param Member $reviewer - * @param string $message - */ - public function addReviewNote(Member $reviewer, $message) { - $reviewLog = ContentReviewLog::create(); - $reviewLog->Note = $message; - $reviewLog->ReviewerID = $reviewer->ID; - $this->owner->ReviewLogs()->add($reviewLog); - } - - /** - * Advance review date to the next date based on review period or set it to null - * if there is no schedule - * - * @return bool - returns true if date was set and false is content review is 'off' - */ - public function advanceReviewDate() { - $options = $this->getOptions(); - $nextDate = false; - if($options && $options->ReviewPeriodDays) { - $nextDate = date('Y-m-d', strtotime('+ '.$options->ReviewPeriodDays.' days', SS_Datetime::now()->format('U'))); - } - $this->owner->NextReviewDate = $nextDate; - $this->owner->write(); - return (bool)$nextDate; - } - - /** - * Check if a review is due by a member for this owner - * - * @param Member $member - * @return boolean - */ - public function canBeReviewedBy(Member $member = null) { - if(!$this->owner->obj('NextReviewDate')->exists()) { - return false; - } - if($this->owner->obj('NextReviewDate')->InFuture()) { - return false; - } - - $options = $this->getOptions(); - - if($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) { - return false; - } - - // This content should be reviewed by someone - if(!$member) { - return true; - } - - if($member->inGroups($options->OwnerGroups())) { - return true; - } - - if($options->OwnerUsers()->find('ID', $member->ID)) { - return true; - } - return false; - } + /** + * Takes a list of groups and members and return a list of unique member. + * + * @param SS_List $groups + * @param SS_List $members + * + * @return ArrayList + */ + public static function merge_owners(SS_List $groups, SS_List $members) + { + $contentReviewOwners = new ArrayList(); - /** - * Set the review data from the review period, if set. - */ - public function onBeforeWrite() { - $this->owner->LastEditedByName=$this->owner->getEditorName(); - $this->owner->OwnerNames = $this->owner->getOwnerNames(); - - // 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') { - $this->setDefaultReviewDateForDisabled(); - // Changed to Custom - } elseif($this->owner->ContentReviewType == 'Custom') { - $this->setDefaultReviewDateForCustom(); - // Changed to Inherit - } else { - $this->setDefaultReviewDateForInherited(); - } - } - - // Ensure that a inherited page always have a next review date - if($this->owner->ContentReviewType == 'Inherit' && !$this->owner->NextReviewDate) { - $this->setDefaultReviewDateForInherited(); - } - - // 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 created, otherwise the stageChildren - // method will grab all pages in the DB (this messes up unittesting) - if(!$this->owner->exists()) { - return; - } - - // parent page change it's review period - // && !$this->owner->isChanged('ContentReviewType', 2) - if($this->owner->isChanged('ReviewPeriodDays', 2)) { - $nextReviewUnixSec = strtotime(' + '.$this->owner->ReviewPeriodDays . ' days', SS_Datetime::now()->format('U')); - $this->owner->NextReviewDate = date('Y-m-d', $nextReviewUnixSec); - } - - // - if($this->owner->isChanged('NextReviewDate', 2)) { - $c = ContentReviewCompatability::start(); - $children = $this->owner->stageChildren(true)->filter('ContentReviewType', 'Inherit'); - foreach($children as $child) { - $child->NextReviewDate = $this->owner->NextReviewDate; - $child->write(); - } - ContentReviewCompatability::done($c); - } - } - - /** - * - */ - private function setDefaultReviewDateForDisabled() { - $this->owner->NextReviewDate = null; - } - - /** - * - */ - protected function setDefaultReviewDateForCustom() { - if($this->owner->NextReviewDate) { - return; - } - - $this->owner->NextReviewDate = null; - $nextDate = $this->getReviewDate(); - - if(is_object($nextDate)) { - $this->owner->NextReviewDate = $nextDate->getValue(); - } else { - $this->owner->NextReviewDate = $nextDate; - } - - } - - /** - * - */ - protected function setDefaultReviewDateForInherited() { - $options = $this->getOptions(); - $nextDate = null; - // Set the review date to be the same as the parents reviewdate - if($options && $this->owner->parent()->exists()) { - $nextDate = $this->getReviewDate($this->owner->parent()); - // Use the defualt settings - } elseif($options instanceof SiteConfig) { - $nextDate = $this->getReviewDate(); - } - if(is_object($nextDate)) { - $this->owner->NextReviewDate = $nextDate->getValue(); - } else { - $this->owner->NextReviewDate = $nextDate; - } - } - - /** - * Provide permissions to the CMS - * - * @return array - */ - public function providePermissions() { - return array( - "EDIT_CONTENT_REVIEW_FIELDS" => array( - 'name' => "Set content owners and review dates", - 'category' => _t('Permissions.CONTENT_CATEGORY', 'Content permissions'), - 'sort' => 50 - ) - ); - } + if ($groups->count()) { + $groupIDs = array(); - /** - * If the queuedjobs module is installed, queue up the first job for 9am tomorrow morning (by default) - */ - public function requireDefaultRecords() { - if(class_exists('ContentReviewNotificationJob')) { - // Ensure there is not already a job queued - if(QueuedJobDescriptor::get()->filter('Implementation', 'ContentReviewNotificationJob')->first()) return; + foreach ($groups as $group) { + $familyIDs = $group->collateFamilyIDs(); - $nextRun = new ContentReviewNotificationJob(); - $runHour = Config::inst()->get('ContentReviewNotificationJob', 'first_run_hour'); - $firstRunTime = date('Y-m-d H:i:s', mktime($runHour, 0, 0, date("m"), date("d")+1, date("y"))); + if (is_array($familyIDs)) { + $groupIDs = array_merge($groupIDs, array_values($familyIDs)); + } + } - singleton('QueuedJobService')->queueJob( - $nextRun, - $firstRunTime - ); + array_unique($groupIDs); - DB::alteration_message(sprintf('Added ContentReviewNotificationJob to run at %s', $firstRunTime)); - } - } + 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($members); + $contentReviewOwners->removeDuplicates(); + + return $contentReviewOwners; + } + + /** + * @param FieldList $actions + */ + public function updateCMSActions(FieldList $actions) + { + 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")); + + $actions->push($reviewAction); + } + } + + /** + * Returns false if the content review have disabled. + * + * @param SiteTree $page + * + * @return bool|Date + */ + public function getReviewDate(SiteTree $page = null) + { + if ($page === null) { + $page = $this->owner; + } + + if ($page->obj("NextReviewDate")->exists()) { + return $page->obj("NextReviewDate"); + } + + $options = $this->owner->getOptions(); + + if (!$options) { + return false; + } + + if (!$options->ReviewPeriodDays) { + return false; + } + + // Failover to check on ReviewPeriodDays + LastEdited + $nextReviewUnixSec = strtotime(" + " . $options->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. Either: + * + * - a SiteTreeContentReview decorated object + * - the default SiteTree config + * - false if this page have it's content review disabled + * + * Will go through parents and root pages will use the site config if their setting is Inherit. + * + * @return bool|DataObject + * + * @throws Exception + */ + public function getOptions() + { + if ($this->owner->ContentReviewType == "Custom") { + return $this->owner; + } + + if ($this->owner->ContentReviewType == "Disabled") { + return false; + } + + $page = $this->owner; + + // $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 site config + 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 + */ + public function getOwnerNames() + { + $options = $this->getOptions(); + + $names = array(); + + if (!$options) { + return ""; + } + + foreach ($options->OwnerGroups() as $group) { + $names[] = $group->getBreadcrumbs(" > "); + } + + foreach ($options->OwnerUsers() as $group) { + $names[] = $group->getName(); + } + + return implode(", ", $names); + } + + /** + * @return null|string + */ + public function getEditorName() + { + $member = Member::currentUser(); + + if ($member) { + return $member->FirstName . " " . $member->Surname; + } + + 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() + { + return SiteTreeContentReview::merge_owners( + $this->OwnerGroups(), + $this->OwnerUsers() + ); + } + + /** + * @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 updateSettingsFields(FieldList $fields) + { + Requirements::javascript("contentreview/javascript/contentreview.js"); + + // 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($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"), $schedule[$this->owner->ReviewPeriodDays]); + } + + $logConfig = GridFieldConfig::create() + ->addComponent(new GridFieldSortableHeader()) + ->addComponent($logColumns = new GridFieldDataColumns()); + + // Cast the value to the users preferred date format + $logColumns->setFieldCasting(array( + "Created" => "DateTimeField->value", + )); + + $logs = GridField::create("ROReviewNotes", "Review Notes", $this->owner->ReviewLogs(), $logConfig); + + + $optionsFrom = ReadonlyField::create("ROType", _t("ContentReview.SETTINGSFROM", "Options are"), $this->owner->ContentReviewType); + + $fields->addFieldsToTab("Root.ContentReview", array( + $contentOwners, + $nextReviewAt->performReadonlyTransformation(), + $reviewFreq, + $optionsFrom, + $logs, + )); + + 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); + + $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')); + + $groupsMap = array(); + + foreach (Group::get() as $group) { + $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")); + + $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"), 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()); + + $fields->addFieldsToTab("Root.ContentReview", array( + new HeaderField(_t("ContentReview.REVIEWHEADER", "Content review"), 2), + $viewersOptionsField, + CompositeField::create( + $userField, + $groupField, + $reviewDate, + $reviewFrequency + )->addExtraClass("custom-settings"), + ReadonlyField::create("ROContentOwners", _t("ContentReview.CONTENTOWNERS", "Content Owners"), $this->getOwnerNames()), + ReadonlyField::create("RONextReviewDate", _t("ContentReview.NEXTREVIEWDATE", "Next review date"), $this->owner->NextReviewDate), + $notesField, + )); + } + + /** + * Creates a ContentReviewLog and connects it to this Page. + * + * @param Member $reviewer + * @param string $message + */ + public function addReviewNote(Member $reviewer, $message) + { + $reviewLog = ContentReviewLog::create(); + $reviewLog->Note = $message; + $reviewLog->ReviewerID = $reviewer->ID; + $this->owner->ReviewLogs()->add($reviewLog); + } + + /** + * Advance review date to the next date based on review period or set it to null + * if there is no schedule. Returns true if date was required and false is content + * review is 'off'. + * + * @return bool + */ + public function advanceReviewDate() + { + $nextDate = false; + $options = $this->getOptions(); + + if ($options && $options->ReviewPeriodDays) { + $nextDate = date('Y-m-d', strtotime('+ ' . $options->ReviewPeriodDays . ' days', SS_Datetime::now()->format('U'))); + + $this->owner->NextReviewDate = $nextDate; + $this->owner->write(); + } + + return (bool) $nextDate; + } + + /** + * Check if a review is due by a member for this owner. + * + * @param Member $member + * + * @return bool + */ + public function canBeReviewedBy(Member $member = null) + { + if (!$this->owner->obj("NextReviewDate")->exists()) { + return false; + } + + if ($this->owner->obj("NextReviewDate")->InFuture()) { + return false; + } + + $options = $this->getOptions(); + + if ($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) { + return false; + } + + if (!$member) { + return true; + } + + if ($member->inGroups($options->OwnerGroups())) { + return true; + } + + if ($options->OwnerUsers()->find("ID", $member->ID)) { + return true; + } + + return false; + } + + /** + * Set the review data from the review period, if set. + */ + public function onBeforeWrite() + { + $this->owner->LastEditedByName = $this->owner->getEditorName(); + $this->owner->OwnerNames = $this->owner->getOwnerNames(); + + // If the user changed the type, we need to recalculate the review date. + if ($this->owner->isChanged("ContentReviewType", 2)) { + if ($this->owner->ContentReviewType == "Disabled") { + $this->setDefaultReviewDateForDisabled(); + } elseif ($this->owner->ContentReviewType == "Custom") { + $this->setDefaultReviewDateForCustom(); + } else { + $this->setDefaultReviewDateForInherited(); + } + } + + // Ensure that a inherited page always have a next review date + if ($this->owner->ContentReviewType == "Inherit" && !$this->owner->NextReviewDate) { + $this->setDefaultReviewDateForInherited(); + } + + // We need to update all the child pages that inherit this setting. We can only + // change children after this record has been created, otherwise the stageChildren + // method will grab all pages in the DB (this messes up unit testing) + if (!$this->owner->exists()) { + return; + } + + // parent page change it's review period + // && !$this->owner->isChanged('ContentReviewType', 2) + if ($this->owner->isChanged("ReviewPeriodDays", 2)) { + $nextReviewUnixSec = strtotime(" + " . $this->owner->ReviewPeriodDays . " days", SS_Datetime::now()->format("U")); + $this->owner->NextReviewDate = date("Y-m-d", $nextReviewUnixSec); + } + + // + if ($this->owner->isChanged("NextReviewDate", 2)) { + $children = $this->owner->stageChildren(true)->filter("ContentReviewType", "Inherit"); + $compatibility = ContentReviewCompatability::start(); + + foreach ($children as $child) { + $child->NextReviewDate = $this->owner->NextReviewDate; + $child->write(); + } + + ContentReviewCompatability::done($compatibility); + } + } + + private function setDefaultReviewDateForDisabled() + { + $this->owner->NextReviewDate = null; + } + + protected function setDefaultReviewDateForCustom() + { + if ($this->owner->NextReviewDate) { + return; + } + + $this->owner->NextReviewDate = null; + $nextDate = $this->getReviewDate(); + + if (is_object($nextDate)) { + $this->owner->NextReviewDate = $nextDate->getValue(); + } else { + $this->owner->NextReviewDate = $nextDate; + } + } + + protected function setDefaultReviewDateForInherited() + { + $options = $this->getOptions(); + $nextDate = null; + + if ($options && $this->owner->parent()->exists()) { + $nextDate = $this->getReviewDate($this->owner->parent()); + } elseif ($options instanceof SiteConfig) { + $nextDate = $this->getReviewDate(); + } + + if (is_object($nextDate)) { + $this->owner->NextReviewDate = $nextDate->getValue(); + } else { + $this->owner->NextReviewDate = $nextDate; + } + } + + /** + * Provide permissions to the CMS. + * + * @return array + */ + public function providePermissions() + { + return array( + "EDIT_CONTENT_REVIEW_FIELDS" => array( + "name" => "Set content owners and review dates", + "category" => _t("Permissions.CONTENT_CATEGORY", "Content permissions"), + "sort" => 50, + ), + ); + } + + /** + * If the queued jobs module is installed, queue up the first job for 9am tomorrow morning + * (by default). + */ + public function requireDefaultRecords() + { + if (class_exists("ContentReviewNotificationJob")) { + // Ensure there is not already a job queued + if (QueuedJobDescriptor::get()->filter("Implementation", "ContentReviewNotificationJob")->first()) { + return; + } + + $nextRun = new ContentReviewNotificationJob(); + $runHour = Config::inst()->get("ContentReviewNotificationJob", "first_run_hour"); + $firstRunTime = date("Y-m-d H:i:s", mktime($runHour, 0, 0, date("m"), date("d") + 1, date("y"))); + + singleton("QueuedJobService")->queueJob( + $nextRun, + $firstRunTime + ); + + DB::alteration_message(sprintf("Added ContentReviewNotificationJob to run at %s", $firstRunTime)); + } + } } diff --git a/code/jobs/ContentReviewNotificationJob.php b/code/jobs/ContentReviewNotificationJob.php index 654a71f..7dfdd7b 100644 --- a/code/jobs/ContentReviewNotificationJob.php +++ b/code/jobs/ContentReviewNotificationJob.php @@ -1,82 +1,106 @@ totalSteps = 1; - return QueuedJob::QUEUED; - } + /** + * @return string + */ + public function getJobType() + { + $this->totalSteps = 1; - public function process() { - $this->queueNextRun(); + return QueuedJob::QUEUED; + } - $task = new ContentReviewEmails(); - $task->run(new SS_HTTPRequest('GET', '/dev/tasks/ContentReviewEmails')); + public function process() + { + $this->queueNextRun(); - $this->currentStep = 1; - $this->isComplete = true; - } + $task = new ContentReviewEmails(); + $task->run(new SS_HTTPRequest("GET", "/dev/tasks/ContentReviewEmails")); - /** - * Queue up the next job to run. - */ - protected function queueNextRun() { - $nextRun = new ContentReviewNotificationJob(); - $nextRunTime = mktime( - self::$next_run_hour, - self::$next_run_minute, - 0, - date('m'), - date('d') + self::$next_run_in_days, - date('Y') - ); + $this->currentStep = 1; + $this->isComplete = true; + } - singleton('QueuedJobService')->queueJob( - $nextRun, - date('Y-m-d H:i:s', $nextRunTime) - ); - } -} \ No newline at end of file + /** + * Queue up the next job to run. + */ + protected function queueNextRun() + { + $nextRun = new ContentReviewNotificationJob(); + + $nextRunTime = mktime( + self::$next_run_hour, + self::$next_run_minute, + 0, + date("m"), + date("d") + self::$next_run_in_days, + date("Y") + ); + + singleton("QueuedJobService")->queueJob( + $nextRun, + date("Y-m-d H:i:s", $nextRunTime) + ); + } +} diff --git a/code/models/ContentReviewLog.php b/code/models/ContentReviewLog.php index d1aa737..4c890c8 100644 --- a/code/models/ContentReviewLog.php +++ b/code/models/ContentReviewLog.php @@ -1,45 +1,43 @@ 'Text' - ); - - /** - * - * @var array - */ - private static $has_one = array( - 'Reviewer' => 'Member', - 'SiteTree' => 'SiteTree' - ); - - /** - * - * @var array - */ - private static $summary_fields = array( - 'Note' => array('title' => 'Note'), - 'Created' => array('title' => 'Reviewed at'), - 'Reviewer.Title' => array('title' => 'Reviewed by') - ); - - /** - * - * @var string - */ - private static $default_sort = 'Created DESC'; - - /** - * - * @return bool - */ - public function canView($member = null) { - return (bool)Member::currentUser(); - } +class ContentReviewLog extends DataObject +{ + /** + * @var array + */ + private static $db = array( + "Note" => "Text", + ); + + /** + * @var array + */ + private static $has_one = array( + "Reviewer" => "Member", + "SiteTree" => "SiteTree", + ); + + /** + * @var array + */ + private static $summary_fields = array( + "Note" => array("title" => "Note"), + "Created" => array("title" => "Reviewed at"), + "Reviewer.Title" => array("title" => "Reviewed by"), + ); + + /** + * @var string + */ + private static $default_sort = "Created DESC"; + + /** + * @param mixed $member + * + * @return bool + */ + public function canView($member = null) + { + return (bool) Member::currentUser(); + } } diff --git a/code/reports/PagesDueForReviewReport.php b/code/reports/PagesDueForReviewReport.php index 682f38b..5e0baf0 100644 --- a/code/reports/PagesDueForReviewReport.php +++ b/code/reports/PagesDueForReviewReport.php @@ -1,165 +1,168 @@ push( + DateField::create("ReviewDateAfter", "Review date after or on") + ->setConfig("showcalendar", true) + ); - $filtersList->push( - DateField::create('ReviewDateAfter', 'Review date after or on') - ->setConfig('showcalendar', true) - ); - $filtersList->push( - DateField::create('ReviewDateBefore', 'Review date before or on', date('d/m/Y', strtotime('midnight'))) - ->setConfig('showcalendar', true) - ); + $filtersList->push( + DateField::create("ReviewDateBefore", "Review date before or on", date("d/m/Y", strtotime("midnight"))) + ->setConfig("showcalendar", true) + ); - $filtersList->push(new CheckboxField('ShowVirtualPages', 'Show Virtual Pages')); + $filtersList->push(new CheckboxField("ShowVirtualPages", "Show Virtual Pages")); - return $filtersList; - } + return $filtersList; + } - /** - * - * @return array - */ - public function columns() { - - $linkBase = singleton('CMSPageEditController')->Link('show'); - $linkPath = parse_url($linkBase, PHP_URL_PATH); - $linkQuery = parse_url($linkBase, PHP_URL_QUERY); + /** + * @return array + */ + public function columns() + { + $linkBase = singleton("CMSPageEditController")->Link("show"); + $linkPath = parse_url($linkBase, PHP_URL_PATH); + $linkQuery = parse_url($linkBase, PHP_URL_QUERY); - - $fields = array( - 'Title' => array( - 'title' => 'Page name', - 'formatting' => sprintf('$value', $linkPath, $linkQuery) - ), - 'NextReviewDate' => array( - 'title' => 'Review Date', - 'casting' => 'Date->Full', - 'formatting' => function($value, $item) { - if($item->ContentReviewType == 'Disabled') { - return 'disabled'; - } - if($item->ContentReviewType == 'Inherit') { - $setting = $item->getOptions(); - if(!$setting) { - return 'disabled'; - } - return $item->obj('NextReviewDate')->Full(); - } - return $value; - } - ), - 'OwnerNames' => array( - 'title' => 'Owner' - ), - 'LastEditedByName' => 'Last edited by', - 'AbsoluteLink' => array( - 'title' => 'URL', - 'formatting' => function($value, $item) { - $liveLink = $item->AbsoluteLiveLink; - $stageLink = $item->AbsoluteLink(); - return sprintf('%s %s', - $stageLink, - $liveLink ? $liveLink : $stageLink . '?stage=Stage', - $liveLink ? '(live)' : '(draft)' - ); - } - ), - 'ContentReviewType' => array( - 'title' => 'Settings are', - 'formatting' => function($value, $item) use($linkPath, $linkQuery) { - if($item->ContentReviewType == 'Inherit') { - $options = $item->getOptions(); - if($options && $options instanceof SiteConfig) { - return 'Inherited from Settings'; - } elseif($options) { - return sprintf( - 'Inherited from %s', - $linkPath, - $options->ID, - $linkQuery, - $options->Title - ); - } - } - return $value; - } - ), - ); + $fields = array( + "Title" => array( + "title" => "Page name", + "formatting" => "\$value" + ), + "NextReviewDate" => array( + "title" => "Review Date", + "casting" => "Date->Full", + "formatting" => function ($value, $item) { + if ($item->ContentReviewType == "Disabled") { + return "disabled"; + } + if ($item->ContentReviewType == "Inherit") { + $setting = $item->getOptions(); + if (!$setting) { + return "disabled"; + } + return $item->obj("NextReviewDate")->Full(); + } + return $value; + } + ), + "OwnerNames" => array( + "title" => "Owner" + ), + "LastEditedByName" => "Last edited by", + "AbsoluteLink" => array( + "title" => "URL", + "formatting" => function ($value, $item) { + $liveLink = $item->AbsoluteLiveLink; + $stageLink = $item->AbsoluteLink(); - return $fields; - } + return sprintf("%s %s", + $stageLink, + $liveLink ? $liveLink : $stageLink . "?stage=Stage", + $liveLink ? "(live)" : "(draft)" + ); + } + ), + "ContentReviewType" => array( + "title" => "Settings are", + "formatting" => function ($value, $item) use ($linkPath,$linkQuery) { + if ($item->ContentReviewType == "Inherit") { + $options = $item->getOptions(); + if ($options && $options instanceof SiteConfig) { + return "Inherited from Settings"; + } elseif ($options) { + return sprintf( + "Inherited from %s", + $linkPath, + $options->ID, + $linkQuery, + $options->Title + ); + } + } - /** - * - * @param array $params - * @return SS_List - */ - public function sourceRecords($params = array()) { - Versioned::reading_stage('Stage'); - $c = ContentReviewCompatability::start(); - $records = SiteTree::get(); + return $value; + } + ), + ); - if(empty($params['ReviewDateBefore']) && empty($params['ReviewDateAfter'])) { - // If there's no review dates set, default to all pages due for review now - $reviewDate = new Zend_Date(SS_Datetime::now()->Format('U')); - $reviewDate->add(1, Zend_Date::DAY); - $records = $records->where(sprintf('"NextReviewDate" < \'%s\'', $reviewDate->toString('YYYY-MM-dd'))); - } else { - // Review date before - if(!empty($params['ReviewDateBefore'])) { - // TODO Get value from DateField->dataValue() once we have access to form elements here - $reviewDate = new Zend_Date($params['ReviewDateBefore'], i18n::get_date_format()); - $reviewDate->add(1, Zend_Date::DAY); - $records = $records->where(sprintf('"NextReviewDate" < \'%s\'', $reviewDate->toString('YYYY-MM-dd'))); - } + return $fields; + } - // Review date after - if(!empty($params['ReviewDateAfter'])) { - // TODO Get value from DateField->dataValue() once we have access to form elements here - $reviewDate = new Zend_Date($params['ReviewDateAfter'], i18n::get_date_format()); - $records = $records->where(sprintf('"NextReviewDate" >= \'%s\'', $reviewDate->toString('YYYY-MM-dd'))); - } - } + /** + * @param array $params + * + * @return SS_List + */public function sourceRecords($params = array()) + { + Versioned::reading_stage("Stage"); - // Show virtual pages? - if(empty($params['ShowVirtualPages'])) { - $virtualPageClasses = ClassInfo::subclassesFor('VirtualPage'); - $records = $records->where(sprintf( - '"SiteTree"."ClassName" NOT IN (\'%s\')', - implode("','", array_values($virtualPageClasses)) - )); - } + $records = SiteTree::get(); + $compatibility = ContentReviewCompatability::start(); - // Owner dropdown - if(!empty($params['ContentReviewOwner'])) { - $ownerNames = Convert::raw2sql($params['ContentReviewOwner']); - $records = $records->filter('OwnerNames:PartialMatch', $ownerNames); - } - - $r = new ArrayList($records->sort('NextReviewDate', 'DESC')->toArray()); - ContentReviewCompatability::done($c); - return $r; - } + if (empty($params["ReviewDateBefore"]) && empty($params["ReviewDateAfter"])) { + // If there's no review dates set, default to all pages due for review now + $reviewDate = new Zend_Date(SS_Datetime::now()->Format("U")); + $reviewDate->add(1, Zend_Date::DAY); + $records = $records->where(sprintf('"NextReviewDate" < \'%s\'', $reviewDate->toString("YYYY-MM-dd"))); + } else { + // Review date before + if (!empty($params['ReviewDateBefore'])) { + // TODO Get value from DateField->dataValue() once we have access to form elements here + $reviewDate = new Zend_Date($params["ReviewDateBefore"], Config::inst()->get("i18n", "date_format")); + $reviewDate->add(1, Zend_Date::DAY); + $records = $records->where(sprintf("\"NextReviewDate\" < '%s'", $reviewDate->toString("YYYY-MM-dd"))); + } + + // Review date after + if (!empty($params["ReviewDateAfter"])) { + // TODO Get value from DateField->dataValue() once we have access to form elements here + $reviewDate = new Zend_Date($params["ReviewDateAfter"], Config::inst()->get("i18n", "date_format")); + $records = $records->where(sprintf("\"NextReviewDate\" >= '%s'", $reviewDate->toString("YYYY-MM-dd"))); + } + } + + // Show virtual pages? + if (empty($params["ShowVirtualPages"])) { + $virtualPageClasses = ClassInfo::subclassesFor("VirtualPage"); + $records = $records->where(sprintf( + "\"SiteTree\".\"ClassName\" NOT IN ('%s')", + implode("','", array_values($virtualPageClasses)) + )); + } + + // Owner dropdown + if (!empty($params["ContentReviewOwner"])) { + $ownerNames = Convert::raw2sql($params["ContentReviewOwner"]); + $records = $records->filter("OwnerNames:PartialMatch", $ownerNames); + } + + $records = new ArrayList($records->sort("NextReviewDate", "DESC")->toArray()); + + ContentReviewCompatability::done($compatibility); + + return $records; + } } diff --git a/code/reports/PagesWithoutReviewScheduleReport.php b/code/reports/PagesWithoutReviewScheduleReport.php index f9cdd7a..8dbae9b 100644 --- a/code/reports/PagesWithoutReviewScheduleReport.php +++ b/code/reports/PagesWithoutReviewScheduleReport.php @@ -1,142 +1,148 @@ push(new CheckboxField("ShowVirtualPages", "Show Virtual Pages")); - /** - * - * @return \FieldList - */ - public function parameterFields() { - $params = new FieldList(); - $params->push(new CheckboxField('ShowVirtualPages', 'Show Virtual Pages')); - return $params; - } + return $params; + } - /** - * - * @return array - */ - public function columns() { - $linkBase = singleton('CMSPageEditController')->Link('show'); - $linkPath = parse_url($linkBase, PHP_URL_PATH); - $linkQuery = parse_url($linkBase, PHP_URL_QUERY); + /** + * @return array + */ + public function columns() + { + $linkBase = singleton("CMSPageEditController")->Link("show"); + $linkPath = parse_url($linkBase, PHP_URL_PATH); + $linkQuery = parse_url($linkBase, PHP_URL_QUERY); - $fields = array( - 'Title' => array( - 'title' => 'Page name', - 'formatting' => sprintf('$value', $linkPath, $linkQuery) - ), - 'NextReviewDate' => array( - 'title' => 'Review Date', - 'casting' => 'Date->Full' - ), - 'OwnerNames' => array( - 'title' => 'Owner' - ), - 'LastEditedByName' => 'Last edited by', - 'AbsoluteLink' => array( - 'title' => 'URL', - 'formatting' => function($value, $item) { - $liveLink = $item->AbsoluteLiveLink; - $stageLink = $item->AbsoluteLink(); - return sprintf('%s %s', - $stageLink, - $liveLink ? $liveLink : $stageLink . '?stage=Stage', - $liveLink ? '(live)' : '(draft)' - ); - } - ), - 'ContentReviewType' => array( - 'title' => 'Settings are', - 'formatting' => function($value, $item) use($linkPath, $linkQuery) { - if($item->ContentReviewType == 'Inherit') { - $options = $item->getOptions(); - if($options && $options instanceof SiteConfig) { - return 'Inherited from Settings'; - } elseif($options) { - return sprintf( - 'Inherited from %s', - $linkPath, - $options->ID, - $linkQuery, - $options->Title - ); - } - } - return $value; - } - ) - ); + $fields = array( + "Title" => array( + "title" => "Page name", + "formatting" => "\$value", + ), + "NextReviewDate" => array( + "title" => "Review Date", + "casting" => "Date->Full", + ), + "OwnerNames" => array( + "title" => "Owner", + ), + "LastEditedByName" => "Last edited by", + "AbsoluteLink" => array( + "title" => "URL", + "formatting" => function ($value, $item) { + $liveLink = $item->AbsoluteLiveLink; + $stageLink = $item->AbsoluteLink(); - return $fields; - } + return sprintf("%s %s", + $stageLink, + $liveLink ? $liveLink : $stageLink . "?stage=Stage", + $liveLink ? "(live)" : "(draft)" + ); + }, + ), + "ContentReviewType" => array( + "title" => "Settings are", + "formatting" => function ($value, $item) use ($linkPath, $linkQuery) { + if ($item->ContentReviewType == "Inherit") { + $options = $item->getOptions(); + if ($options && $options instanceof SiteConfig) { + return "Inherited from Settings"; + } elseif ($options) { + return sprintf( + "Inherited from %s", + $linkPath, + $options->ID, + $linkQuery, + $options->Title + ); + } + } - /** - * - * @param array $params - * @return SS_List - */ - public function sourceRecords($params = array()) { - Versioned::reading_stage('Stage'); - $c = ContentReviewCompatability::start(); - $records = SiteTree::get(); + return $value; + }, + ), + ); - // If there's no review dates set, default to all pages due for review now - // $records = $records->where('"NextReviewDate" IS NULL OR "OwnerNames" IS NULL OR "OwnerNames" = \'\''); + return $fields; + } - // Show virtual pages? - if(empty($params['ShowVirtualPages'])) { - $virtualPageClasses = ClassInfo::subclassesFor('VirtualPage'); - $records = $records->where(sprintf( - '"SiteTree"."ClassName" NOT IN (\'%s\')', - implode("','", array_values($virtualPageClasses)) - )); - } - - $records->sort('ParentID'); - $records = $records->toArray(); - - // Trim out calculated values - $list = new ArrayList(); - foreach($records as $record) { - if(!$this->hasReviewSchedule($record)) { - $list->push($record); - } - } - - ContentReviewCompatability::done($c); - return $list; - } - - /** - * - * @param DataObject $record - * @return boolean - */ - protected function hasReviewSchedule(DataObject $record) { - if(!$record->obj('NextReviewDate')->exists()) { - return false; - } + /** + * @param array $params + * + * @return SS_List + */ + public function sourceRecords($params = array()) + { + Versioned::reading_stage("Stage"); - $options = $record->getOptions(); - if($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) { - return false; - } + $records = SiteTree::get(); + $compatibility = ContentReviewCompatability::start(); - return true; - } -} \ No newline at end of file + // If there's no review dates set, default to all pages due for review now. + + // Show virtual pages? + if (empty($params["ShowVirtualPages"])) { + $virtualPageClasses = ClassInfo::subclassesFor("VirtualPage"); + $records = $records->where(sprintf( + "\"SiteTree\".\"ClassName\" NOT IN ('%s')", + implode("','", array_values($virtualPageClasses)) + )); + } + + $records->sort("ParentID"); + $records = $records->toArray(); + + // Trim out calculated values + $list = new ArrayList(); + foreach ($records as $record) { + if (!$this->hasReviewSchedule($record)) { + $list->push($record); + } + } + + ContentReviewCompatability::done($compatibility); + + return $list; + } + + /** + * @param DataObject $record + * + * @return bool + */ + protected function hasReviewSchedule(DataObject $record) + { + if (!$record->obj("NextReviewDate")->exists()) { + return false; + } + + $options = $record->getOptions(); + + if ($options->OwnerGroups()->count() == 0 && $options->OwnerUsers()->count() == 0) { + return false; + } + + return true; + } +} diff --git a/code/tasks/ContentReviewEmails.php b/code/tasks/ContentReviewEmails.php index 64d2959..a9f2343 100644 --- a/code/tasks/ContentReviewEmails.php +++ b/code/tasks/ContentReviewEmails.php @@ -1,92 +1,94 @@ URLDate() : SSDatetime::now()->URLDate(); - - // First grab all the pages with a custom setting - $pages = Page::get('Page')->where('"SiteTree"."NextReviewDate" <= \''.$now.'\''); - - $overduePages = $this->getOverduePagesForOwners($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->notifyOwner($memberID, $pages); - } - - ContentReviewCompatability::done($c); - } - - /** - * - * @param SS_list $pages - * @param array &$pages - * @return array - */ - protected function getOverduePagesForOwners(SS_list $pages) { - $overduePages = array(); - - foreach($pages as $page) { - if(!$page->canBeReviewedBy()) { - continue; - } - $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(); - } - $overduePages[$owner->ID]->push($page); - } - } - return $overduePages; - } - - /** - * - * @param int $owner - * @param array $pages - */ - 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'); + /** + * @param SS_HTTPRequest $request + */ + public function run($request) + { + $compatibility = ContentReviewCompatability::start(); - $subject = _t('ContentReviewEmails.SUBJECT', 'Page(s) are due for content review'); - $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->send(); - } + $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}'"); + + $overduePages = $this->getOverduePagesForOwners($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->notifyOwner($memberID, $pages); + } + + ContentReviewCompatability::done($compatibility); + } + + /** + * @param SS_list $pages + * + * @return array + */ + protected function getOverduePagesForOwners(SS_list $pages) + { + $overduePages = array(); + + foreach ($pages as $page) { + if (!$page->canBeReviewedBy()) { + continue; + } + + $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(); + } + + $overduePages[$owner->ID]->push($page); + } + } + + return $overduePages; + } + + /** + * @param int $ownerID + * @param array|SS_List $pages + */ + 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"); + + $subject = _t("ContentReviewEmails.SUBJECT", "Page(s) are due for content review"); + $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->send(); + } } diff --git a/code/tasks/ContentReviewOwnerMigrationTask.php b/code/tasks/ContentReviewOwnerMigrationTask.php index bec0d8f..f0c8029 100644 --- a/code/tasks/ContentReviewOwnerMigrationTask.php +++ b/code/tasks/ContentReviewOwnerMigrationTask.php @@ -1,25 +1,27 @@ OwnerID column to a new column name + * Task which migrates the ContentReview Module's SiteTree->OwnerID column to a new column name. */ -class ContentReviewOwnerMigrationTask extends BuildTask { - - /** - * - * @param SS_HTTPRequest $request - */ - public function run($request) { - $results = DB::query('SHOW columns from "SiteTree" WHERE "field" = \'OwnerID\''); - if ($results->numRecords() == 0) { - echo "

No need to run task. SiteTree->OwnerID doesn't exist

"; - } else { - DB::query('UPDATE "SiteTree" SET "ContentReviewOwnerID" = "OwnerID"'); - DB::query('UPDATE "SiteTree_Live" SET "ContentReviewOwnerID" = "OwnerID"'); - DB::query('UPDATE "SiteTree_versions" SET "ContentReviewOwnerID" = "OwnerID"'); - DB::query('ALTER TABLE "SiteTree" DROP COLUMN "OwnerID"'); - DB::query('ALTER TABLE "SiteTree_Live" DROP COLUMN "OwnerID"'); - DB::query('ALTER TABLE "SiteTree_versions" DROP COLUMN "OwnerID"'); - echo "

Migrated 3 tables. Dropped obsolete OwnerID column

"; - } - } -} \ No newline at end of file +class ContentReviewOwnerMigrationTask extends BuildTask +{ + /** + * @param SS_HTTPRequest $request + */ + public function run($request) + { + $results = DB::query("SHOW columns from \"SiteTree\" WHERE \"field\" = 'OwnerID'"); + + if ($results->numRecords() == 0) { + echo "

No need to run task. SiteTree->OwnerID doesn't exist

"; + } else { + DB::query("UPDATE \"SiteTree\" SET \"ContentReviewOwnerID\" = \"OwnerID\""); + DB::query("UPDATE \"SiteTree_Live\" SET \"ContentReviewOwnerID\" = \"OwnerID\""); + DB::query("UPDATE \"SiteTree_versions\" SET \"ContentReviewOwnerID\" = \"OwnerID\""); + DB::query("ALTER TABLE \"SiteTree\" DROP COLUMN \"OwnerID\""); + DB::query("ALTER TABLE \"SiteTree_Live\" DROP COLUMN \"OwnerID\""); + DB::query("ALTER TABLE \"SiteTree_versions\" DROP COLUMN \"OwnerID\""); + echo "

Migrated 3 tables. Dropped obsolete OwnerID column

"; + } + } +} diff --git a/tests/ContentReviewBaseTest.php b/tests/ContentReviewBaseTest.php index a34cff6..470792e 100644 --- a/tests/ContentReviewBaseTest.php +++ b/tests/ContentReviewBaseTest.php @@ -1,36 +1,42 @@ -post()) do not seem to get passed the tester's locale, but instead fallback to the default locale. - * - * So we set the pages locale to be the default locale, which will then match any subsequent requests. - * - * If creating pages in your unit tests (rather than reading from the fixtures file), you must explicitly call - * self::compat() on the page, for the same reasons as above. - */ - if(class_exists('Translatable')){ - fwrite(STDOUT, 'TRANSLATABLE DISABLED FFS'); - $this->translatableEnabledBefore = Translatable::is_enabled(); - Translatable::disable(); - } - } - - public function tearDown(){ - if(class_exists('Translatable')){ - if($this->translatableEnabledBefore) Translatable::enable(); - } - } - -} \ No newline at end of file +abstract class ContentReviewBaseTest extends FunctionalTest +{ + /** + * @var bool + */ + protected $translatableEnabledBefore; + + public function setUp() + { + parent::setUp(); + + /* + * We set the locale for pages explicitly, because if we don't, then we get into a situation + * where the page takes on the tester's (your) locale, and any calls to simulate subsequent requests + * (e.g. $this->post()) do not seem to get passed the tester's locale, but instead fallback to the default locale. + * + * So we set the pages locale to be the default locale, which will then match any subsequent requests. + * + * If creating pages in your unit tests (rather than reading from the fixtures file), you must explicitly call + * self::compat() on the page, for the same reasons as above. + */ + if (class_exists("Translatable")) { + $this->translatableEnabledBefore = SiteTree::has_extension("Translatable"); + SiteTree::remove_extension("Translatable"); + } + } + + public function tearDown() + { + if (class_exists("Translatable")) { + if ($this->translatableEnabledBefore) { + SiteTree::add_extension("Translatable"); + } + } + } +} diff --git a/tests/ContentReviewCMSPageEditControllerTest.php b/tests/ContentReviewCMSPageEditControllerTest.php index 8c0e8b8..e809719 100644 --- a/tests/ContentReviewCMSPageEditControllerTest.php +++ b/tests/ContentReviewCMSPageEditControllerTest.php @@ -1,67 +1,101 @@ array("SiteTreeContentReview"), - "Group" => array("ContentReviewOwner"), - "Member" => array("ContentReviewOwner"), - "CMSPageEditController" => array("ContentReviewCMSExtension"), - "SiteConfig" => array("ContentReviewDefaultSettings"), - ); - - public function testReviewedThrowsExceptionWithNoRecordID() { - $this->setExpectedException('SS_HTTPResponse_Exception', 'No record ID', 404); - $controller = new CMSPageEditController(); - $dummyForm = new CMSForm($controller, 'EditForm', new FieldList(), new FieldList()); - $controller->reviewed(array('ID'=>null, 'Message' => null), $dummyForm); - } - - public function testReviewedThrowsExceptionWithWrongRecordID() { - $this->setExpectedException('SS_HTTPResponse_Exception'); - $controller = new CMSPageEditController(); - $dummyForm = new CMSForm($controller, 'EditForm', new FieldList(), new FieldList()); - $controller->reviewed(array('ID'=>'FAIL', 'Message' => null), $dummyForm); - } - - public function testReviewedWithAuthor() { - $author = $this->objFromFixture('Member', 'author'); - $this->loginAs($author); - $page = $this->objFromFixture('Page', 'home'); - - $data = array( - 'action_reviewed' => 1, - 'ID' => $page->ID - ); - - $response = $this->post('admin/pages/edit/EditForm', $data); - $this->assertEquals('OK', $response->getStatusDescription()); - $this->assertEquals(200, $response->getStatusCode()); - } - - public function testSaveReview() { - $author = $this->objFromFixture('Member', 'author'); - $this->loginAs($author); - $page = $this->objFromFixture('Page', 'home'); - - $data = array( - 'action_save_review' => 1, - 'ID' => $page->ID, - 'ReviewNotes' => 'This is the best page ever' - ); - - $response = $this->post('admin/pages/edit/EditForm', $data); - - $this->assertEquals('OK', $response->getStatusDescription()); - $this->assertEquals(200, $response->getStatusCode()); - - $this->assertEquals(1, $page->ReviewLogs()->count()); - $reviewLog = $page->ReviewLogs()->first(); - - $this->assertEquals($data['ReviewNotes'], $reviewLog->Note); - } - -} +/** + * @mixin PHPUnit_Framework_TestCase + */ +class ContentReviewCMSPageEditControllerTest extends ContentReviewBaseTest +{ + /** + * @var string + */ + public static $fixture_file = "contentreview/tests/ContentReviewTest.yml"; + /** + * @var array + */ + protected $requiredExtensions = array( + "SiteTree" => array("SiteTreeContentReview"), + "Group" => array("ContentReviewOwner"), + "Member" => array("ContentReviewOwner"), + "CMSPageEditController" => array("ContentReviewCMSExtension"), + "SiteConfig" => array("ContentReviewDefaultSettings"), + ); + + public function testReviewedThrowsExceptionWithNoRecordID() + { + $this->setExpectedException("SS_HTTPResponse_Exception"); + + /** @var CMSPageEditController|ContentReviewCMSExtension $controller */ + $controller = new CMSPageEditController(); + + $dummyForm = new CMSForm($controller, "EditForm", new FieldList(), new FieldList()); + + $controller->reviewed(array( + "ID" => null, + "Message" => null, + ), $dummyForm); + } + + public function testReviewedThrowsExceptionWithWrongRecordID() + { + $this->setExpectedException("SS_HTTPResponse_Exception"); + + /** @var CMSPageEditController|ContentReviewCMSExtension $controller */ + $controller = new CMSPageEditController(); + + $dummyForm = new CMSForm($controller, "EditForm", new FieldList(), new FieldList()); + + $controller->reviewed(array( + "ID" => "FAIL", + "Message" => null, + ), $dummyForm); + } + + public function testReviewedWithAuthor() + { + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + $this->loginAs($author); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "home"); + + $data = array( + "action_reviewed" => 1, + "ID" => $page->ID, + ); + + $response = $this->post("admin/pages/edit/EditForm", $data); + + $this->assertEquals("OK", $response->getStatusDescription()); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testSaveReview() + { + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + $this->loginAs($author); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "home"); + + $data = array( + "action_save_review" => 1, + "ID" => $page->ID, + "ReviewNotes" => "This is the best page ever", + ); + + $response = $this->post("admin/pages/edit/EditForm", $data); + + $this->assertEquals("OK", $response->getStatusDescription()); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(1, $page->ReviewLogs()->count()); + + $reviewLog = $page->ReviewLogs()->first(); + + $this->assertEquals($data["ReviewNotes"], $reviewLog->Note); + } +} diff --git a/tests/ContentReviewNotificationTest.php b/tests/ContentReviewNotificationTest.php index 90c7ea1..c8f16fa 100644 --- a/tests/ContentReviewNotificationTest.php +++ b/tests/ContentReviewNotificationTest.php @@ -1,38 +1,47 @@ 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'); - - // This propagates the next review date to 'contact-child' page from the parent page - $childParentPage = $this->objFromFixture('Page', 'contact'); - $childParentPage->NextReviewDate = '2010-02-23'; - $childParentPage->write(); - - $task = new 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); - - $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']); - - SS_Datetime::clear_mock_now(); - } +/** + * @mixin PHPUnit_Framework_TestCase + */ +class ContentReviewNotificationTest extends SapphireTest +{ + /** + * @var string + */ + public static $fixture_file = "contentreview/tests/ContentReviewTest.yml"; + + /** + * @var array + */ + protected $requiredExtensions = array( + "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"); + + /** @var Page|SiteTreeContentReview $childParentPage */ + $childParentPage = $this->objFromFixture("Page", "contact"); + $childParentPage->NextReviewDate = "2010-02-23"; + $childParentPage->write(); + + $task = new 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); + + $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"]); + + SS_Datetime::clear_mock_now(); + } } diff --git a/tests/ContentReviewReportTest.php b/tests/ContentReviewReportTest.php index 6d9181b..bf5d6fa 100644 --- a/tests/ContentReviewReportTest.php +++ b/tests/ContentReviewReportTest.php @@ -1,67 +1,81 @@ array("SiteTreeContentReview"), - "Group" => array("ContentReviewOwner"), - "Member" => array("ContentReviewOwner"), - "CMSPageEditController" => array("ContentReviewCMSExtension"), - "SiteConfig" => array("ContentReviewDefaultSettings"), - ); - - public function testPagesDueForReviewReport() { - $editor = $this->objFromFixture('Member', 'editor'); - $this->logInAs($editor); - $report = new PagesDueForReviewReport(); - - $report->parameterFields(); - $report->columns(); - $report->title(); - - $results = $report->sourceRecords(array( - 'ReviewDateAfter' => '01/01/2010', - 'ReviewDateBefore' => '12/12/2010' - )); +class ContentReviewReportTest extends FunctionalTest +{ + /** + * @var string + */ + public static $fixture_file = "contentreview/tests/ContentReviewTest.yml"; - $this->assertEquals(array( - 'Contact Us', - 'Contact Us Child', - 'Staff', - 'About Us', - 'Home' - ), $results->column('Title')); - - SS_Datetime::set_mock_now('2010-02-13 00:00:00'); - $results = $report->sourceRecords(array()); - $this->assertEquals(array( - 'About Us', - 'Home' - ), $results->column('Title')); - - SS_Datetime::clear_mock_now(); - } - - public function testPagesWithoutReviewScheduleReport() { - $editor = $this->objFromFixture('Member', 'editor'); - $this->logInAs($editor); - $report = new PagesWithoutReviewScheduleReport(); - - $report->parameterFields(); - $report->columns(); - $report->title(); - - $results = $report->sourceRecords(); - - $this->assertEquals(array( - 'Home', - 'About Us', - 'Page without review date', - 'Page owned by group', - ), $results->column('Title')); - } - + /** + * @var array + */ + protected $requiredExtensions = array( + "SiteTree" => array("SiteTreeContentReview"), + "Group" => array("ContentReviewOwner"), + "Member" => array("ContentReviewOwner"), + "CMSPageEditController" => array("ContentReviewCMSExtension"), + "SiteConfig" => array("ContentReviewDefaultSettings"), + ); + + public function testPagesDueForReviewReport() + { + /** @var Member $editor */ + $editor = $this->objFromFixture("Member", "editor"); + + $this->logInAs($editor); + + $report = new PagesDueForReviewReport(); + + $report->parameterFields(); + $report->columns(); + $report->title(); + + $results = $report->sourceRecords(array( + "ReviewDateAfter" => "01/01/2010", + "ReviewDateBefore" => "12/12/2010", + )); + + $this->assertEquals(array( + "Contact Us", + "Contact Us Child", + "Staff", + "About Us", + "Home", + ), $results->column("Title")); + + SS_Datetime::set_mock_now("2010-02-13 00:00:00"); + + $results = $report->sourceRecords(array()); + + $this->assertEquals(array( + "About Us", + "Home", + ), $results->column("Title")); + + SS_Datetime::clear_mock_now(); + } + + public function testPagesWithoutReviewScheduleReport() + { + /** @var Member $editor */ + $editor = $this->objFromFixture("Member", "editor"); + + $this->logInAs($editor); + + $report = new PagesWithoutReviewScheduleReport(); + + $report->parameterFields(); + $report->columns(); + $report->title(); + + $results = $report->sourceRecords(); + + $this->assertEquals(array( + "Home", + "About Us", + "Page without review date", + "Page owned by group", + ), $results->column("Title")); + } } - diff --git a/tests/ContentReviewSettingsTest.php b/tests/ContentReviewSettingsTest.php index 8187e75..be447ab 100644 --- a/tests/ContentReviewSettingsTest.php +++ b/tests/ContentReviewSettingsTest.php @@ -1,196 +1,294 @@ array("SiteTreeContentReview"), - "Group" => array("ContentReviewOwner"), - "Member" => array("ContentReviewOwner"), - "CMSPageEditController" => array("ContentReviewCMSExtension"), - "SiteConfig" => array("ContentReviewDefaultSettings"), - ); - - public function testAdvanceReviewDate10Days() { - $page = new Page(); - $page->ContentReviewType = 'Custom'; - $page->ReviewPeriodDays = 10; - $this->assertTrue($page->advanceReviewDate()); - $page->write(); - $this->assertEquals(date('Y-m-d', strtotime('now + 10 days')), $page->NextReviewDate); - } - - public function testAdvanceReviewDateNull() { - $page = new Page(); - $page->ContentReviewType = 'Custom'; - $page->ReviewPeriodDays = 0; - $this->assertFalse($page->advanceReviewDate()); - $page->write(); - $this->assertEquals(null, $page->NextReviewDate); - } - - 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); - $this->assertEquals($page, $page->getOptions()); - } - - public function testGetSettingsObjectFromDisabled() { - $page = $this->objFromFixture('Page', 'disabled'); - $this->assertEquals('Disabled', $page->ContentReviewType); - $this->assertFalse($page->getOptions()); - } - - public function testGetOptionObjectFromInheritedDisabled() { - $page = $this->objFromFixture('Page', 'page-2-1-1'); - $this->assertEquals('Inherit', $page->ContentReviewType); - $this->assertFalse($page->getOptions()); - } - - public function testGetOptionObjectFromDeeplyInheritedPage() { - $page = $this->objFromFixture('Page', 'page-3-1-1-1'); - $this->assertEquals('Inherit', $page->ContentReviewType); - $this->assertInstanceOf('SiteConfig', $page->getOptions()); - } - - public function testGetSettingsObjectFromInheritPage() { - $page = $this->objFromFixture('Page', 'page-1-1'); - $parentPage = $this->objFromFixture('Page', 'page-1'); - $this->assertEquals('Inherit', $page->ContentReviewType); - $this->assertEquals(get_class($parentPage), get_class($page->getOptions())); - $this->assertEquals($parentPage->ID, $page->getOptions()->ID); - } + /** + * @var array + */ + protected $requiredExtensions = array( + "SiteTree" => array("SiteTreeContentReview"), + "Group" => array("ContentReviewOwner"), + "Member" => array("ContentReviewOwner"), + "CMSPageEditController" => array("ContentReviewCMSExtension"), + "SiteConfig" => array("ContentReviewDefaultSettings"), + ); - public function testGetSettingsObjectFromInheritedRootPage() { - $page = $this->objFromFixture('Page', 'inherit'); - $this->assertEquals('Inherit', $page->ContentReviewType); - $this->assertEquals($this->objFromFixture('SiteConfig', 'default'), $page->getOptions()); - } - - public function testGetNextReviewDateFromCustomSettings() { - $page = $this->objFromFixture('Page', 'custom'); - $date = $page->getReviewDate(); - $this->assertEquals('2010-02-01', $date->format('Y-m-d')); - } - - public function testGetNextReviewDateFromSiteConfigInheritedSetting() { - $page = $this->objFromFixture('Page', 'inherit'); - $nextReviewDate = $page->getReviewDate(); - - $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', 'page-1-1'); - $nextReviewDate = $page->getReviewDate(); - - $this->assertInstanceOf('Date', $nextReviewDate); - // 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 10 days, so childPage should have a - // review date now + 10 days - $this->assertNotEquals($oldChildDate, $childPage->NextReviewDate); - $this->assertEquals($this->addDaysToDate(date('Y-m-d'), 10), $childPage->NextReviewDate); - $this->assertEquals($parentPage->NextReviewDate, $childPage->NextReviewDate); - } - - // helper method for this test class - private function addDaysToDate($date, $days, $format='Y-m-d') { - if(is_object($date)) { - $sec = strtotime('+ '. $days .' days', $date->format('U')); - } else { - $sec = strtotime('+ '. $days .' days', strtotime($date)); - } - return date($format, $sec); - } -} \ No newline at end of file + public function testAdvanceReviewDate10Days() + { + /** @var Page|SiteTreeContentReview $page */ + $page = new Page(); + + $page->ContentReviewType = "Custom"; + $page->ReviewPeriodDays = 10; + + $this->assertTrue($page->advanceReviewDate()); + + $page->write(); + + $this->assertEquals(date("Y-m-d", strtotime("now + 10 days")), $page->NextReviewDate); + } + + public function testAdvanceReviewDateNull() + { + /** @var Page|SiteTreeContentReview $page */ + $page = new Page(); + + $page->ContentReviewType = "Custom"; + $page->ReviewPeriodDays = 0; + + $this->assertFalse($page->advanceReviewDate()); + + $page->write(); + + $this->assertEquals(null, $page->NextReviewDate); + } + + public function testAdvanceReviewFromCustomSettings() + { + /** @var Page|SiteTreeContentReview $page */ + $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() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "page-1-1"); + + /** @var Page|SiteTreeContentReview $parentPage */ + $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() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "inherit"); + + /** @var SiteConfig|ContentReviewDefaultSettings $siteConfig */ + $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() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "custom"); + + $this->assertEquals("Custom", $page->ContentReviewType); + $this->assertEquals($page, $page->getOptions()); + } + + public function testGetSettingsObjectFromDisabled() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "disabled"); + + $this->assertEquals("Disabled", $page->ContentReviewType); + $this->assertFalse($page->getOptions()); + } + + public function testGetOptionObjectFromInheritedDisabled() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "page-2-1-1"); + + $this->assertEquals("Inherit", $page->ContentReviewType); + $this->assertFalse($page->getOptions()); + } + + public function testGetOptionObjectFromDeeplyInheritedPage() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "page-3-1-1-1"); + + $this->assertEquals("Inherit", $page->ContentReviewType); + $this->assertInstanceOf("SiteConfig", $page->getOptions()); + } + + public function testGetSettingsObjectFromInheritPage() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "page-1-1"); + + /** @var Page|SiteTreeContentReview $parentPage */ + $parentPage = $this->objFromFixture("Page", "page-1"); + + $this->assertEquals("Inherit", $page->ContentReviewType); + $this->assertEquals(get_class($parentPage), get_class($page->getOptions())); + $this->assertEquals($parentPage->ID, $page->getOptions()->ID); + } + + public function testGetSettingsObjectFromInheritedRootPage() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "inherit"); + + $this->assertEquals("Inherit", $page->ContentReviewType); + $this->assertEquals($this->objFromFixture("SiteConfig", "default"), $page->getOptions()); + } + + public function testGetNextReviewDateFromCustomSettings() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture('Page', 'custom'); + + $date = $page->getReviewDate(); + + $this->assertEquals('2010-02-01', $date->format('Y-m-d')); + } + + public function testGetNextReviewDateFromSiteConfigInheritedSetting() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "inherit"); + + $nextReviewDate = $page->getReviewDate(); + + $this->assertInstanceOf("Date", $nextReviewDate); + + /** @var SiteConfig|ContentReviewDefaultSettings $siteConfig */ + $siteConfig = $this->objFromFixture("SiteConfig", "default"); + + $expected = $this->addDaysToDate(SS_Datetime::now(), $siteConfig->ReviewPeriodDays); + + $this->assertEquals($expected, $nextReviewDate->format("Y-m-d")); + } + + public function testGetNextReviewDateFromPageInheritedSetting() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "page-1-1"); + + $nextReviewDate = $page->getReviewDate(); + + $this->assertInstanceOf("Date", $nextReviewDate); + + /** @var Page|SiteTreeContentReview $nextPage */ + $nextPage = $this->objFromFixture("Page", "page-1"); + + $expected = $nextPage->NextReviewDate; + + $this->assertEquals($expected, $nextReviewDate->format("Y-m-d")); + } + + public function testUpdateNextReviewDateFromCustomToDisabled() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "custom"); + + // before write() + $this->assertEquals("2010-02-01", $page->NextReviewDate); + + $page->ContentReviewType = "Disabled"; + $page->write(); + + DataObject::flush_and_destroy_cache(); + unset($page); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "custom"); + + $this->assertNull($page->NextReviewDate); + } + + public function testUpdateNextReviewDateFromDisabledToCustom() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "disabled"); + + $this->assertNull($page->NextReviewDate); + + $page->ContentReviewType = "Custom"; + $page->ReviewPeriodDays = "7"; + $page->write(); + + DataObject::flush_and_destroy_cache(); + unset($page); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "disabled"); + + $expected = date("Y-m-d", strtotime("+ " . $page->ReviewPeriodDays . " days")); + + $this->assertEquals($expected, $page->NextReviewDate); + } + + public function testParentChangedOptionsAndChildShouldToo() + { + /** @var Page|SiteTreeContentReview $parentPage */ + $parentPage = $this->objFromFixture("Page", "page-1"); + + /** @var Page|SiteTreeContentReview $childPage */ + $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 + $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(); + + /** @var Page|SiteTreeContentReview $page */ + $parentPage = $this->objFromFixture("Page", "page-1"); + + /** @var Page|SiteTreeContentReview $page */ + $childPage = $this->objFromFixture("Page", "page-1-1"); + + // AFTER: parent page have a period of 10 days, so childPage should have + // a review date now + 10 days. + $this->assertNotEquals($oldChildDate, $childPage->NextReviewDate); + $this->assertEquals($this->addDaysToDate(date("Y-m-d"), 10), $childPage->NextReviewDate); + $this->assertEquals($parentPage->NextReviewDate, $childPage->NextReviewDate); + } + + /** + * @param string|SS_DateTime|DateTime $date + * @param int $days + * @param string $format + * + * @return bool|string + */ + private function addDaysToDate($date, $days, $format = "Y-m-d") + { + if (is_object($date)) { + $sec = strtotime("+ " . $days . " days", $date->format("U")); + } else { + $sec = strtotime("+ " . $days . " days", strtotime($date)); + } + + return date($format, $sec); + } +} diff --git a/tests/SiteTreeContentReviewTest.php b/tests/SiteTreeContentReviewTest.php index 609a2ba..858f816 100644 --- a/tests/SiteTreeContentReviewTest.php +++ b/tests/SiteTreeContentReviewTest.php @@ -1,177 +1,284 @@ array("SiteTreeContentReview"), - "Group" => array("ContentReviewOwner"), - "Member" => array("ContentReviewOwner"), - "CMSPageEditController" => array("ContentReviewCMSExtension"), - "SiteConfig" => array("ContentReviewDefaultSettings"), - ); - - public function testOwnerNames() { - $editor = $this->objFromFixture('Member', 'editor'); - $this->logInAs($editor); - - $page = new Page(); - $page->ReviewPeriodDays = 10; - $page->ContentReviewType ='Custom'; - $page->ContentReviewUsers()->push($editor); - $page->write(); +/** + * @mixin PHPUnit_Framework_TestCase + */ +class SiteTreeContentReviewTest extends ContentReviewBaseTest +{ + /** + * @var string + */ + public static $fixture_file = "contentreview/tests/ContentReviewTest.yml"; - $this->assertTrue($page->canPublish()); - $this->assertTrue($page->doPublish()); - $this->assertEquals($page->OwnerNames, "Test Editor", 'Test Editor should be the owner'); - - $page = $this->objFromFixture('Page', 'about'); - $page->OwnerUsers()->removeAll(); - $page->write(); - - $this->assertTrue($page->canPublish()); - $this->assertTrue($page->doPublish()); - $this->assertEquals('', $page->OwnerNames); - } - - public function testPermissionsExists() { - $perms = singleton('SiteTreeContentReview')->providePermissions(); - $this->assertTrue(isset($perms['EDIT_CONTENT_REVIEW_FIELDS'])); - } - - public function testUserWithPermissionCanEdit() { - $editor = $this->objFromFixture('Member', 'editor'); - $this->logInAs($editor); - $page = new Page(); - $fields = $page->getSettingsFields(); - $this->assertNotNull($fields->dataFieldByName('NextReviewDate')); - } - - public function testUserWithoutPermissionCannotEdit() { - $author = $this->objFromFixture('Member', 'author'); - $this->logInAs($author); - $page = new Page(); - $fields = $page->getSettingsFields(); - $this->assertNull($fields->dataFieldByName('NextReviewDate')); - } - - public function testAutomaticallyToNotSetReviewDate() { - $editor = $this->objFromFixture('Member', 'editor'); - $this->logInAs($editor); - - $page = new Page(); - $page->ReviewPeriodDays = 10; - $page->write(); - $this->assertTrue($page->doPublish()); - $this->assertEquals(null, $page->NextReviewDate); - } - - public function testAddReviewNote() { - $author = $this->objFromFixture('Member', 'author'); - $page = $this->objFromFixture('Page', 'home'); - $page->addReviewNote($author, 'This is a message'); - - // Get the page again to make sure it's not only cached in memory - $homepage = $this->objFromFixture('Page', 'home'); - $this->assertEquals(1, $homepage->ReviewLogs()->count()); - $this->assertEquals('This is a message', $homepage->ReviewLogs()->first()->Note); - } - - public function testGetContentReviewOwners() { - $page = $this->objFromFixture('Page', 'group-owned'); - $owners = $page->ContentReviewOwners(); - $this->assertEquals(1, $owners->count()); - $this->assertEquals('author@example.com', $owners->first()->Email); - } - - public function testCanNotBeReviewBecauseNoReviewDate() { - SS_Datetime::set_mock_now('2010-01-01 12:00:00'); - $author = $this->objFromFixture('Member', 'author'); - $page = $this->objFromFixture('Page', 'no-review'); - // page 'no-review' is owned by author, but there is no review date - $this->assertFalse($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testCanNotBeReviewedBecauseInFuture() { - SS_Datetime::set_mock_now('2010-01-01 12:00:00'); - $author = $this->objFromFixture('Member', 'author'); - $page = $this->objFromFixture('Page', 'staff'); - // page 'staff' is owned by author, but the review date is in the future - $this->assertFalse($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testCanNotBeReviewedByUser() { - SS_Datetime::set_mock_now('2010-03-01 12:00:00'); - $author = $this->objFromFixture('Member', 'author'); - $page = $this->objFromFixture('Page', 'home'); - // page 'home' doesnt have any owners - $this->assertFalse($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testCanBeReviewedByUser() { - SS_Datetime::set_mock_now('2010-03-01 12:00:00'); - $author = $this->objFromFixture('Member', 'author'); - $page = $this->objFromFixture('Page', 'staff'); - // page 'staff' is owned by author - $this->assertTrue($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testCanNotBeReviewedByGroup() { - SS_Datetime::set_mock_now('2010-03-01 12:00:00'); - $author = $this->objFromFixture('Member', 'editor'); - $page = $this->objFromFixture('Page', 'contact'); - // page 'contact' is owned by the authorgroup - $this->assertFalse($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testCanBeReviewedByGroup() { - SS_Datetime::set_mock_now('2010-03-01 12:00:00'); - $author = $this->objFromFixture('Member', 'author'); - $page = $this->objFromFixture('Page', 'contact'); - // page 'contact' is owned by the authorgroup - $this->assertTrue($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testCanBeReviewedFromInheritedSetting() { - SS_Datetime::set_mock_now('2013-03-01 12:00:00'); - $author = $this->objFromFixture('Member', 'author'); - $parentPage = $this->objFromFixture('Page', 'contact'); - // This saves the parentPages.NextReview date to the child page - $parentPage->NextReviewDate = '2013-01-01'; - $parentPage->write(); - - $page = $this->objFromFixture('Page', 'contact-child'); - $this->assertTrue($page->canBeReviewedBy($author)); - SS_Datetime::clear_mock_now(); - } - - public function testReviewActionVisibleForAuthor() { - SS_Datetime::set_mock_now('2020-03-01 12:00:00'); - $page = $this->objFromFixture('Page', 'contact'); - $author = $this->objFromFixture('Member', 'author'); - $this->logInAs($author); + /** + * @var array + */ + protected $requiredExtensions = array( + "SiteTree" => array("SiteTreeContentReview"), + "Group" => array("ContentReviewOwner"), + "Member" => array("ContentReviewOwner"), + "CMSPageEditController" => array("ContentReviewCMSExtension"), + "SiteConfig" => array("ContentReviewDefaultSettings"), + ); - $fields = $page->getCMSActions(); - $this->assertNotNull($fields->fieldByName('action_reviewed')); - SS_Datetime::clear_mock_now(); - } - - public function testReviewActionNotVisibleForEditor() { - SS_Datetime::set_mock_now('2020-03-01 12:00:00'); - $page = $this->objFromFixture('Page', 'contact'); - $author = $this->objFromFixture('Member', 'editor'); - $this->logInAs($author); - - $fields = $page->getCMSActions(); - $this->assertNull($fields->fieldByName('action_reviewed')); - SS_Datetime::clear_mock_now(); - } - + public function testOwnerNames() + { + /** @var Member $editor */ + $editor = $this->objFromFixture("Member", "editor"); + + $this->logInAs($editor); + + /** @var Page|SiteTreeContentReview $page */ + $page = new Page(); + $page->ReviewPeriodDays = 10; + $page->ContentReviewType = "Custom"; + + $page->ContentReviewUsers()->push($editor); + $page->write(); + + $this->assertTrue($page->canPublish()); + $this->assertTrue($page->doPublish()); + $this->assertEquals($page->OwnerNames, "Test Editor", "Test Editor should be the owner"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "about"); + + $page->OwnerUsers()->removeAll(); + $page->write(); + + $this->assertTrue($page->canPublish()); + $this->assertTrue($page->doPublish()); + $this->assertEquals("", $page->OwnerNames); + } + + public function testPermissionsExists() + { + $perms = singleton("SiteTreeContentReview")->providePermissions(); + + $this->assertTrue(isset($perms["EDIT_CONTENT_REVIEW_FIELDS"])); + } + + public function testUserWithPermissionCanEdit() + { + /** @var Member $editor */ + $editor = $this->objFromFixture("Member", "editor"); + + $this->logInAs($editor); + + /** @var Page|SiteTreeContentReview $page */ + $page = new Page(); + + $fields = $page->getSettingsFields(); + + $this->assertNotNull($fields->dataFieldByName("NextReviewDate")); + } + + public function testUserWithoutPermissionCannotEdit() + { + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + $this->logInAs($author); + + /** @var Page|SiteTreeContentReview $page */ + $page = new Page(); + + $fields = $page->getSettingsFields(); + + $this->assertNull($fields->dataFieldByName("NextReviewDate")); + } + + public function testAutomaticallyToNotSetReviewDate() + { + /** @var Member $editor */ + $editor = $this->objFromFixture("Member", "editor"); + + $this->logInAs($editor); + + /** @var Page|SiteTreeContentReview $page */ + $page = new Page(); + + $page->ReviewPeriodDays = 10; + $page->write(); + + $this->assertTrue($page->doPublish()); + $this->assertEquals(null, $page->NextReviewDate); + } + + public function testAddReviewNote() + { + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "home"); + + $page->addReviewNote($author, "This is a message"); + + /** @var Page|SiteTreeContentReview $page */ + $homepage = $this->objFromFixture("Page", "home"); + + $this->assertEquals(1, $homepage->ReviewLogs()->count()); + $this->assertEquals("This is a message", $homepage->ReviewLogs()->first()->Note); + } + + public function testGetContentReviewOwners() + { + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "group-owned"); + + $owners = $page->ContentReviewOwners(); + + $this->assertEquals(1, $owners->count()); + $this->assertEquals("author@example.com", $owners->first()->Email); + } + + public function testCanNotBeReviewBecauseNoReviewDate() + { + SS_Datetime::set_mock_now("2010-01-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "no-review"); + + $this->assertFalse($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testCanNotBeReviewedBecauseInFuture() + { + SS_Datetime::set_mock_now("2010-01-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "staff"); + + $this->assertFalse($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testCanNotBeReviewedByUser() + { + SS_Datetime::set_mock_now("2010-03-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "home"); + + $this->assertFalse($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testCanBeReviewedByUser() + { + SS_Datetime::set_mock_now("2010-03-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "staff"); + + $this->assertTrue($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testCanNotBeReviewedByGroup() + { + SS_Datetime::set_mock_now("2010-03-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "editor"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "contact"); + + $this->assertFalse($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testCanBeReviewedByGroup() + { + SS_Datetime::set_mock_now("2010-03-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "contact"); + + $this->assertTrue($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testCanBeReviewedFromInheritedSetting() + { + SS_Datetime::set_mock_now("2013-03-01 12:00:00"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + /** @var Page|SiteTreeContentReview $parentPage */ + $parentPage = $this->objFromFixture("Page", "contact"); + + $parentPage->NextReviewDate = "2013-01-01"; + $parentPage->write(); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "contact-child"); + + $this->assertTrue($page->canBeReviewedBy($author)); + + SS_Datetime::clear_mock_now(); + } + + public function testReviewActionVisibleForAuthor() + { + SS_Datetime::set_mock_now("2020-03-01 12:00:00"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "contact"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "author"); + + $this->logInAs($author); + + $fields = $page->getCMSActions(); + + $this->assertNotNull($fields->fieldByName("action_reviewed")); + + SS_Datetime::clear_mock_now(); + } + + public function testReviewActionNotVisibleForEditor() + { + SS_Datetime::set_mock_now("2020-03-01 12:00:00"); + + /** @var Page|SiteTreeContentReview $page */ + $page = $this->objFromFixture("Page", "contact"); + + /** @var Member $author */ + $author = $this->objFromFixture("Member", "editor"); + + $this->logInAs($author); + + $fields = $page->getCMSActions(); + + $this->assertNull($fields->fieldByName("action_reviewed")); + + SS_Datetime::clear_mock_now(); + } }