mirror of
https://github.com/silverstripe/silverstripe-blog
synced 2024-10-22 11:05:58 +02:00
Merge pull request #159 from tractorcow/pulls/2.0/editor-management
API New permissions for writers, editors, and contributors
This commit is contained in:
commit
c5d32f7fb2
65
README.md
65
README.md
@ -5,65 +5,8 @@ Silverstripe Blogger
|
||||
|
||||
A fresh take on blogging in Silverstripe set out to tackle the issue of a cluttered Site Tree.
|
||||
|
||||
## Documentation index
|
||||
|
||||
## Requirements
|
||||
```
|
||||
silverstripe/cms: ~3.1
|
||||
```
|
||||
|
||||
## Suggested Modules
|
||||
```
|
||||
silverstripe/widgets: *
|
||||
silverstripe/comments: *
|
||||
```
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require micmania1/silverstripe-blog:1.0.*
|
||||
```
|
||||
|
||||
|
||||
## Features
|
||||
* Blog Posts extend SiteTree so no Page functionality is lost by using DataObject's.
|
||||
* Blog Posts can be listed in the SiteTree **OR** in a GridField within the blog, removing clutter for large blogs.
|
||||
* Filter blog posts by category
|
||||
* Filter blog posts by tag
|
||||
* Full blog Archive by Year, month or day
|
||||
* 4 widgets included (category listing, tag listing, archive & recent posts).
|
||||
* Publish Date - Set a blog post to publish in the future.
|
||||
* Custom GridField components for quickly adding new objects.
|
||||
* RSS Feed of the blog
|
||||
|
||||
|
||||
## Screenshots
|
||||
*Easily manage and add new blog posts through a GridField.*
|
||||
![](screenshots/blog-post-management.png)
|
||||
|
||||
*Quickly add new tags and categories.*
|
||||
![](screenshots/blogpost-add-tags-categories.png)
|
||||
|
||||
|
||||
## Usage
|
||||
Because the blog is part of the SiteTree the usage is the same as any other page.
|
||||
|
||||
By default, blog posts are filtered out of the SiteTree to avoid clutter and instead put in a GridField inside of the blog. If you wish to display the blog posts within the site tree you can do so using Silverstripe config.
|
||||
|
||||
**YAML**
|
||||
```
|
||||
BlogPost:
|
||||
show_in_sitetree: true
|
||||
```
|
||||
|
||||
**PHP**
|
||||
```
|
||||
<?php
|
||||
Config::inst()->update("BlogPost", "show_in_sitetree", true);
|
||||
```
|
||||
|
||||
Doing this will remove the GridField & result in a normal behaving SiteTree.
|
||||
|
||||
|
||||
## Theme
|
||||
You can download a blog sub-theme of Silverstripe's default 'Simple' theme: https://github.com/micmania1/simple_blog
|
||||
* [Features](docs/en/features.md)
|
||||
* [Setup](docs/en/setup.md)
|
||||
* [Roles](docs/en/roles.md)
|
||||
|
@ -29,7 +29,7 @@ class BlogFilter extends Lumberjack {
|
||||
// Get the current stage.
|
||||
$stage = Versioned::current_stage();
|
||||
if($stage == "Stage") $stage = "";
|
||||
else $stage = "_" . Convert::raw2sql($stage);
|
||||
elseif($stage) $stage = "_{$stage}";
|
||||
|
||||
// Filter published posts
|
||||
$dataQuery = $staged->dataQuery()
|
||||
@ -40,28 +40,50 @@ class BlogFilter extends Lumberjack {
|
||||
return $staged;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Augments (@link Hierarchy::liveChildren()}
|
||||
*
|
||||
* @param $staged DataList
|
||||
* @param $showAll boolean
|
||||
**/
|
||||
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
|
||||
$staged = parent::liveChildren($showAll, $onlyDeletedFromStage);
|
||||
|
||||
if(!$this->shouldFilter()
|
||||
&& in_array(get_class($this->owner), ClassInfo::subClassesFor("Blog"))
|
||||
&& $this->owner instanceof Blog
|
||||
&& !Permission::check("VIEW_DRAFT_CONTENT")
|
||||
) {
|
||||
// Filter publish posts
|
||||
$dataQuery = $staged->dataQuery()
|
||||
->innerJoin("BlogPost", '"BlogPost_Live"."ID" = "SiteTree"_"Live"."ID"')
|
||||
->where('"PublishDate" < \'' . Convert::raw2sql(SS_Datetime::now()) . '\'');
|
||||
->innerJoin("BlogPost", '"BlogPost_Live"."ID" = "SiteTree_Live"."ID"')
|
||||
->where('"BlogPost"."PublishDate" < \'' . Convert::raw2sql(SS_Datetime::now()->getValue()) . '\'');
|
||||
$staged = $staged->setDataQuery($dataQuery);
|
||||
}
|
||||
return $staged;
|
||||
}
|
||||
|
||||
}
|
||||
public function updateCMSFields(FieldList $fields) {
|
||||
$excluded = $this->owner->getExcludedSiteTreeClassNames();
|
||||
if(!empty($excluded)) {
|
||||
$pages = SiteTree::get()->filter(array(
|
||||
'ParentID' => $this->owner->ID,
|
||||
'ClassName' => $excluded
|
||||
));
|
||||
$gridField = new BlogFilter_GridField(
|
||||
"ChildPages",
|
||||
$this->getLumberjackTitle(),
|
||||
$pages,
|
||||
$this->getLumberjackGridFieldConfig()
|
||||
);
|
||||
|
||||
$tab = new Tab('ChildPages', $this->getLumberjackTitle(), $gridField);
|
||||
$fields->insertAfter($tab, 'Main');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables children of non-editable pages to be edited
|
||||
*/
|
||||
class BlogFilter_GridField extends GridField {
|
||||
public function transform(FormTransformation $trans) {
|
||||
// Don't allow parent object transformations to propegate automatically to child records
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -5,39 +5,79 @@
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*
|
||||
* @method HasManyList Tags() List of tags in this blog
|
||||
* @method HasManyList Categories() List of categories in this blog
|
||||
* @method ManyManyList Editors() List of editors
|
||||
* @method ManyManyList Writers() List of writers
|
||||
* @method ManyManyList Contributors() List of contributors
|
||||
*
|
||||
* @author Michael Strong <github@michaelstrong.co.uk>
|
||||
**/
|
||||
class Blog extends Page {
|
||||
class Blog extends Page implements PermissionProvider {
|
||||
|
||||
/**
|
||||
* Permission for user management
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MANAGE_USERS = 'BLOG_MANAGE_USERS';
|
||||
|
||||
/**
|
||||
* If true, users assigned as editor, writer, or contributor will be automatically
|
||||
* granted CMS_ACCESS_CMSMain permission. If false, only users with this permission
|
||||
* already may be assigned.
|
||||
*
|
||||
* @var boolean
|
||||
* @config
|
||||
*/
|
||||
private static $grant_user_access = true;
|
||||
|
||||
/**
|
||||
* Permission to either require, or grant to users assigned to work on this blog
|
||||
*
|
||||
* @var string
|
||||
* @config
|
||||
*/
|
||||
private static $grant_user_permission = 'CMS_ACCESS_CMSMain';
|
||||
|
||||
/**
|
||||
* Group code to assign newly granted users toh
|
||||
*
|
||||
* @var string
|
||||
* @config
|
||||
*/
|
||||
private static $grant_user_group = 'blog-users';
|
||||
|
||||
private static $db = array(
|
||||
"PostsPerPage" => "Int",
|
||||
);
|
||||
|
||||
|
||||
private static $has_many = array(
|
||||
"Tags" => "BlogTag",
|
||||
"Categories" => "BlogCategory",
|
||||
);
|
||||
|
||||
private static $many_many = array(
|
||||
'Editors' => 'Member',
|
||||
'Writers' => 'Member',
|
||||
'Contributors' => 'Member',
|
||||
);
|
||||
|
||||
private static $allowed_children = array(
|
||||
"BlogPost",
|
||||
);
|
||||
|
||||
|
||||
private static $extensions = array(
|
||||
"BlogFilter",
|
||||
);
|
||||
|
||||
|
||||
private static $defaults = array(
|
||||
"ProvideComments" => false,
|
||||
);
|
||||
|
||||
private static $description = 'Adds a blog to your website.';
|
||||
|
||||
|
||||
public function getCMSFields() {
|
||||
$self =& $this;
|
||||
$this->beforeUpdateCMSFields(function($fields) use ($self) {
|
||||
@ -65,13 +105,144 @@ class Blog extends Page {
|
||||
$categories,
|
||||
$tags
|
||||
));
|
||||
|
||||
});
|
||||
|
||||
$fields = parent::getCMSFields();
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this member is an editor of the blog
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEditor($member) {
|
||||
$isEditor = $this->isMemberOf($member, $this->Editors());
|
||||
$this->extend('updateIsEditor', $isEditor, $member);
|
||||
return $isEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this member is a writer of the blog
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWriter($member) {
|
||||
$isWriter = $this->isMemberOf($member, $this->Writers());
|
||||
$this->extend('updateIsWriter', $isWriter, $member);
|
||||
return $isWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this member is a contributor of the blog
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function isContributor($member) {
|
||||
$isContributor = $this->isMemberOf($member, $this->Contributors());
|
||||
$this->extend('updateIsContributor', $isContributor, $member);
|
||||
return $isContributor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given member belongs to the given subrelation
|
||||
*
|
||||
* @param Member $member
|
||||
* @param DataList $list Relation to check
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isMemberOf($member, $list) {
|
||||
if(!$member || !$member->exists()) return false;
|
||||
|
||||
if($list instanceof UnsavedRelationList) {
|
||||
return in_array($member->ID, $list->getIDList());
|
||||
}
|
||||
|
||||
return $list->byID($member->ID) !== null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function canEdit($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
// Allow editors to edit this page
|
||||
if($this->isEditor($member)) return true;
|
||||
|
||||
return parent::canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this user can edit the editors list
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEditEditors($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
|
||||
$extended = $this->extendedCan('canEditEditors', $member);
|
||||
if($extended !== null) return $extended;
|
||||
|
||||
// Check permission
|
||||
return Permission::checkMember($member, self::MANAGE_USERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this user can edit writers list
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEditWriters($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
$extended = $this->extendedCan('canEditWriters', $member);
|
||||
if($extended !== null) return $extended;
|
||||
|
||||
// Editors can edit writers
|
||||
if($this->isEditor($member)) return true;
|
||||
|
||||
// Check permission
|
||||
return Permission::checkMember($member, self::MANAGE_USERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this user can edit the contributors list
|
||||
*
|
||||
* @param type $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEditContributors($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
$extended = $this->extendedCan('canEditContributors', $member);
|
||||
if($extended !== null) return $extended;
|
||||
|
||||
// Editors can edit writers
|
||||
if($this->isEditor($member)) return true;
|
||||
|
||||
// Check permission
|
||||
return Permission::checkMember($member, self::MANAGE_USERS);
|
||||
}
|
||||
|
||||
public function canAddChildren($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
// Editors, Writers and Contributors can add children
|
||||
if($this->isEditor($member) || $this->isWriter($member) || $this->isContributor($member)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::canAddChildren($member);
|
||||
}
|
||||
|
||||
|
||||
public function getSettingsFields() {
|
||||
@ -79,11 +250,44 @@ class Blog extends Page {
|
||||
$fields->addFieldToTab("Root.Settings",
|
||||
NumericField::create("PostsPerPage", _t("Blog.PostsPerPage", "Posts Per Page"))
|
||||
);
|
||||
|
||||
// Roles and Permissions
|
||||
$members = $this->getCandidateUsers()->map()->toArray();
|
||||
|
||||
// Editors
|
||||
$editorField = ListboxField::create('Editors', 'Editors', $members)
|
||||
->setMultiple(true)
|
||||
->setDescription('Editors are able to manage this blog and its posts');
|
||||
if(!$this->canEditEditors()) {
|
||||
$editorField = $editorField->performDisabledTransformation();
|
||||
}
|
||||
|
||||
// Writers
|
||||
$writerField = ListboxField::create('Writers', 'Writers', $members)
|
||||
->setMultiple(true)
|
||||
->setDescription('Writers are able to write and publish posts');
|
||||
if(!$this->canEditWriters()) {
|
||||
$writerField = $writerField->performDisabledTransformation();
|
||||
}
|
||||
|
||||
// Contributors
|
||||
$contributorField = ListboxField::create('Contributors', 'Contributors', $members)
|
||||
->setMultiple(true)
|
||||
->setDescription('Contributors are able to write, but not publish, posts');
|
||||
if(!$this->canEditContributors()) {
|
||||
$contributorField = $contributorField->performDisabledTransformation();
|
||||
}
|
||||
|
||||
$fields->addFieldsToTab('Root.Users', array(
|
||||
$editorField,
|
||||
$writerField,
|
||||
$contributorField
|
||||
));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return blog posts
|
||||
*
|
||||
@ -103,7 +307,7 @@ class Blog extends Page {
|
||||
*
|
||||
* @param $year int
|
||||
* @param $month int
|
||||
* @param $dat int
|
||||
* @param $day int
|
||||
*
|
||||
* @return DataList
|
||||
**/
|
||||
@ -147,6 +351,86 @@ class Blog extends Page {
|
||||
return GridFieldConfig_BlogPost::create();
|
||||
}
|
||||
|
||||
public function providePermissions() {
|
||||
return array(
|
||||
Blog::MANAGE_USERS => array(
|
||||
'name' => _t(
|
||||
'Blog.PERMISSION_MANAGE_USERS_DESCRIPTION',
|
||||
'Manage users for individual blogs'
|
||||
),
|
||||
'help' => _t(
|
||||
'Blog.PERMISSION_MANAGE_USERS_HELP',
|
||||
'Allow assignment of Editors, Writers, or Contributors to blogs'
|
||||
),
|
||||
'category' => _t('Blog.PERMISSIONS_CATEGORY', 'Blog permissions'),
|
||||
'sort' => 100
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of user candidates to be assigned to assist with this blog
|
||||
*
|
||||
* @return SS_List
|
||||
*/
|
||||
protected function getCandidateUsers() {
|
||||
if($this->config()->grant_user_access) {
|
||||
// If we are allowed to grant CMS access, all users are candidates
|
||||
return Member::get();
|
||||
} else {
|
||||
// If access cannot be granted, limit users to those who can access the CMS
|
||||
// This is useful for more secure sites
|
||||
$permission = $this->config()->grant_user_permission;
|
||||
return Permission::get_members_by_permission($permission);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or creates the group used to assign CMS access
|
||||
*
|
||||
* @return Group
|
||||
*/
|
||||
protected function getUserGroup() {
|
||||
$code = $this->config()->grant_user_group;
|
||||
$group = Group::get()->filter('Code', $code)->first();
|
||||
if($group) return $group;
|
||||
|
||||
// Create new group
|
||||
$group = new Group();
|
||||
$group->Title = 'Blog users';
|
||||
$group->Code = $code;
|
||||
$group->write();
|
||||
|
||||
// Add permission
|
||||
$permission = new Permission();
|
||||
$permission->Code = $this->config()->grant_user_permission;
|
||||
$group->Permissions()->add($permission);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
protected function onBeforeWrite() {
|
||||
parent::onBeforeWrite();
|
||||
$this->assignGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign users as necessary to the blog group
|
||||
*/
|
||||
protected function assignGroup() {
|
||||
// Ensure that any user granted editor, writer, or contributor have CMS_ACCESS_CMSMain access
|
||||
if(!$this->config()->grant_user_access) return;
|
||||
|
||||
// Generate or retrieve the group
|
||||
$group = $this->getUserGroup();
|
||||
foreach(array($this->Editors(), $this->Writers(), $this->Contributors()) as $userlist) {
|
||||
foreach($userlist as $user) {
|
||||
// Ensure user exists in the group
|
||||
if(!$user->inGroup($group)) $user->Groups()->add($group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,12 +6,17 @@
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*
|
||||
* @method ManyManyList Categories()
|
||||
* @method ManyManyList Tags()
|
||||
* @method ManyManyList Authors()
|
||||
*
|
||||
* @author Michael Strong <github@michaelstrong.co.uk>
|
||||
**/
|
||||
class BlogPost extends Page {
|
||||
|
||||
private static $db = array(
|
||||
"PublishDate" => "SS_Datetime",
|
||||
"AuthorNames" => "Varchar(1024)"
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
@ -21,6 +26,7 @@ class BlogPost extends Page {
|
||||
private static $many_many = array(
|
||||
"Categories" => "BlogCategory",
|
||||
"Tags" => "BlogTag",
|
||||
"Authors" => "Member",
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
@ -60,6 +66,23 @@ class BlogPost extends Page {
|
||||
private static $show_in_sitetree = false;
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the given member is an author of this post
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isAuthor($member) {
|
||||
if(!$member || !$member->exists()) return false;
|
||||
|
||||
$list = $this->Authors();
|
||||
if($list instanceof UnsavedRelationList) {
|
||||
return in_array($member->ID, $list->getIDList());
|
||||
}
|
||||
|
||||
return $list->byID($member->ID) !== null;
|
||||
}
|
||||
|
||||
|
||||
public function getCMSFields() {
|
||||
Requirements::css(BLOGGER_DIR . '/css/cms.css');
|
||||
@ -87,6 +110,24 @@ class BlogPost extends Page {
|
||||
'URLSegment',
|
||||
));
|
||||
|
||||
// Author field
|
||||
$authorField = ListboxField::create(
|
||||
"Authors",
|
||||
_t("BlogPost.Authors", "Authors"),
|
||||
Member::get()->map()->toArray()
|
||||
)->setMultiple(true);
|
||||
|
||||
$authorNames = TextField::create(
|
||||
"AuthorNames",
|
||||
_t("BlogPost.AdditionalCredits", "Additional Credits"),
|
||||
null,
|
||||
1024
|
||||
)->setDescription('Comma separated list of names');
|
||||
if(!$this->canEditAuthors()) {
|
||||
$authorField = $authorField->performDisabledTransformation();
|
||||
$authorNames = $authorNames->performDisabledTransformation();
|
||||
}
|
||||
|
||||
// Build up our sidebar
|
||||
$options = BlogAdminSidebar::create(
|
||||
$publishDate = DatetimeField::create("PublishDate", _t("BlogPost.PublishDate", "Publish Date")),
|
||||
@ -100,7 +141,9 @@ class BlogPost extends Page {
|
||||
"Tags",
|
||||
_t("BlogPost.Tags", "Tags"),
|
||||
$self->Parent()->Tags()->map()->toArray()
|
||||
)->setMultiple(true)
|
||||
)->setMultiple(true),
|
||||
$authorField,
|
||||
$authorNames
|
||||
)->setTitle('Post Options');
|
||||
$publishDate->getDateField()->setConfig("showcalendar", true);
|
||||
|
||||
@ -116,14 +159,17 @@ class BlogPost extends Page {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If no publish date is set, set the date to now.
|
||||
**/
|
||||
protected function onBeforeWrite() {
|
||||
parent::onBeforeWrite();
|
||||
if(!$this->PublishDate) $this->setCastedField("PublishDate", time());
|
||||
|
||||
// If no publish date is set, set the date to now.
|
||||
if(!$this->PublishDate) $this->PublishDate = SS_Datetime::now()->getValue();
|
||||
|
||||
// If creating a new entry, assign the current member as an author
|
||||
// This allows writers and contributors to then edit their new post
|
||||
if(!$this->exists() && ($member = Member::currentUser())) {
|
||||
$this->Authors()->add($member);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -133,7 +179,7 @@ class BlogPost extends Page {
|
||||
**/
|
||||
public function onBeforePublish() {
|
||||
if ($this->dbObject('PublishDate')->InPast() && !$this->isPublished()) {
|
||||
$this->setCastedField("PublishDate", time());
|
||||
$this->PublishDate = SS_Datetime::now()->getValue();
|
||||
$this->write();
|
||||
}
|
||||
}
|
||||
@ -159,6 +205,81 @@ class BlogPost extends Page {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canEdit($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
// Inherit permission
|
||||
if(parent::canEdit($member)) return true;
|
||||
|
||||
// Check if assigned to a blog
|
||||
$parent = $this->Parent();
|
||||
if(!$parent || !$parent->exists() || !($parent instanceof Blog)) return false;
|
||||
|
||||
// Editors have full control
|
||||
if($parent->isEditor($member)) return true;
|
||||
|
||||
// Only writers or contributors can edit
|
||||
if(!$parent->isWriter($member) && !$parent->isContributor($member)) return false;
|
||||
|
||||
// And only if they are also authors
|
||||
return $this->isAuthor($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this user can edit the authors list
|
||||
*
|
||||
* @param Member $member
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEditAuthors($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
$extended = $this->extendedCan('canEditAuthors', $member);
|
||||
if($extended !== null) return $extended;
|
||||
|
||||
// Check blog roles
|
||||
$parent = $this->Parent();
|
||||
if($parent instanceof Blog && $parent->exists()) {
|
||||
// Editors can do anything
|
||||
if($parent->isEditor($member)) return true;
|
||||
|
||||
// Writers who are also authors can edit authors
|
||||
if($parent->isWriter($member) && $this->isAuthor($member)) return true;
|
||||
}
|
||||
|
||||
// Check permission
|
||||
return Permission::checkMember($member, Blog::MANAGE_USERS);
|
||||
}
|
||||
|
||||
public function canPublish($member = null) {
|
||||
$member = $member ?: Member::currentUser();
|
||||
if(is_numeric($member)) $member = Member::get()->byID($member);
|
||||
|
||||
if(Permission::checkMember($member, "ADMIN")) return true;
|
||||
|
||||
// Standard mechanism for accepting permission changes from extensions
|
||||
$extended = $this->extendedCan('canPublish', $member);
|
||||
if($extended !== null) return $extended;
|
||||
|
||||
// Check blog roles
|
||||
$parent = $this->Parent();
|
||||
if($parent instanceof Blog && $parent->exists()) {
|
||||
// Editors can do anything
|
||||
if($parent->isEditor($member)) return true;
|
||||
|
||||
// Writers who are also authors can edit authors
|
||||
if($parent->isWriter($member) && $this->isAuthor($member)) return true;
|
||||
|
||||
// Contributors can ONLY publish this page if they somehow have global publish permissions
|
||||
// In this case defer to old canEdit implementation
|
||||
if($parent->isContributor($member)) return parent::canEdit($member);
|
||||
}
|
||||
|
||||
// Normal case - fail over to canEdit()
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -183,7 +304,6 @@ class BlogPost extends Page {
|
||||
**/
|
||||
public function getMonthlyArchiveLink($type = "day") {
|
||||
$date = $this->dbObject("PublishDate");
|
||||
$year = $date->format("Y");
|
||||
if($type != "year") {
|
||||
if($type == "day") {
|
||||
return Controller::join_links(
|
||||
|
0
docs/_manifest_exclude
Normal file
0
docs/_manifest_exclude
Normal file
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
20
docs/en/features.md
Normal file
20
docs/en/features.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Features
|
||||
|
||||
* Blog Posts extend SiteTree so no Page functionality is lost by using DataObject's.
|
||||
* Blog Posts can be listed in the SiteTree **OR** in a GridField within the blog, removing clutter for large blogs.
|
||||
* Filter blog posts by category
|
||||
* Filter blog posts by tag
|
||||
* Full blog Archive by Year, month or day
|
||||
* 4 widgets included (category listing, tag listing, archive & recent posts).
|
||||
* Publish Date - Set a blog post to publish in the future.
|
||||
* Custom GridField components for quickly adding new objects.
|
||||
* RSS Feed of the blog
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
*Easily manage and add new blog posts through a GridField.*
|
||||
![](_images/blog-post-management.png)
|
||||
|
||||
*Quickly add new tags and categories.*
|
||||
![](_images/blogpost-add-tags-categories.png)
|
43
docs/en/roles.md
Normal file
43
docs/en/roles.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Roles
|
||||
|
||||
Blog authoring and management can be delegated to users at up to three different levels. Users assigned to a blog
|
||||
using one of the below methods will be automatically assigned to the "Blog users" group and granted basic CMS access.
|
||||
|
||||
Note: If it's necessary to restrict access to non-blog pages to any of the below users, ensure that
|
||||
the "Who can edit pages on this site?" option under Settings is changed from "Anyone who can log-in to the CMS"
|
||||
to a specific selection of groups.
|
||||
|
||||
## Editor
|
||||
|
||||
An editor has control over specific Blogs, and their respective BlogPost subpages. Short of being able to assign
|
||||
other editors to a Blog, they are able to handle most changes to their assigned section.
|
||||
|
||||
Editors have these permissions:
|
||||
|
||||
* Update or Publish any BlogPost in their Blog
|
||||
* Update or Publish their Blog
|
||||
* Assign/Unassign writers to their Blog
|
||||
* Assign/Unassign contributors to their Blog
|
||||
* Assign/Unassign any member as an author of a particular BlogPost
|
||||
|
||||
## Writer
|
||||
|
||||
A writer is able to handle most tasks involving writing and publishing BlogPosts. While they
|
||||
are not able to edit a Blog directly, they are able to add new BlogPost entries within a Blog.
|
||||
They can't edit BlogPosts, however, that they are not been assigned to or have authored themselves.
|
||||
|
||||
Writers have these permissions:
|
||||
|
||||
* Update or Publish any BlogPost they have authored (or are assigned as author of)
|
||||
* Assign/Unassign any member as an author of a particular BlogPost they have authored
|
||||
(or are assigned as author of)
|
||||
|
||||
## Contributor
|
||||
|
||||
Contributors have the ability to create or edit BlogPosts, but are unable to publish without
|
||||
authorisation of an editor. They are also unable to assign other contributing authors to
|
||||
any of their BlogPosts.
|
||||
|
||||
Contributors have these permissions:
|
||||
|
||||
* Update or edit any BlogPost they have authored (or are assigned as author of)
|
36
docs/en/setup.md
Normal file
36
docs/en/setup.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Setup
|
||||
|
||||
## Requirements
|
||||
|
||||
```
|
||||
silverstripe/cms: ~3.1
|
||||
```
|
||||
|
||||
## Suggested Modules
|
||||
|
||||
```
|
||||
silverstripe/widgets: *
|
||||
silverstripe/comments: *
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require silverstripe/blog 2.0.x-dev
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Because the blog is part of the SiteTree the usage is the same as any other page.
|
||||
|
||||
By default, blog posts are filtered out of the SiteTree to avoid clutter and instead put in a GridField inside
|
||||
of the blog. If you wish to display the blog posts within the site tree you can do so using Silverstripe config.
|
||||
|
||||
In mysite/_config/settings.yml
|
||||
|
||||
```yaml
|
||||
BlogPost:
|
||||
show_in_sitetree: true
|
||||
```
|
||||
|
||||
Doing this will remove the GridField & result in a normal behaving SiteTree.
|
@ -22,4 +22,14 @@
|
||||
|
||||
<%t Blog.Posted "Posted" %>
|
||||
<a href="$MonthlyArchiveLink">$PublishDate.ago</a>
|
||||
|
||||
<% if $Authors || $AuthorNames %>
|
||||
<%t Blog.By "by" %>
|
||||
<% if $Authors %><% loop $Authors %>
|
||||
$Name.XML<% if not $Last || $Up.AuthorNames %>,<% end_if %>
|
||||
<% end_loop %><% end_if %>
|
||||
<% if $AuthorNames %>
|
||||
$AuthorNames
|
||||
<% end_if %>
|
||||
<% end_if %>
|
||||
</p>
|
||||
|
@ -18,11 +18,13 @@ class BlogPostFilterTest extends SapphireTest {
|
||||
$member = Member::currentUser();
|
||||
if($member) $member->logout();
|
||||
|
||||
$count = BlogPost::get()->count();
|
||||
$blog = $this->objFromFixture('Blog', 'firstblog');
|
||||
|
||||
$count = $blog->AllChildren()->Count();
|
||||
$this->assertEquals(3, $count, "Filtered blog posts");
|
||||
|
||||
SS_Datetime::set_mock_now("2020-01-01 00:00:00");
|
||||
$count = BlogPost::get()->count();
|
||||
$count = $blog->AllChildren()->Count();
|
||||
$this->assertEquals(5, $count, "Unfiltered blog posts");
|
||||
}
|
||||
|
||||
|
@ -98,4 +98,65 @@ class BlogTest extends SapphireTest {
|
||||
|
||||
}
|
||||
|
||||
public function testRoles() {
|
||||
$blog = $this->objFromFixture('Blog', 'fourthblog');
|
||||
$blog2 = $this->objFromFixture('Blog', 'firstblog');
|
||||
$postA = $this->objFromFixture('BlogPost', 'post-a');
|
||||
$postB = $this->objFromFixture('BlogPost', 'post-b');
|
||||
$postC = $this->objFromFixture('BlogPost', 'post-c');
|
||||
$editor = $this->objFromFixture('Member', 'blogeditor');
|
||||
$writer = $this->objFromFixture('Member', 'writer');
|
||||
$contributor = $this->objFromFixture('Member', 'contributor');
|
||||
$visitor = $this->objFromFixture('Member', 'visitor');
|
||||
|
||||
// Check that editors have all permissions on their own blog
|
||||
$this->assertTrue($blog->canEdit($editor));
|
||||
Debug::dump($blog2->Editors()->count());
|
||||
$this->assertFalse($blog2->canEdit($editor));
|
||||
$this->assertTrue($blog->canAddChildren($editor));
|
||||
$this->assertFalse($blog2->canAddChildren($editor));
|
||||
$this->assertTrue($postA->canEdit($editor));
|
||||
$this->assertTrue($postB->canEdit($editor));
|
||||
$this->assertTrue($postC->canEdit($editor));
|
||||
$this->assertTrue($postA->canPublish($editor));
|
||||
$this->assertTrue($postB->canPublish($editor));
|
||||
$this->assertTrue($postC->canPublish($editor));
|
||||
|
||||
// check rights of writers
|
||||
$this->assertFalse($blog->canEdit($writer));
|
||||
$this->assertFalse($blog2->canEdit($writer));
|
||||
$this->assertTrue($blog->canAddChildren($writer));
|
||||
$this->assertFalse($blog2->canAddChildren($writer));
|
||||
$this->assertTrue($postA->canEdit($writer));
|
||||
$this->assertFalse($postB->canEdit($writer));
|
||||
$this->assertTrue($postC->canEdit($writer));
|
||||
$this->assertTrue($postA->canPublish($writer));
|
||||
$this->assertFalse($postB->canPublish($writer));
|
||||
$this->assertTrue($postC->canPublish($writer));
|
||||
|
||||
// Check rights of contributors
|
||||
$this->assertFalse($blog->canEdit($contributor));
|
||||
$this->assertFalse($blog2->canEdit($contributor));
|
||||
$this->assertTrue($blog->canAddChildren($contributor));
|
||||
$this->assertFalse($blog2->canAddChildren($contributor));
|
||||
$this->assertTrue($postA->canEdit($contributor));
|
||||
$this->assertFalse($postB->canEdit($contributor));
|
||||
$this->assertTrue($postC->canEdit($contributor));
|
||||
$this->assertFalse($postA->canPublish($contributor));
|
||||
$this->assertFalse($postB->canPublish($contributor));
|
||||
$this->assertFalse($postC->canPublish($contributor));
|
||||
|
||||
// Check rights of non-cms user
|
||||
$this->assertFalse($blog->canEdit($visitor));
|
||||
$this->assertFalse($blog2->canEdit($visitor));
|
||||
$this->assertFalse($blog->canAddChildren($visitor));
|
||||
$this->assertFalse($blog2->canAddChildren($visitor));
|
||||
$this->assertFalse($postA->canEdit($visitor));
|
||||
$this->assertFalse($postB->canEdit($visitor));
|
||||
$this->assertFalse($postC->canEdit($visitor));
|
||||
$this->assertFalse($postA->canPublish($visitor));
|
||||
$this->assertFalse($postB->canPublish($visitor));
|
||||
$this->assertFalse($postC->canPublish($visitor));
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,9 @@ Group:
|
||||
Title: Administrators
|
||||
editors:
|
||||
Title: Editors
|
||||
blog-users:
|
||||
Title: Blog Users
|
||||
Code: blog-users
|
||||
|
||||
Permission:
|
||||
admins:
|
||||
@ -14,6 +17,17 @@ Permission:
|
||||
editors:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
Group: =>Group.editors
|
||||
blog-users:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
Group: =>Group.blog-users
|
||||
|
||||
|
||||
SiteConfig:
|
||||
default:
|
||||
CanEditType: 'OnlyTheseUsers'
|
||||
CanCreateTopLevelType: 'OnlyTheseUsers'
|
||||
EditorGroups: =>Group.admins,=>Group.editors
|
||||
CreateTopLevelGroups: =>Group.admins,=>Group.editors
|
||||
|
||||
Member:
|
||||
admin:
|
||||
@ -24,6 +38,21 @@ Member:
|
||||
FirstName: Test
|
||||
Surname: Editor
|
||||
Groups: =>Group.editors
|
||||
blogeditor:
|
||||
FirstName: Blog
|
||||
Surname: Editor
|
||||
Groups: =>Group.blog-users
|
||||
writer:
|
||||
FirstName: Blog
|
||||
Surname: Writer
|
||||
Groups: =>Group.blog-users
|
||||
contributor:
|
||||
FirstName: Blog
|
||||
Surname: Contributor
|
||||
Groups: =>Group.blog-users
|
||||
visitor:
|
||||
FirstName: Blog
|
||||
Surname: Visitor
|
||||
|
||||
Blog:
|
||||
firstblog:
|
||||
@ -38,6 +67,11 @@ Blog:
|
||||
Title: 'Third Blog'
|
||||
CanEditType: 'OnlyTheseUsers'
|
||||
EditorGroups: =>Group.editors
|
||||
fourthblog:
|
||||
Title: 'Fourth Blog'
|
||||
Editors: =>Member.blogeditor
|
||||
Writers: =>Member.writer
|
||||
Contributors: =>Member.contributor
|
||||
|
||||
BlogTag:
|
||||
firsttag:
|
||||
@ -91,9 +125,26 @@ BlogPost:
|
||||
PublishDate: '2015-01-01 00:00:00'
|
||||
Tags: =>BlogTag.firsttag
|
||||
Categories: =>BlogCategory.firstcategory
|
||||
Parent: =>Blog.firstblog
|
||||
futurepost2:
|
||||
Title: 'Future Post 2'
|
||||
URLSegment: future-post-2
|
||||
PublishDate: '2013-11-01 00:00:00'
|
||||
Tags: =>BlogTag.firsttag
|
||||
Categories: =>BlogCategory.firstcategory
|
||||
Parent: =>Blog.firstblog
|
||||
post-a:
|
||||
Title: 'One Post'
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.fourthblog
|
||||
Authors: =>Member.writer,=>Member.contributor
|
||||
post-b:
|
||||
Title: 'Second Post'
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.fourthblog
|
||||
Authors: =>Member.blogeditor
|
||||
post-c:
|
||||
Title: 'Third Post'
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.fourthblog
|
||||
Authors: =>Member.blogeditor,=>Member.writer,=>Member.contributor
|
||||
|
Loading…
Reference in New Issue
Block a user