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.
|
A fresh take on blogging in Silverstripe set out to tackle the issue of a cluttered Site Tree.
|
||||||
|
|
||||||
|
## Documentation index
|
||||||
|
|
||||||
## Requirements
|
* [Features](docs/en/features.md)
|
||||||
```
|
* [Setup](docs/en/setup.md)
|
||||||
silverstripe/cms: ~3.1
|
* [Roles](docs/en/roles.md)
|
||||||
```
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
@ -29,7 +29,7 @@ class BlogFilter extends Lumberjack {
|
|||||||
// Get the current stage.
|
// Get the current stage.
|
||||||
$stage = Versioned::current_stage();
|
$stage = Versioned::current_stage();
|
||||||
if($stage == "Stage") $stage = "";
|
if($stage == "Stage") $stage = "";
|
||||||
else $stage = "_" . Convert::raw2sql($stage);
|
elseif($stage) $stage = "_{$stage}";
|
||||||
|
|
||||||
// Filter published posts
|
// Filter published posts
|
||||||
$dataQuery = $staged->dataQuery()
|
$dataQuery = $staged->dataQuery()
|
||||||
@ -40,28 +40,50 @@ class BlogFilter extends Lumberjack {
|
|||||||
return $staged;
|
return $staged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Augments (@link Hierarchy::liveChildren()}
|
|
||||||
*
|
|
||||||
* @param $staged DataList
|
|
||||||
* @param $showAll boolean
|
|
||||||
**/
|
|
||||||
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
|
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
|
||||||
$staged = parent::liveChildren($showAll, $onlyDeletedFromStage);
|
$staged = parent::liveChildren($showAll, $onlyDeletedFromStage);
|
||||||
|
|
||||||
if(!$this->shouldFilter()
|
if(!$this->shouldFilter()
|
||||||
&& in_array(get_class($this->owner), ClassInfo::subClassesFor("Blog"))
|
&& $this->owner instanceof Blog
|
||||||
&& !Permission::check("VIEW_DRAFT_CONTENT")
|
&& !Permission::check("VIEW_DRAFT_CONTENT")
|
||||||
) {
|
) {
|
||||||
// Filter publish posts
|
// Filter publish posts
|
||||||
$dataQuery = $staged->dataQuery()
|
$dataQuery = $staged->dataQuery()
|
||||||
->innerJoin("BlogPost", '"BlogPost_Live"."ID" = "SiteTree"_"Live"."ID"')
|
->innerJoin("BlogPost", '"BlogPost_Live"."ID" = "SiteTree_Live"."ID"')
|
||||||
->where('"PublishDate" < \'' . Convert::raw2sql(SS_Datetime::now()) . '\'');
|
->where('"BlogPost"."PublishDate" < \'' . Convert::raw2sql(SS_Datetime::now()->getValue()) . '\'');
|
||||||
$staged = $staged->setDataQuery($dataQuery);
|
$staged = $staged->setDataQuery($dataQuery);
|
||||||
}
|
}
|
||||||
return $staged;
|
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
|
* @package silverstripe
|
||||||
* @subpackage blog
|
* @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>
|
* @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(
|
private static $db = array(
|
||||||
"PostsPerPage" => "Int",
|
"PostsPerPage" => "Int",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
private static $has_many = array(
|
private static $has_many = array(
|
||||||
"Tags" => "BlogTag",
|
"Tags" => "BlogTag",
|
||||||
"Categories" => "BlogCategory",
|
"Categories" => "BlogCategory",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static $many_many = array(
|
||||||
|
'Editors' => 'Member',
|
||||||
|
'Writers' => 'Member',
|
||||||
|
'Contributors' => 'Member',
|
||||||
|
);
|
||||||
|
|
||||||
private static $allowed_children = array(
|
private static $allowed_children = array(
|
||||||
"BlogPost",
|
"BlogPost",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
private static $extensions = array(
|
private static $extensions = array(
|
||||||
"BlogFilter",
|
"BlogFilter",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
private static $defaults = array(
|
private static $defaults = array(
|
||||||
"ProvideComments" => false,
|
"ProvideComments" => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $description = 'Adds a blog to your website.';
|
private static $description = 'Adds a blog to your website.';
|
||||||
|
|
||||||
|
|
||||||
public function getCMSFields() {
|
public function getCMSFields() {
|
||||||
$self =& $this;
|
$self =& $this;
|
||||||
$this->beforeUpdateCMSFields(function($fields) use ($self) {
|
$this->beforeUpdateCMSFields(function($fields) use ($self) {
|
||||||
@ -65,13 +105,144 @@ class Blog extends Page {
|
|||||||
$categories,
|
$categories,
|
||||||
$tags
|
$tags
|
||||||
));
|
));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
return $fields;
|
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() {
|
public function getSettingsFields() {
|
||||||
@ -79,11 +250,44 @@ class Blog extends Page {
|
|||||||
$fields->addFieldToTab("Root.Settings",
|
$fields->addFieldToTab("Root.Settings",
|
||||||
NumericField::create("PostsPerPage", _t("Blog.PostsPerPage", "Posts Per Page"))
|
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 $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return blog posts
|
* Return blog posts
|
||||||
*
|
*
|
||||||
@ -103,7 +307,7 @@ class Blog extends Page {
|
|||||||
*
|
*
|
||||||
* @param $year int
|
* @param $year int
|
||||||
* @param $month int
|
* @param $month int
|
||||||
* @param $dat int
|
* @param $day int
|
||||||
*
|
*
|
||||||
* @return DataList
|
* @return DataList
|
||||||
**/
|
**/
|
||||||
@ -147,6 +351,86 @@ class Blog extends Page {
|
|||||||
return GridFieldConfig_BlogPost::create();
|
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
|
* @package silverstripe
|
||||||
* @subpackage blog
|
* @subpackage blog
|
||||||
*
|
*
|
||||||
|
* @method ManyManyList Categories()
|
||||||
|
* @method ManyManyList Tags()
|
||||||
|
* @method ManyManyList Authors()
|
||||||
|
*
|
||||||
* @author Michael Strong <github@michaelstrong.co.uk>
|
* @author Michael Strong <github@michaelstrong.co.uk>
|
||||||
**/
|
**/
|
||||||
class BlogPost extends Page {
|
class BlogPost extends Page {
|
||||||
|
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
"PublishDate" => "SS_Datetime",
|
"PublishDate" => "SS_Datetime",
|
||||||
|
"AuthorNames" => "Varchar(1024)"
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $has_one = array(
|
private static $has_one = array(
|
||||||
@ -21,6 +26,7 @@ class BlogPost extends Page {
|
|||||||
private static $many_many = array(
|
private static $many_many = array(
|
||||||
"Categories" => "BlogCategory",
|
"Categories" => "BlogCategory",
|
||||||
"Tags" => "BlogTag",
|
"Tags" => "BlogTag",
|
||||||
|
"Authors" => "Member",
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $defaults = array(
|
private static $defaults = array(
|
||||||
@ -60,6 +66,23 @@ class BlogPost extends Page {
|
|||||||
private static $show_in_sitetree = false;
|
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() {
|
public function getCMSFields() {
|
||||||
Requirements::css(BLOGGER_DIR . '/css/cms.css');
|
Requirements::css(BLOGGER_DIR . '/css/cms.css');
|
||||||
@ -87,6 +110,24 @@ class BlogPost extends Page {
|
|||||||
'URLSegment',
|
'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
|
// Build up our sidebar
|
||||||
$options = BlogAdminSidebar::create(
|
$options = BlogAdminSidebar::create(
|
||||||
$publishDate = DatetimeField::create("PublishDate", _t("BlogPost.PublishDate", "Publish Date")),
|
$publishDate = DatetimeField::create("PublishDate", _t("BlogPost.PublishDate", "Publish Date")),
|
||||||
@ -100,7 +141,9 @@ class BlogPost extends Page {
|
|||||||
"Tags",
|
"Tags",
|
||||||
_t("BlogPost.Tags", "Tags"),
|
_t("BlogPost.Tags", "Tags"),
|
||||||
$self->Parent()->Tags()->map()->toArray()
|
$self->Parent()->Tags()->map()->toArray()
|
||||||
)->setMultiple(true)
|
)->setMultiple(true),
|
||||||
|
$authorField,
|
||||||
|
$authorNames
|
||||||
)->setTitle('Post Options');
|
)->setTitle('Post Options');
|
||||||
$publishDate->getDateField()->setConfig("showcalendar", true);
|
$publishDate->getDateField()->setConfig("showcalendar", true);
|
||||||
|
|
||||||
@ -116,14 +159,17 @@ class BlogPost extends Page {
|
|||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If no publish date is set, set the date to now.
|
|
||||||
**/
|
|
||||||
protected function onBeforeWrite() {
|
protected function onBeforeWrite() {
|
||||||
parent::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() {
|
public function onBeforePublish() {
|
||||||
if ($this->dbObject('PublishDate')->InPast() && !$this->isPublished()) {
|
if ($this->dbObject('PublishDate')->InPast() && !$this->isPublished()) {
|
||||||
$this->setCastedField("PublishDate", time());
|
$this->PublishDate = SS_Datetime::now()->getValue();
|
||||||
$this->write();
|
$this->write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +205,81 @@ class BlogPost extends Page {
|
|||||||
return true;
|
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") {
|
public function getMonthlyArchiveLink($type = "day") {
|
||||||
$date = $this->dbObject("PublishDate");
|
$date = $this->dbObject("PublishDate");
|
||||||
$year = $date->format("Y");
|
|
||||||
if($type != "year") {
|
if($type != "year") {
|
||||||
if($type == "day") {
|
if($type == "day") {
|
||||||
return Controller::join_links(
|
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" %>
|
<%t Blog.Posted "Posted" %>
|
||||||
<a href="$MonthlyArchiveLink">$PublishDate.ago</a>
|
<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>
|
</p>
|
||||||
|
@ -18,11 +18,13 @@ class BlogPostFilterTest extends SapphireTest {
|
|||||||
$member = Member::currentUser();
|
$member = Member::currentUser();
|
||||||
if($member) $member->logout();
|
if($member) $member->logout();
|
||||||
|
|
||||||
$count = BlogPost::get()->count();
|
$blog = $this->objFromFixture('Blog', 'firstblog');
|
||||||
|
|
||||||
|
$count = $blog->AllChildren()->Count();
|
||||||
$this->assertEquals(3, $count, "Filtered blog posts");
|
$this->assertEquals(3, $count, "Filtered blog posts");
|
||||||
|
|
||||||
SS_Datetime::set_mock_now("2020-01-01 00:00:00");
|
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");
|
$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
|
Title: Administrators
|
||||||
editors:
|
editors:
|
||||||
Title: Editors
|
Title: Editors
|
||||||
|
blog-users:
|
||||||
|
Title: Blog Users
|
||||||
|
Code: blog-users
|
||||||
|
|
||||||
Permission:
|
Permission:
|
||||||
admins:
|
admins:
|
||||||
@ -14,6 +17,17 @@ Permission:
|
|||||||
editors:
|
editors:
|
||||||
Code: CMS_ACCESS_CMSMain
|
Code: CMS_ACCESS_CMSMain
|
||||||
Group: =>Group.editors
|
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:
|
Member:
|
||||||
admin:
|
admin:
|
||||||
@ -24,6 +38,21 @@ Member:
|
|||||||
FirstName: Test
|
FirstName: Test
|
||||||
Surname: Editor
|
Surname: Editor
|
||||||
Groups: =>Group.editors
|
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:
|
Blog:
|
||||||
firstblog:
|
firstblog:
|
||||||
@ -38,6 +67,11 @@ Blog:
|
|||||||
Title: 'Third Blog'
|
Title: 'Third Blog'
|
||||||
CanEditType: 'OnlyTheseUsers'
|
CanEditType: 'OnlyTheseUsers'
|
||||||
EditorGroups: =>Group.editors
|
EditorGroups: =>Group.editors
|
||||||
|
fourthblog:
|
||||||
|
Title: 'Fourth Blog'
|
||||||
|
Editors: =>Member.blogeditor
|
||||||
|
Writers: =>Member.writer
|
||||||
|
Contributors: =>Member.contributor
|
||||||
|
|
||||||
BlogTag:
|
BlogTag:
|
||||||
firsttag:
|
firsttag:
|
||||||
@ -91,9 +125,26 @@ BlogPost:
|
|||||||
PublishDate: '2015-01-01 00:00:00'
|
PublishDate: '2015-01-01 00:00:00'
|
||||||
Tags: =>BlogTag.firsttag
|
Tags: =>BlogTag.firsttag
|
||||||
Categories: =>BlogCategory.firstcategory
|
Categories: =>BlogCategory.firstcategory
|
||||||
|
Parent: =>Blog.firstblog
|
||||||
futurepost2:
|
futurepost2:
|
||||||
Title: 'Future Post 2'
|
Title: 'Future Post 2'
|
||||||
URLSegment: future-post-2
|
URLSegment: future-post-2
|
||||||
PublishDate: '2013-11-01 00:00:00'
|
PublishDate: '2013-11-01 00:00:00'
|
||||||
Tags: =>BlogTag.firsttag
|
Tags: =>BlogTag.firsttag
|
||||||
Categories: =>BlogCategory.firstcategory
|
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