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 "