├── _config.php ├── templates └── Includes │ ├── raw │ ├── ContentNotifierEmailItem_RAW.ss │ └── ContentNotifierEmail_RAW.ss │ ├── ContentNotifierEmailItem.ss │ └── ContentNotifierEmail.ss ├── code-of-conduct.md ├── src ├── ContentNotifier.php ├── Admin │ └── ContentNotifierAdmin.php ├── Tasks │ ├── ContentNotifierTask.php │ └── ContentNotifierCleanTask.php ├── Model │ ├── ContentNotifierEmail.php │ └── ContentNotifierQueue.php └── Extensions │ └── ContentNotifierExtension.php ├── _config └── config.yml ├── .upgrade.yml ├── .editorconfig ├── composer.json ├── license.md └── README.md /_config.php: -------------------------------------------------------------------------------- 1 | $ContentNotifierHeadline 2 |

$ContentNotifierExcerpt

-------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct). 2 | -------------------------------------------------------------------------------- /src/ContentNotifier.php: -------------------------------------------------------------------------------- 1 | array( 12 | 'title' => 'Notifications' 13 | ) 14 | ); 15 | 16 | private static $menu_title = 'Content Notifications'; 17 | 18 | private static $url_segment = 'content-notifications'; 19 | } 20 | -------------------------------------------------------------------------------- /.upgrade.yml: -------------------------------------------------------------------------------- 1 | mappings: 2 | ContentNotifier: SilverStripe\ContentNotifier\ContentNotifier 3 | ContentNotifierAdmin: SilverStripe\ContentNotifier\Admin\ContentNotifierAdmin 4 | ContentNotifierExtension: SilverStripe\ContentNotifier\Extensions\ContentNotifierExtension 5 | ContentNotifierEmail: SilverStripe\ContentNotifier\Model\ContentNotifierEmail 6 | ContentNotifierQueue: SilverStripe\ContentNotifier\Model\ContentNotifierQueue 7 | ContentNotifierCleanTask: SilverStripe\ContentNotifier\Tasks\ContentNotifierCleanTask 8 | ContentNotifierTask: SilverStripe\ContentNotifier\Tasks\ContentNotifierTask 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in 2 | # this file, please see the EditorConfig documentation: 3 | # http://editorconfig.org/ 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.yml] 17 | indent_size = 2 18 | 19 | [{.travis.yml,package.json}] 20 | # The indent size used in the `package.json` file cannot be changed 21 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /templates/Includes/ContentNotifierEmailItem.ss: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | $ContentNotifierHeadline 5 | 6 |

7 |

8 | $ContentNotifierExcerpt 9 |

10 | 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"silverstripe/content-notifier", 3 | "type": "silverstripe-module", 4 | "description": "Notifies admins of new user-generated content", 5 | "keywords": ["silverstripe"], 6 | "license": "BSD-3-Clause", 7 | "homepage": "https://github.com/silverstripe/silverstripe-content-notifier/", 8 | "require": { 9 | "silverstripe/framework": "^4.0@dev" 10 | }, 11 | "autoload": { 12 | "psr-4": { 13 | "SilverStripe\\ContentNotifier\\": "src/" 14 | } 15 | }, 16 | "extra": { 17 | "branch-alias": { 18 | "dev-master": "2.0.x-dev" 19 | } 20 | }, 21 | "authors": [ 22 | { 23 | "name": "Uncle Cheese", 24 | "homepage": "http://leftandmain.com", 25 | "email" : "unclecheese@leftandmain.com" 26 | } 27 | ], 28 | "minimum-stability": "dev", 29 | "prefer-stable": true 30 | } 31 | -------------------------------------------------------------------------------- /src/Tasks/ContentNotifierTask.php: -------------------------------------------------------------------------------- 1 | exists()) { 22 | $count = $queue->count(); 23 | $e = Injector::inst()->create(ContentNotifierEmail::class) 24 | ->setRecords($queue) 25 | ->send(); 26 | 27 | echo "Sent $count notifications\n"; 28 | } else { 29 | echo "No queued notifications\n"; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, SilverStripe Limited 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /src/Model/ContentNotifierEmail.php: -------------------------------------------------------------------------------- 1 | emailer = Email::create(); 31 | $config = $this->config(); 32 | $this->emailer->setFrom($config->from); 33 | $this->emailer->setTo($config->to); 34 | $this->emailer->setSubject($config->subject); 35 | $this->emailer->setHTMLTemplate($config->template); 36 | } 37 | /** 38 | * @param DataList $list 39 | * @return $this 40 | */ 41 | public function setRecords(DataList $list) 42 | { 43 | $this->records = $list; 44 | return $this; 45 | } 46 | public function send() 47 | { 48 | if (!$this->records) { 49 | $this->setRecords(ContentNotifierQueue::get_unnotified()); 50 | } 51 | ContentNotifierExtension::disable_filtering(); 52 | $total = $this->records->count(); 53 | $grouped = GroupedList::create($this->records->limit($this->config()->items_limit))->GroupedBy('Category'); 54 | $this->emailer->setData(array('Headline' => $this->config()->headline, 'GroupedItems' => $grouped, 'Total' => $total, 'Link' => Controller::join_links(Director::absoluteBaseURL(), 'admin', 'content-notifications'))); 55 | $this->emailer->send(); 56 | foreach ($this->records as $record) { 57 | $record->HasNotified = true; 58 | $record->write(); 59 | } 60 | ContentNotifierExtension::enable_filtering(true); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Tasks/ContentNotifierCleanTask.php: -------------------------------------------------------------------------------- 1 | getVar('action'); 19 | $count = 0; 20 | switch ($action) { 21 | case "all": 22 | $count = ContentNotifierQueue::get()->count(); 23 | ContentNotifierQueue::get()->removeAll(); 24 | break; 25 | 26 | case "approved": 27 | foreach (ContentNotifierQueue::get() as $q) { 28 | if ($rec = $q->getRecord()) { 29 | if ($rec->ContentNotifierApproved) { 30 | $q->delete(); 31 | $count++; 32 | } 33 | } 34 | } 35 | break; 36 | 37 | case "denied": 38 | foreach (ContentNotifierQueue::get() as $q) { 39 | if ($rec = $q->getRecord()) { 40 | if (!$rec->ContentNotifierApproved) { 41 | $q->delete(); 42 | $count++; 43 | } 44 | } 45 | } 46 | break; 47 | 48 | case "orphaned": 49 | foreach (ContentNotifierQueue::get() as $q) { 50 | if (!$q->getRecord()) { 51 | $q->delete(); 52 | $count++; 53 | } 54 | } 55 | break; 56 | 57 | default: 58 | die("Please specify an 'action' parameter ('all','approved','denied', or 'orphaned') in the request"); 59 | break; 60 | } 61 | 62 | die("Deleted $count records from the content notifier queue."); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Model/ContentNotifierQueue.php: -------------------------------------------------------------------------------- 1 | 'Varchar', 20 | 'RecordID' => 'Int', 21 | 'Event' => "Enum('CREATED,UPDATED')", 22 | 'HasNotified' => 'Boolean' 23 | ); 24 | 25 | private static $summary_fields = array( 26 | 'Created' => 'Created.Nice', 27 | 'Event' => 'Event', 28 | 'RecordClass' => 'Content type', 29 | 'Status' => 'Status' 30 | ); 31 | 32 | private static $better_buttons_actions = array( 33 | 'approve', 34 | 'deny' 35 | ); 36 | 37 | private static $searchable_fields = array(); 38 | 39 | private static $default_sort = "Created DESC"; 40 | 41 | private static $table_name = 'ContentNotifierQueue'; 42 | 43 | public static function get_unnotified() 44 | { 45 | return self::get()->filter(array( 46 | 'HasNotified' => false 47 | )); 48 | } 49 | 50 | /** 51 | * @return FieldList 52 | */ 53 | public function getCMSFields() 54 | { 55 | if (!$this->getRecord()) { 56 | return FieldList::create(); 57 | } 58 | 59 | $fields = $this->getRecord()->getCMSFields(); 60 | $fields->unshift( 61 | new LiteralField("stat", "

Status: " . $this->getRecord()->getStatus()."

") 62 | ); 63 | 64 | // Create a dummy form so we can get access to loadDataFrom(). :-( 65 | return Form::create(Controller::curr(), "dummy", $fields, FieldList::create()) 66 | ->loadDataFrom($this->getRecord()) 67 | ->Fields() 68 | ->makeReadonly(); 69 | } 70 | 71 | public function Category() 72 | { 73 | return Injector::inst()->get($this->RecordClass)->plural_name(); 74 | } 75 | 76 | public function getRecord() 77 | { 78 | $class = Injector::inst()->get($this->RecordClass); 79 | return DataList::create(get_class($class))->byID($this->RecordID); 80 | } 81 | 82 | public function getTitle() 83 | { 84 | if ($this->getRecord()) { 85 | return "[{$this->RecordClass}] " . $this->getRecord()->getTitle(); 86 | } 87 | } 88 | 89 | public function getBetterButtonsActions() 90 | { 91 | $fields = parent::getBetterButtonsActions(); 92 | if (!$this->getRecord()) { 93 | return $fields; 94 | } 95 | 96 | if ($this->getRecord()->ContentNotifierApproved) { 97 | $fields->push( 98 | BetterButtonCustomAction::create('deny', 'Deny') 99 | ->setRedirectType(BetterButtonCustomAction::REFRESH) 100 | ); 101 | } else { 102 | $fields->push( 103 | BetterButtonCustomAction::create('approve', 'Approve') 104 | ->setRedirectType(BetterButtonCustomAction::REFRESH) 105 | ); 106 | } 107 | 108 | $fields->push( 109 | new BetterButtonLink( 110 | 'Edit this ' . $this->RecordClass, 111 | $this->getRecord()->getContentNotifierLink() 112 | ) 113 | ); 114 | 115 | return $fields; 116 | } 117 | 118 | public function getStatus() 119 | { 120 | if ($this->getRecord()) { 121 | return $this->getRecord()->getStatus(); 122 | } 123 | } 124 | 125 | public function approve() 126 | { 127 | if ($this->getRecord()) { 128 | $this->getRecord()->approve(); 129 | 130 | return 'Approved for publication'; 131 | } 132 | } 133 | 134 | public function deny() 135 | { 136 | if ($this->getRecord()) { 137 | $this->getRecord()->deny(); 138 | 139 | return 'Denied for publication'; 140 | } 141 | } 142 | 143 | public function canEdit($member = null) 144 | { 145 | return Permission::check("CMS_ACCESS_ContentNotifierAdmin"); 146 | } 147 | 148 | public function canView($member = null) 149 | { 150 | return Permission::check("CMS_ACCESS_ContentNotifierAdmin"); 151 | } 152 | 153 | public function canDelete($member = null) 154 | { 155 | return Permission::check("CMS_ACCESS_ContentNotifierAdmin"); 156 | } 157 | 158 | public function canCreate($member = null, $context = []) 159 | { 160 | return false; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SilverStripe Content Notifier Module 2 | 3 | ## Maintainer 4 | Aaron Carlino (aaron at silverstripe dot com) 5 | 6 | ## Description 7 | 8 | The Content Notifier module is used to allow moderation of user-generated content on websites running SilverStripe. When content is created or updated, an admin is notified and can approve the change. How strict the approval process is and who gets notified and how often is all configurable. 9 | 10 | ## Usage 11 | 12 | * Install with composer: `composer require silverstripe/content-notifier` 13 | * Build and flush: `/dev/build?flush=1` 14 | * Add the extension `ContentNotifierExtension` to any DataObjects you want to use the notifier. The DataObject class receiving the extension **must implement the `SilverStripe\ContentNotifier\ContentNotifier` interface**. 15 | 16 | ### Requirements 17 | As defined in the `ContentNotfifer` interface, you must add the following methods to your DataObject: 18 | 19 | * `getContentNotifierExcerpt()`: The summary of content that will be displayed in the notification email. 20 | * `getContentNotifierLink()`: The link to edit this record in the CMS. 21 | * `getContentNotifierHeadline()`: The title of the DataObject that will appear in the notification email 22 | 23 | #### Example 24 | ```php 25 | 'Text', 34 | 'Author' => 'Varchar' 35 | ); 36 | 37 | public function getContentNotifierExcerpt() 38 | { 39 | return $this->obj('Comment')->LimitWordCount(50); 40 | } 41 | 42 | public function getContentNotifierLink() 43 | { 44 | return "/admin/comments/edit/{$this->ID}"; 45 | } 46 | 47 | public function getContentNotifierHeadLine() 48 | { 49 | return "/admin/comments/Comment/EditForm/field/Comment/item/{$this->ID}/edit"; 50 | } 51 | } 52 | ``` 53 | 54 | ### Configuration 55 | In your `_config/` directory, first define some global settings: 56 | 57 | ```yaml 58 | SilverStripe\ContentNotifier\Model\ContentNotifierEmail: 59 | to: "me@example.com" 60 | from: notifications@example.com 61 | subject: Content has been updated on your site 62 | headline: New Content 63 | ``` 64 | 65 | Then, you can specify behaviours for each individual implementor of the Content Notifier extension. 66 | 67 | ```yaml 68 | Comment: 69 | ContentNotifier: 70 | batch_email: true 71 | delete_on_resolve: true 72 | auto_approve: UPDATED 73 | JobListing: 74 | ContentNotifier: 75 | batch_email: true 76 | delete_on_resolve: true 77 | auto_approve: '*' 78 | ``` 79 | 80 | The following settings are available: 81 | 82 | * **batch_email:** Don't notify of changes immediately. Use the `ContentNotifierTask` to deliver a batch email of updates. (Requires setting up a cron job) 83 | * **delete_on_resolve:** Once content is approved or denied, delete the `ContentNotifierQueue` record. (Recommended for tidiness) 84 | * **auto_approve:** Depending on the event, the content can auto-approve. The moderator of the content will still receive an email detailing the update, but the content is optimistically approved in advance rather than waiting for manual approval. Possible values: `*` (always approve), `UPDATED` (auto-approve updates), `CREATED` (auto-approve creates). 85 | 86 | ### ContentNotifierQueue 87 | 88 | This is a polymorphic object that just displays readonly fields of the record that it points to for the admin to review. Approvals and Denials can be executed when looking at the queue record. These records are intended to be disposable, and there should never be a large population of them. 89 | 90 | ### ContentNotifierTask 91 | 92 | Run `/dev/tasks/ContentNotifierTask` to send out the batch email of all the content that needs approval. 93 | 94 | ### ContentNotifierCleanTask 95 | 96 | Run `/dev/tasks/ContentNotifierCleanTask` to bulk-delete any ContentNotifierQueue records that are no longer needed. You must include an ```action``` parameter in the request that contains one of the following values: 97 | 98 | * `all` (delete all queue records) 99 | * `approved` (delete all queue records that have been approved) 100 | * `denied` (delete all queue records that have been denied) 101 | * `orphaned` (delete all queue records that no longer point to an existing record) 102 | 103 | 104 | ## Turning off the filter 105 | 106 | By default, anything that uses the ContentNotifierExtension will be hidden from a result set unless the user is an admin (see the `admin_permission` setting to customise). Occasionally, you may want to allow the record to be seen, for instance, when a user is editing his unapproved content. In that case, you can invoke `ContentNotifierExtension::disable_filtering()`. 107 | 108 | 109 | ## How is this different from the Advanced Workflow module? 110 | 111 | Most importantly, it doesn't contain nearly as many features, but more to the point, this module is intended to be used with user-generated content, or content that comes in via a thirdparty service, such as Meetup events. It provides a very simple way to ensure that your website isn't polluted with content that you don't necessarily want showing. 112 | -------------------------------------------------------------------------------- /src/Extensions/ContentNotifierExtension.php: -------------------------------------------------------------------------------- 1 | 'Boolean' 25 | ); 26 | 27 | private static $better_buttons_actions = array( 28 | 'approve', 29 | 'deny' 30 | ); 31 | 32 | protected static $filter_unapproved = true; 33 | 34 | public static function get_extra_config($class, $extension, $args) 35 | { 36 | if (!ClassInfo::classImplements($class, ContentNotifier::class)) { 37 | throw new RuntimeException("$class must implement ContentNotifier to be used by the ContentNotifierExtension"); 38 | } 39 | } 40 | 41 | 42 | public static function enable_filtering() 43 | { 44 | self::$filter_unapproved = true; 45 | } 46 | 47 | 48 | public static function disable_filtering() 49 | { 50 | self::$filter_unapproved = false; 51 | } 52 | 53 | 54 | public function updateBetterButtonsActions($actions) 55 | { 56 | if ($this->owner->ContentNotifierApproved) { 57 | $actions->push(new BetterButtonCustomAction( 58 | 'deny', 59 | 'Deny' 60 | )); 61 | } else { 62 | $actions->push(new BetterButtonCustomAction( 63 | 'approve', 64 | 'Approve' 65 | )); 66 | } 67 | } 68 | 69 | protected function resolve($approved) 70 | { 71 | $this->owner->ContentNotifierApproved = $approved; 72 | $this->owner->write(); 73 | 74 | if ($this->getSetting('delete_on_resolve')) { 75 | if ($object = $this->getQueue()) { 76 | $object->delete(); 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Returns the ContentNotifier setting (note: not fully qualified) 83 | * 84 | * @param string $setting 85 | * @return string|false 86 | */ 87 | protected function getSetting($setting) 88 | { 89 | $config = Config::inst()->get(get_class($this->owner), ContentNotifier::class); 90 | 91 | return isset($config[$setting]) ? $config[$setting] : false; 92 | } 93 | 94 | protected function shouldAutoApprove($type) 95 | { 96 | $autoApprove = $this->getSetting('auto_approve'); 97 | if ($autoApprove) { 98 | return ($autoApprove == "*") || (strtolower($autoApprove) == strtolower($type)); 99 | } 100 | 101 | return false; 102 | } 103 | 104 | public function approve() 105 | { 106 | $this->resolve(true); 107 | } 108 | 109 | public function deny() 110 | { 111 | $this->resolve(false); 112 | } 113 | 114 | public function EmailSummary() 115 | { 116 | $template = $this->getSetting('email_notifier_template') ?: Config::inst() 117 | ->get(ContentNotifier::class, 'item_template'); 118 | 119 | return $this->owner->renderWith($template); 120 | } 121 | 122 | public function getStatus() 123 | { 124 | return $this->owner->ContentNotifierApproved 125 | ? _t('ContentNotifier.APPROVED', 'APPROVED') 126 | : _t('ContentNotifier.UNAPPROVED', 'UNAPPROVED'); 127 | } 128 | 129 | public function onBeforeWrite() 130 | { 131 | // Prevent CMS actions or updates being overridden 132 | if ($this->checkPermission()) { 133 | $this->owner->ContentNotifierApproved = true; 134 | } 135 | 136 | // If creating a dataobject for the first time, auto-approve if allowed 137 | if (!$this->owner->isInDB()) { 138 | $this->owner->isCreating = true; 139 | 140 | // New records can approve themselves 141 | if ($this->shouldAutoApprove('CREATED')) { 142 | $this->owner->ContentNotifierApproved = true; 143 | } 144 | 145 | return; 146 | } 147 | 148 | // If editing a record, allow auto unapproval 149 | if (!$this->owner->isChanged('ContentNotifierApproved')) { 150 | // Adjust approvel only if not changed explicitly 151 | $this->owner->ContentNotifierApproved = $this->shouldAutoApprove('UPDATED'); 152 | } 153 | } 154 | 155 | public function onAfterWrite() 156 | { 157 | // Trigger events after approval state changes. 158 | if ($this->owner->isChanged('ContentNotifierApproved', 2)) { 159 | if ($this->owner->ContentNotifierApproved) { 160 | $this->owner->invokeWithExtensions('onAfterContentNotifierApprove'); 161 | } else { 162 | $this->owner->invokeWithExtensions('onAfterContentNotifierUnapprove'); 163 | } 164 | } 165 | 166 | // Note: this has an effect that privileged user's showcase submissions will not show up in the queue. 167 | if ($this->checkPermission()) { 168 | return; 169 | } 170 | 171 | if ($this->owner->isCreating) { 172 | $this->createQueue('CREATED'); 173 | } elseif ($this->owner->isChanged()) { 174 | // Clear any existing entry 175 | if ($queue = $this->getQueue('UPDATED')) { 176 | $queue->delete(); 177 | } 178 | $this->createQueue('UPDATED'); 179 | } 180 | 181 | if (!$this->getSetting('batch_email')) { 182 | $email = ContentNotifierEmail::create(); 183 | $email->setRecords(ContentNotifierQueue::get_unnotified()); 184 | $email->send(); 185 | } 186 | } 187 | 188 | public function onAfterDelete() 189 | { 190 | ContentNotifierQueue::get()->filter(array( 191 | 'RecordClass' => get_class($this->owner), 192 | 'RecordID' => $this->owner->ID ?: 0 193 | ))->removeAll(); 194 | } 195 | 196 | public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) 197 | { 198 | if (!$this->checkPermission() && self::$filter_unapproved) { 199 | $query->addWhere("ContentNotifierApproved = 1"); 200 | } 201 | } 202 | 203 | protected function createQueue($event) 204 | { 205 | return ContentNotifierQueue::create(array( 206 | 'RecordClass' => get_class($this->owner), 207 | 'Event' => $event, 208 | 'RecordID' => $this->owner->ID 209 | ))->write(); 210 | } 211 | 212 | public function getQueue($event = null) 213 | { 214 | $list = ContentNotifierQueue::get()->filter(array( 215 | 'RecordClass' => get_class($this->owner), 216 | 'RecordID' => $this->owner->ID ?: 0 217 | )); 218 | 219 | if ($event) { 220 | $list = $list->filter('Event', $event); 221 | } 222 | return $list->first(); 223 | } 224 | 225 | protected function checkPermission() 226 | { 227 | if (Director::is_cli()) { 228 | return false; 229 | } 230 | 231 | $perm = Config::inst()->get(__CLASS__, 'admin_permission'); 232 | $cms = is_subclass_of(get_class(Controller::curr()), LeftAndMain::class); 233 | 234 | return Permission::check($perm) || $cms; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /templates/Includes/ContentNotifierEmail.ss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Content Notifier Email 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 40 | 42 | 43 |
16 | 18 | 19 | 20 |
21 | 22 | 23 | 28 | 33 | 34 | 35 |
24 |

25 | $Headline 26 |

27 |
29 |
30 | $Total items 31 |
32 |
36 |
37 | 38 | 39 |
41 |
44 | 45 | 46 | 47 | 48 | 50 | 86 | 88 | 89 |
49 | 51 | 52 | 53 | 54 | <% loop $GroupedItems %> 55 |
56 |

57 | $Category 58 |

59 | 60 | 61 | <% loop $Children %> 62 | 63 | 76 | $Record.EmailSummary 77 | 78 | <% end_loop %> 79 |
64 | 65 | <% if $Event == 'CREATED' %> 66 | 67 | CREATED 68 | 69 | <% else_if $Event == 'UPDATED' %> 70 | 71 | UPDATED 72 | 73 | <% end_if %> 74 | 75 |
80 |
81 | <% end_loop %> 82 | 83 | 84 | 85 |
87 |
90 | 91 | 92 | 93 | 94 | 96 | 119 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /templates/Includes/raw/ContentNotifierEmail_RAW.ss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Content Notifier Email 9 | 10 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 245 | 246 | 247 |
233 | 234 | 235 |
236 | 237 | 238 | 239 | 240 | 241 |
$Headline
$Total items
242 |
243 | 244 |
248 | 249 | 250 | 251 | 252 | 253 | 281 | 282 | 283 |
254 | 255 | 256 | 257 | <% loop $GroupedItems %> 258 |
259 |

$Category

260 | 261 | <% loop $Children %> 262 | 263 | 270 | 273 | 274 | <% end_loop %> 275 |
264 | <% if $Event == 'UPDATED' %> 265 | UPDATED 266 | <% else_if $Event == 'CREATED' %> 267 | CREATED 268 | <% end_if %> 269 | 271 | $Record.EmailSummary 272 |
276 | 277 |
278 | <% end_loop %> 279 | 280 |
284 | 285 | 286 | 287 | 288 | 289 | 305 | 306 | 307 | 308 | 309 | 310 | --------------------------------------------------------------------------------