This commit is contained in:
Christopher Pitt 2015-05-10 02:33:12 +12:00
parent e72905c023
commit 787c02a19d
34 changed files with 2249 additions and 1606 deletions

View File

@ -11,20 +11,23 @@ class GridFieldCategorisationConfig extends GridFieldConfig_RecordEditor {
public function __construct($itemsPerPage = 15, $mergeRecords, $parentType, $parentMethod, $childMethod) { public function __construct($itemsPerPage = 15, $mergeRecords, $parentType, $parentMethod, $childMethod) {
parent::__construct($itemsPerPage); parent::__construct($itemsPerPage);
$this->removeComponentsByType("GridFieldAddNewButton"); $this->removeComponentsByType('GridFieldAddNewButton');
$this->addComponent( $this->addComponent(
new GridFieldAddByDBField("buttons-before-left") new GridFieldAddByDBField('buttons-before-left')
); );
$this->addComponent( $this->addComponent(
new GridFieldMergeAction($mergeRecords, $parentType, $parentMethod, $childMethod) new GridFieldMergeAction($mergeRecords, $parentType, $parentMethod, $childMethod)
); );
/**
* @var GridFieldDataColumns $columns
*/
$columns = $this->getComponentByType('GridFieldDataColumns'); $columns = $this->getComponentByType('GridFieldDataColumns');
$columns->setFieldFormatting(array( $columns->setFieldFormatting(array(
'BlogPostsCount' => function($value, &$item) { 'BlogPostsCount' => function ($value, CategorisationObject $item) {
return $item->BlogPosts()->Count(); return $item->BlogPosts()->Count();
} }
)); ));

View File

@ -16,8 +16,7 @@ class GridFieldFormAction extends GridField_FormAction {
/** /**
* @return array * @return array
*/ */
public function getAttributes() public function getAttributes() {
{
$attributes = parent::getAttributes(); $attributes = parent::getAttributes();
return array_merge( return array_merge(

View File

@ -66,7 +66,7 @@ class GridFieldMergeAction implements GridField_ColumnProvider, GridField_Action
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName) {
if($columnName === 'MergeAction' && $record->{$this->childMethod}()->Count() > 0) { if($columnName === 'MergeAction' && $record->{$this->childMethod}()->Count() > 0) {
$dropdown = new DropdownField('Target', 'Target', $this->records->exclude('ID', $record->ID)->map()); $dropdown = new DropdownField('Target', 'Target', $this->records->exclude('ID', $record->ID)->map());
$prefix = strtolower($this->parentMethod . '-' . $this->childMethod); $prefix = strtolower($this->parentMethod . '-' . $this->childMethod);
$action = GridFieldFormAction::create( $action = GridFieldFormAction::create(
@ -129,7 +129,7 @@ class GridFieldMergeAction implements GridField_ColumnProvider, GridField_Action
$posts = $fromParent->{$this->childMethod}(); $posts = $fromParent->{$this->childMethod}();
foreach ($posts as $post) { foreach($posts as $post) {
$relationship = $post->{$this->parentMethod}(); $relationship = $post->{$this->parentMethod}();
$relationship->remove($fromParent); $relationship->remove($fromParent);

View File

@ -2,59 +2,82 @@
/** /**
* @deprecated since version 2.0 * @deprecated since version 2.0
*
* @property int $ParentID
* @property string $Date
* @property string $PublishDate
* @property string $Tags
*/ */
class BlogEntry extends BlogPost implements MigratableObject { class BlogEntry extends BlogPost implements MigratableObject {
/**
* @var string
*/
private static $hide_ancestor = 'BlogEntry'; private static $hide_ancestor = 'BlogEntry';
/**
* @var array
*/
private static $db = array( private static $db = array(
"Date" => "SS_Datetime", 'Date' => 'SS_Datetime',
"Author" => "Text", 'Author' => 'Text',
"Tags" => "Text" 'Tags' => 'Text',
); );
/** /**
* Safely split and parse all distinct tags assigned to this BlogEntry * {@inheritdoc}
*
* @return array Associative array of lowercase tag to native case tags
* @deprecated since version 2.0
*/ */
public function TagNames() {
$tags = preg_split("/\s*,\s*/", trim($this->Tags));
$results = array();
foreach($tags as $tag) {
if($tag) $results[mb_strtolower($tag)] = $tag;
}
return $results;
}
public function canCreate($member = null) { public function canCreate($member = null) {
// Deprecated
return false; return false;
} }
/**
* {@inheritdoc}
*/
public function up() { public function up() {
// Migrate tags
foreach($this->TagNames() as $tag) { foreach($this->TagNames() as $tag) {
// Skip if tag already assigned if($this->Tags()->filter('Title', $tag)->count()) {
if($this->Tags()->filter('Title', $tag)->count()) continue; continue;
}
// Create tag
$tagObject = new BlogTag(); $tagObject = new BlogTag();
$tagObject->Title = $tag; $tagObject->Title = $tag;
$tagObject->BlogID = $this->ParentID; $tagObject->BlogID = $this->ParentID;
$tagObject->write(); $tagObject->write();
$this->Tags()->add($tagObject); $this->Tags()->add($tagObject);
} }
// Update fields
$this->PublishDate = $this->Date; $this->PublishDate = $this->Date;
if($this->ClassName === 'BlogEntry') { if($this->ClassName === 'BlogEntry') {
$this->ClassName = 'BlogPost'; $this->ClassName = 'BlogPost';
$this->write(); $this->write();
} }
} }
/**
* Safely split and parse all distinct tags assigned to this BlogEntry.
*
* @deprecated since version 2.0
*
* @return array
*/
public function TagNames() {
$tags = preg_split('/\s*,\s*/', trim($this->Tags));
$results = array();
foreach($tags as $tag) {
if($tag) $results[mb_strtolower($tag)] = $tag;
}
return $results;
}
/**
* {@inheritdoc}
*/
public function requireDefaultRecords() { public function requireDefaultRecords() {
parent::requireDefaultRecords(); parent::requireDefaultRecords();
@ -69,4 +92,5 @@ class BlogEntry extends BlogPost implements MigratableObject {
* @deprecated since version 2.0 * @deprecated since version 2.0
*/ */
class BlogEntry_Controller extends BlogPost_Controller { class BlogEntry_Controller extends BlogPost_Controller {
} }

View File

@ -4,23 +4,36 @@
* @deprecated since version 2.0 * @deprecated since version 2.0
*/ */
class BlogHolder extends BlogTree implements MigratableObject { class BlogHolder extends BlogTree implements MigratableObject {
/**
* @var string
*/
private static $hide_ancestor = 'BlogHolder'; private static $hide_ancestor = 'BlogHolder';
/**
* @var array
*/
private static $db = array( private static $db = array(
'AllowCustomAuthors' => 'Boolean', 'AllowCustomAuthors' => 'Boolean',
'ShowFullEntry' => 'Boolean', 'ShowFullEntry' => 'Boolean',
); );
/**
* @var array
*/
private static $has_one = array( private static $has_one = array(
'Owner' => 'Member', 'Owner' => 'Member',
); );
/**
* {@inheritdoc}
*/
public function canCreate($member = null) { public function canCreate($member = null) {
// Deprecated
return false; return false;
} }
/**
* {@inheritdoc}
*/
public function up() { public function up() {
if($this->ClassName === 'BlogHolder') { if($this->ClassName === 'BlogHolder') {
$this->ClassName = 'Blog'; $this->ClassName = 'Blog';
@ -33,5 +46,5 @@ class BlogHolder extends BlogTree implements MigratableObject {
* @deprecated since version 2.0 * @deprecated since version 2.0
*/ */
class BlogHolder_Controller extends BlogTree_Controller { class BlogHolder_Controller extends BlogTree_Controller {
} }

View File

@ -4,19 +4,29 @@
* @deprecated since version 2.0 * @deprecated since version 2.0
*/ */
class BlogTree extends Page implements MigratableObject { class BlogTree extends Page implements MigratableObject {
/**
* @var string
*/
private static $hide_ancestor = 'BlogTree'; private static $hide_ancestor = 'BlogTree';
/**
* @var array
*/
private static $db = array( private static $db = array(
'Name' => 'Varchar(255)', 'Name' => 'Varchar(255)',
'LandingPageFreshness' => 'Varchar', 'LandingPageFreshness' => 'Varchar',
); );
/**
* {@inheritdoc}
*/
public function canCreate($member = null) { public function canCreate($member = null) {
// Deprecated
return false; return false;
} }
/**
* {@inheritdoc}
*/
public function up() { public function up() {
if($this->ClassName === 'BlogTree') { if($this->ClassName === 'BlogTree') {
$this->ClassName = 'Page'; $this->ClassName = 'Page';
@ -29,4 +39,5 @@ class BlogTree extends Page implements MigratableObject {
* @deprecated since version 2.0 * @deprecated since version 2.0
*/ */
class BlogTree_Controller extends Page_Controller { class BlogTree_Controller extends Page_Controller {
}
}

View File

@ -1,34 +1,24 @@
<?php <?php
/**
* Description of BlogMigrationTask
*
* @author dmooyman
*/
class BlogMigrationTask extends MigrationTask { class BlogMigrationTask extends MigrationTask {
/** /**
* Should this task be invoked automatically via dev/build? * Should this task be invoked automatically via dev/build?
* *
* @config * @config
* @var boolean *
* @var bool
*/ */
private static $run_during_dev_build = true; private static $run_during_dev_build = true;
protected function message($text) { /**
if(Controller::curr() instanceof DatabaseAdmin) { * {@inheritdoc}
DB::alteration_message($text, 'obsolete'); */
} else {
Debug::message($text);
}
}
public function up() { public function up() {
$classes = ClassInfo::implementorsOf('MigratableObject'); $classes = ClassInfo::implementorsOf('MigratableObject');
$this->message('Migrating legacy blog records'); $this->message('Migrating legacy blog records');
foreach($classes as $class) { foreach($classes as $class) {
// migrate objects in each stage
if(is_subclass_of($class, 'SiteTree')) { if(is_subclass_of($class, 'SiteTree')) {
foreach(array('Stage', 'Live') as $stage) { foreach(array('Stage', 'Live') as $stage) {
$oldMode = Versioned::get_reading_mode(); $oldMode = Versioned::get_reading_mode();
@ -37,40 +27,66 @@ class BlogMigrationTask extends MigrationTask {
Versioned::set_reading_mode($oldMode); Versioned::set_reading_mode($oldMode);
} }
} else { } else {
// Migrate object
$this->upClass($class); $this->upClass($class);
} }
} }
} }
public function down() { /**
$this->message('BlogMigrationTask::down() not implemented'); * @param string $text
*/
protected function message($text) {
if(Controller::curr() instanceof DatabaseAdmin) {
DB::alteration_message($text, 'obsolete');
} else {
Debug::message($text);
}
} }
/** /**
* Migrate records of a single class * Migrate records of a single class
* *
* @param type $class * @param string $class
* @param type $stage * @param null|string $stage
*/ */
protected function upClass($class, $stage = null) { protected function upClass($class, $stage = null) {
if(!class_exists($class)) return; if(!class_exists($class)) {
return;
}
// Migrate all records
$items = $class::get(); $items = $class::get();
if($count = $items->count()) { if($count = $items->count()) {
$stageMessage = " in stage {$stage}"; $this->message(
$this->message("Migrating {$count} legacy {$class} records{$stageMessage}."); sprintf(
'Migrating %s legacy %s records in stage %s.',
$count,
$class,
$stage
)
);
foreach($items as $item) { foreach($items as $item) {
// Cancel if onBeforeUp returns false
$cancel = $item->extend('onBeforeUp'); $cancel = $item->extend('onBeforeUp');
if($cancel && min($cancel) === false) continue;
// Up if($cancel && min($cancel) === false) {
continue;
}
/**
* @var MigratableObject $item
*/
$item->up(); $item->up();
// Post extensions
$item->extend('onAfterUp'); $item->extend('onAfterUp');
} }
} }
} }
/**
* {@inheritdoc}
*/
public function down() {
$this->message('BlogMigrationTask::down() not implemented');
}
} }

View File

@ -1,9 +1,8 @@
<?php <?php
interface MigratableObject { interface MigratableObject {
/** /**
* Migrate the object up to the current version * Migrate the object up to the current version.
*/ */
public function up(); public function up();
} }

View File

@ -1,27 +1,49 @@
<?php <?php
if(!class_exists('Widget')) return; if(!class_exists('Widget')) {
return;
}
/** /**
* @deprecated since version 2.0 * @deprecated since version 2.0
*
* @property string $DisplayMode
* @property string $ArchiveType
*/ */
class ArchiveWidget extends BlogArchiveWidget implements MigratableObject { class ArchiveWidget extends BlogArchiveWidget implements MigratableObject {
/**
* @var array
*/
private static $db = array( private static $db = array(
'DisplayMode' => 'Varchar' 'DisplayMode' => 'Varchar',
); );
private static $only_available_in = array('none'); /**
* @var array
*/
private static $only_available_in = array(
'none',
);
/**
* {@inheritdoc}
*/
public function canCreate($member = null) { public function canCreate($member = null) {
// Deprecated
return false; return false;
} }
/**
* {@inheritdoc}
*/
public function up() { public function up() {
if($this->DisplayMode) { if($this->DisplayMode) {
$this->ArchiveType = ($this->DisplayMode === 'year') ? 'Yearly' : 'Monthly'; $this->ArchiveType = 'Monthly';
if($this->DisplayMode === 'year') {
$this->ArchiveType = 'Yearly';
}
} }
$this->ClassName = 'BlogArchiveWidget'; $this->ClassName = 'BlogArchiveWidget';
$this->write(); $this->write();
} }

View File

@ -1,29 +1,43 @@
<?php <?php
if(! class_exists('Widget')) return; if(!class_exists('Widget')) {
return;
}
/** /**
* A list of tags associated with blog posts * A list of tags associated with blog posts.
* *
* @package blog * @package blog
*/ */
class TagCloudWidget extends BlogTagsWidget implements MigratableObject { class TagCloudWidget extends BlogTagsWidget implements MigratableObject {
/**
* @var array
*/
private static $db = array( private static $db = array(
"Title" => "Varchar", 'Title' => 'Varchar',
"Limit" => "Int", 'Limit' => 'Int',
"Sortby" => "Varchar" 'Sortby' => 'Varchar',
); );
private static $only_available_in = array('none'); /**
* @var array
*/
private static $only_available_in = array(
'none',
);
/**
* {@inheritdoc}
*/
public function canCreate($member = null) { public function canCreate($member = null) {
// Deprecated
return false; return false;
} }
/**
* {@inheritdoc}
*/
public function up() { public function up() {
$this->ClassName = 'BlogTagsWidget'; $this->ClassName = 'BlogTagsWidget';
$this->write(); $this->write();
} }
} }

View File

@ -1,23 +1,23 @@
<?php <?php
/** /**
* Adds Blog specific behaviour to Comment * Adds Blog specific behaviour to Comment.
* Extends {@see Comment}
*/ */
class BlogCommentExtension extends DataExtension { class BlogCommentExtension extends DataExtension {
/** /**
* Extra CSS classes for styling different comment types. * Extra CSS classes for styling different comment types.
*
* @return string * @return string
*/ */
public function getExtraClass() { public function getExtraClass() {
$blogPost = $this->owner->getParent(); $blogPost = $this->owner->getParent();
// Make sure we're dealing with a BlogPost. if($blogPost instanceof BlogPost) {
if ( ($blogPost instanceof BlogPost) if($blogPost->isAuthor($this->owner->Author())) {
&& $blogPost->isAuthor($this->owner->Author()) return 'author-comment';
) { }
return 'author-comment';
} }
return '';
} }
} }

View File

@ -1,89 +1,106 @@
<?php <?php
/** /**
* This class is responsible for filtering the SiteTree when necessary and also * This class is responsible for filtering the SiteTree when necessary and also overlaps into
* overlaps into filtering only published posts. * filtering only published posts.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk>
*
**/
class BlogFilter extends Lumberjack { class BlogFilter extends Lumberjack {
/** /**
* Augments (@link Hierarchy::stageChildren()} * {@inheritdoc}
* */
* @param $staged DataList
* @param $showAll boolean
**/
public function stageChildren($showAll = false) { public function stageChildren($showAll = false) {
$staged = parent::stageChildren($showAll); $staged = parent::stageChildren($showAll);
if(!$this->shouldFilter() if(!$this->shouldFilter() && $this->subclassForBlog() && !Permission::check('VIEW_DRAFT_CONTENT')) {
&& in_array(get_class($this->owner), ClassInfo::subClassesFor("Blog"))
&& !Permission::check("VIEW_DRAFT_CONTENT")
) {
// Get the current stage.
$stage = Versioned::current_stage(); $stage = Versioned::current_stage();
if($stage == "Stage") $stage = "";
elseif($stage) $stage = "_{$stage}";
// Filter published posts if($stage == 'Stage') {
$stage = '';
} elseif($stage) {
$stage = '_' . $stage;
}
$dataQuery = $staged->dataQuery() $dataQuery = $staged->dataQuery()
->innerJoin("BlogPost", '"BlogPost' . $stage . '"."ID" = "SiteTree' . $stage . '"."ID"') ->innerJoin('BlogPost', sprintf('BlogPost%s.ID = SiteTree%s.ID', $stage, $stage))
->where('"PublishDate" < \'' . Convert::raw2sql(SS_Datetime::now()) . '\''); ->where(sprintf('PublishDate < \'%s\'', Convert::raw2sql(SS_Datetime::now())));
$staged = $staged->setDataQuery($dataQuery); $staged = $staged->setDataQuery($dataQuery);
} }
return $staged; return $staged;
} }
/**
* @return bool
*/
protected function subclassForBlog() {
return in_array(get_class($this->owner), ClassInfo::subClassesFor('Blog'));
}
/**
* {@inheritdoc}
*/
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() && $this->isBlog() && !Permission::check('VIEW_DRAFT_CONTENT')) {
&& $this->owner instanceof Blog
&& !Permission::check("VIEW_DRAFT_CONTENT")
) {
// 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('"BlogPost"."PublishDate" < \'' . Convert::raw2sql(SS_Datetime::now()->getValue()) . '\''); ->where(sprintf('PublishDate < \'%s\'', Convert::raw2sql(SS_Datetime::now())));
$staged = $staged->setDataQuery($dataQuery); $staged = $staged->setDataQuery($dataQuery);
} }
return $staged; return $staged;
} }
/**
* @return bool
*/
protected function isBlog() {
return $this->owner instanceof Blog;
}
/**
* {@inheritdoc}
*/
public function updateCMSFields(FieldList $fields) { public function updateCMSFields(FieldList $fields) {
$excluded = $this->owner->getExcludedSiteTreeClassNames(); $excluded = $this->owner->getExcludedSiteTreeClassNames();
if(!empty($excluded)) { if(!empty($excluded)) {
$pages = SiteTree::get()->filter(array( $pages = SiteTree::get()->filter(array(
'ParentID' => $this->owner->ID, 'ParentID' => $this->owner->ID,
'ClassName' => $excluded 'ClassName' => $excluded
))->sort('"SiteTree"."Created" DESC'); ))->sort('SiteTree.Created DESC');
$gridField = new BlogFilter_GridField( $gridField = new BlogFilter_GridField(
"ChildPages", 'ChildPages',
$this->getLumberjackTitle(), $this->getLumberjackTitle(),
$pages, $pages,
$this->getLumberjackGridFieldConfig() $this->getLumberjackGridFieldConfig()
); );
$tab = new Tab('ChildPages', $this->getLumberjackTitle(), $gridField); $tab = new Tab('ChildPages', $this->getLumberjackTitle(), $gridField);
$fields->insertBefore($tab, 'Main'); $fields->insertBefore($tab, 'Main');
} }
} }
} }
/** /**
* Enables children of non-editable pages to be edited * Enables children of non-editable pages to be edited.
*/ */
class BlogFilter_GridField extends GridField { class BlogFilter_GridField extends GridField {
public function transform(FormTransformation $trans) { /**
// Don't allow parent object transformations to propegate automatically to child records * @param FormTransformation $transformation
*
* @return $this
*/
public function transform(FormTransformation $transformation) {
return $this; return $this;
} }
} }

View File

@ -5,25 +5,34 @@
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
**/
class BlogMemberExtension extends DataExtension { class BlogMemberExtension extends DataExtension {
/**
* @var array
*/
private static $db = array( private static $db = array(
'URLSegment' => 'Varchar', 'URLSegment' => 'Varchar',
'BlogProfileSummary' => 'Text' 'BlogProfileSummary' => 'Text',
); );
/**
* @var array
*/
private static $has_one = array( private static $has_one = array(
'BlogProfileImage' => 'Image' 'BlogProfileImage' => 'Image',
); );
/**
* @var array
*/
private static $belongs_many_many = array( private static $belongs_many_many = array(
'AuthoredPosts' => 'BlogPost' 'AuthoredPosts' => 'BlogPost',
); );
/**
* {@inheritdoc}
*/
public function onBeforeWrite() { public function onBeforeWrite() {
// Generate a unique URL segment for the Member.
$count = 1; $count = 1;
$this->owner->URLSegment = $this->generateURLSegment(); $this->owner->URLSegment = $this->generateURLSegment();
@ -34,45 +43,45 @@ class BlogMemberExtension extends DataExtension {
} }
} }
public function updateCMSFields(FieldList $fields) {
$fields->removeByName('URLSegment');
return $fields;
}
/** /**
* Generate a unique URL segment based on the Member's name. * Generate a unique URL segment based on the Member's name.
* *
* @return string Generated URL segment. * @return string
*/ */
public function generateURLSegment() { public function generateURLSegment() {
$filter = URLSegmentFilter::create(); $filter = URLSegmentFilter::create();
$name = $this->owner->FirstName . ' ' . $this->owner->Surname; $name = $this->owner->FirstName . ' ' . $this->owner->Surname;
$urlSegment = $filter->filter($name); $urlSegment = $filter->filter($name);
// Fallback to generic profile name if path is empty (= no valid, convertable characters)
if(!$urlSegment || $urlSegment == '-' || $urlSegment == '-1') { if(!$urlSegment || $urlSegment == '-' || $urlSegment == '-1') {
$urlSegment = "profile-".$this->owner->ID; $urlSegment = 'profile-' . $this->owner->ID;
} }
return $urlSegment; return $urlSegment;
} }
/** /**
* Returns TRUE if this object has a URL segment value that does not conflict with any other objects. * Returns TRUE if this object has a URL segment value that does not conflict with any other
* * objects.
*
* @return bool * @return bool
*/ */
public function validURLSegment() { public function validURLSegment() {
$conflict = Member::get()->filter('URLSegment', $this->owner->URLSegment); $conflict = Member::get()->filter('URLSegment', $this->owner->URLSegment);
// If the Member we're checking against is saved, exclude them from the check. if($this->owner->ID) {
// i.e. don't conflict against yourself.
if ($this->owner->ID) {
$conflict = $conflict->exclude('ID', $this->owner->ID); $conflict = $conflict->exclude('ID', $this->owner->ID);
} }
return $conflict->count() == 0; return $conflict->count() == 0;
} }
/**
* {@inheritdoc}
*/
public function updateCMSFields(FieldList $fields) {
$fields->removeByName('URLSegment');
return $fields;
}
} }

View File

@ -1,36 +1,38 @@
<?php <?php
/** /**
* This is responsible for filtering only published posts to users who do not have * This is responsible for filtering only published posts to users who do not have permission to
* permission to view non-published posts. * view non-published posts.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk>
**/
class BlogPostFilter extends DataExtension { class BlogPostFilter extends DataExtension {
/** /**
* Augment queries so that we don't fetch unpublished articles. * Augment queries so that we don't fetch unpublished articles.
**/ *
* @param SQLQuery $query
*/
public function augmentSQL(SQLQuery &$query) { public function augmentSQL(SQLQuery &$query) {
$stage = Versioned::current_stage(); $stage = Versioned::current_stage();
if($stage == 'Live' || !Permission::check("VIEW_DRAFT_CONTENT")) {
$query->addWhere("PublishDate < '" . Convert::raw2sql(SS_Datetime::now()) . "'"); if($stage == 'Live' || !Permission::check('VIEW_DRAFT_CONTENT')) {
$query->addWhere(sprintf('PublishDate < \'%s\'', Convert::raw2sql(SS_Datetime::now())));
} }
} }
/** /**
* This is a fix so that when we try to fetch subclasses of BlogPost, * This is a fix so that when we try to fetch subclasses of BlogPost, lazy loading includes the
* lazy loading includes the BlogPost table in its query. Leaving this table out * BlogPost table in its query. Leaving this table out means the default sort order column
* means the default sort order column PublishDate causes an error. * PublishDate causes an error.
* *
* @see https://github.com/silverstripe/silverstripe-framework/issues/1682 * @see https://github.com/silverstripe/silverstripe-framework/issues/1682
**/ *
* @param SQLQuery $query
* @param mixed $dataQuery
* @param mixed $parent
*/
public function augmentLoadLazyFields(SQLQuery &$query, &$dataQuery, $parent) { public function augmentLoadLazyFields(SQLQuery &$query, &$dataQuery, $parent) {
// Ensures that we're joining the BlogPost table which holds required db fields. $dataQuery->innerJoin('BlogPost', 'SiteTree.ID = BlogPost.ID');
$dataQuery->innerJoin("BlogPost", "`SiteTree`.`ID` = `BlogPost`.`ID`");
} }
}
}

View File

@ -1,30 +1,29 @@
<?php <?php
/** /**
* Customise blog post to support comment notifications * Customise blog post to support comment notifications.
* Extends {@see BlogPost} with extensions to {@see CommentNotifiable} *
* Extends {@see BlogPost} with extensions to {@see CommentNotifiable}.
*/ */
class BlogPostNotifications extends DataExtension { class BlogPostNotifications extends DataExtension {
/** /**
* Notify all authors of notifications * Notify all authors of notifications.
* *
* @param type $list * @param SS_List $list
* @param type $comment * @param mixed $comment
*/ */
public function updateNotificationRecipients(&$list, &$comment) { public function updateNotificationRecipients(&$list, &$comment) {
// Notify all authors
$list = $this->owner->Authors(); $list = $this->owner->Authors();
} }
/** /**
* Update comment to include the page title * Update comment to include the page title.
* *
* @param string $subject * @param string $subject
* @param Comment $comment * @param Comment $comment
* @param Member|string $recipient * @param Member|string $recipient
*/ */
public function updateNotificationSubject(&$subject, &$comment, &$recipient) { public function updateNotificationSubject(&$subject, &$comment, &$recipient) {
$subject = "A new comment has been posted on " . $this->owner->Title; $subject = sprintf('A new comment has been posted on ', $this->owner->Title);
} }
} }

View File

@ -1,48 +1,61 @@
<?php <?php
/** /**
* Adds URLSegment functionality to Tags & Categories * Adds URLSegment functionality to Tags & Categories.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk> class URLSegmentExtension extends DataExtension {
**/ /**
class URLSegmentExtension extends DataExtension { * @var array
*/
private static $db = array( private static $db = array(
"URLSegment" => "Varchar(255)" 'URLSegment' => 'Varchar(255)',
); );
public function onBeforeWrite() { /**
$this->owner->generateURLSegment(); * {@inheritdoc}
} */
public function onBeforeWrite() {
$this->owner->generateURLSegment();
}
/**
* Generates a unique URLSegment from the title. /**
* * Generates a unique URLSegment from the title.
* @param $increment *
* * @param int $increment
* @return string URLSegment *
**/ * @return string
public function generateURLSegment($increment = null) { */
$filter = new URLSegmentFilter(); public function generateURLSegment($increment = null) {
$this->owner->URLSegment = $filter->filter($this->owner->Title); $filter = new URLSegmentFilter();
if(is_int($increment)) $this->owner->URLSegment .= '-' . $increment;
$this->owner->URLSegment = $filter->filter($this->owner->Title);
// Check to see if the URLSegment already exists
$duplicate = DataList::create($this->owner->ClassName)->filter(array( if(is_int($increment)) {
"URLSegment" => $this->owner->URLSegment, $this->owner->URLSegment .= '-' . $increment;
"BlogID" => $this->owner->BlogID }
));
if($this->owner->ID) $duplicate = $duplicate->exclude("ID", $this->owner->ID); $duplicate = DataList::create($this->owner->ClassName)->filter(array(
if($duplicate->count() > 0) { 'URLSegment' => $this->owner->URLSegment,
$increment = is_int($increment) ? $increment + 1 : 0; 'BlogID' => $this->owner->BlogID,
$this->owner->generateURLSegment((int) $increment); ));
}
return $this->owner->URLSegment; if($this->owner->ID) {
} $duplicate = $duplicate->exclude('ID', $this->owner->ID);
}
}
if($duplicate->count() > 0) {
if(is_int($increment)) {
$increment += 1;
} else {
$increment = 0;
}
$this->owner->generateURLSegment((int) $increment);
}
return $this->owner->URLSegment;
}
}

View File

@ -1,13 +1,16 @@
<?php <?php
class BlogAdminSidebar extends FieldGroup { class BlogAdminSidebar extends FieldGroup {
/**
* @return bool
*/
public function isOpen() { public function isOpen() {
$sidebar = Cookie::get('blog-admin-sidebar'); $sidebar = Cookie::get('blog-admin-sidebar');
if($sidebar == 1 || is_null($sidebar)) { if($sidebar == 1 || is_null($sidebar)) {
return true; return true;
} }
return false; return false;
} }
}
}

View File

@ -5,168 +5,181 @@
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk>
**/
class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLProvider { class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLProvider {
/** /**
* HTML Fragment to render the field. * HTML Fragment to render the field.
* *
* @var string * @var string
**/ */
protected $targetFragment; protected $targetFragment;
/** /**
* Default field to create the dataobject by should be Title. * Default field to create the DataObject by should be Title.
* *
* @var string * @var string
**/ */
protected $dataObjectField = "Title"; protected $dataObjectField = 'Title';
/** /**
* Creates a text field and add button which allows the user to directly create a new * Creates a text field and add button which allows the user to directly create a new
* DataObject by just entering the title. * DataObject by just entering the title.
* *
* @param $targetFragment string * @param string $targetFragment
**/ * @param string $dataObjectField
public function __construct($targetFragment = 'before', $dataObjectField = "Title") { */
public function __construct($targetFragment = 'before', $dataObjectField = 'Title') {
$this->targetFragment = $targetFragment; $this->targetFragment = $targetFragment;
$this->dataObjectField = (string) $dataObjectField; $this->dataObjectField = (string) $dataObjectField;
} }
/** /**
* Provide actions to this component. * Provide actions to this component.
* *
* @param $gridField GridField * @param GridField $gridField
* *
* @return array * @return array
**/ */
public function getActions($gridField) { public function getActions($gridField) {
return array("add"); return array(
'add',
);
} }
/** /**
* Handles the add action for the given DataObject * Handles the add action for the given DataObject.
* *
* @param $gridFIeld GridFIeld * @param $gridField GridField
* @param $actionName string * @param $actionName string
* @param $arguments mixed * @param $arguments mixed
* @param $data array * @param $data array
**/ *
* @return null|SS_HTTPResponse
*
* @throws UnexpectedValueException
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) { public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if($actionName == "add") { if($actionName == 'add') {
$dbField = $this->getDataObjectField(); $dbField = $this->getDataObjectField();
$objClass = $gridField->getModelClass(); $objClass = $gridField->getModelClass();
$obj = new $objClass(); $obj = new $objClass();
if($obj->hasField($dbField)) { if($obj->hasField($dbField)) {
$obj->setCastedField($dbField, $data['gridfieldaddbydbfield'][$obj->ClassName][$dbField]); $obj->setCastedField($dbField, $data['gridfieldaddbydbfield'][$obj->ClassName][$dbField]);
if($obj->canCreate()) { if($obj->canCreate()) {
$id = $gridField->getList()->add($obj); $id = $gridField->getList()->add($obj);
if(!$id) { if(!$id) {
$gridField->setError(_t( $gridField->setError(
"GridFieldAddByDBField.AddFail", _t(
"Unable to save {class} to the database.", 'GridFieldAddByDBField.AddFail',
"Unable to add the DataObject.", 'Unable to save {class} to the database.',
array( 'Unable to add the DataObject.',
"class" => get_class($obj) array(
)), 'class' => get_class($obj),
"error" )
),
'error'
); );
} }
} else { } else {
return Security::permissionFailure( return Security::permissionFailure(
Controller::curr(), Controller::curr(),
_t( _t(
"GridFieldAddByDBField.PermissionFail", 'GridFieldAddByDBField.PermissionFail',
"You don't have permission to create a {class}.", 'You don\'t have permission to create a {class}.',
"Unable to add the DataObject.", 'Unable to add the DataObject.',
array( array(
"class" => get_class($obj) 'class' => get_class($obj)
) )
) )
); );
} }
} else { } else {
throw new UnexpectedValueException("Invalid field (" . $dbField . ") on " . $obj->ClassName . "."); throw new UnexpectedValueException(
sprintf(
'Invalid field (%s) on %s.',
$dbField,
$obj->ClassName
)
);
} }
} }
return null;
} }
/**
* Renders the TextField and add button to the GridField.
*
* @param $girdField GridField
*
* @return string HTML
**/
public function getHTMLFragments($gridField) {
$dataClass = $gridField->getList()->dataClass();
$obj = singleton($dataClass);
if(!$obj->canCreate()) return "";
$dbField = $this->getDataObjectField();
$textField = TextField::create(
"gridfieldaddbydbfield[" . $obj->ClassName . "][" . Convert::raw2htmlatt($dbField) . "]"
)->setAttribute("placeholder", $obj->fieldLabel($dbField))
->addExtraClass("no-change-track");
$addAction = new GridField_FormAction($gridField,
'add',
_t('GridFieldAddByDBField.Add',
"Add {name}", "Add button text",
array("name" => $obj->i18n_singular_name())
),
'add',
'add'
);
$addAction->setAttribute('data-icon', 'add');
// Start thinking about rending this back to the GF
$forTemplate = new ArrayData(array());
$forTemplate->Fields = new ArrayList();
$forTemplate->Fields->push($textField);
$forTemplate->Fields->push($addAction);
return array(
$this->targetFragment => $forTemplate->renderWith("GridFieldAddByDBField")
);
}
/** /**
* Returns the database field for which we'll add the new data object. * Returns the database field for which we'll add the new data object.
* *
* @return string * @return string
**/ */
public function getDataObjectField() { public function getDataObjectField() {
return $this->dataObjectField; return $this->dataObjectField;
} }
/** /**
* Set the database field. * Set the database field.
* *
* @param $field string * @param $field string
**/ */
public function setDataObjectField($field) { public function setDataObjectField($field) {
$this->dataObjectField = (string) $field; $this->dataObjectField = (string) $field;
} }
} /**
* Renders the TextField and add button to the GridField.
*
* @param $gridField GridField
*
* @return string
*/
public function getHTMLFragments($gridField) {
$dataClass = $gridField->getList()->dataClass();
$obj = singleton($dataClass);
if(!$obj->canCreate()) {
return "";
}
$dbField = $this->getDataObjectField();
$textField = TextField::create(
sprintf(
"gridfieldaddbydbfield[%s][%s]",
$obj->ClassName,
Convert::raw2htmlatt($dbField)
)
)
->setAttribute('placeholder', $obj->fieldLabel($dbField))
->addExtraClass('no-change-track');
$addAction = new GridField_FormAction(
$gridField,
'add',
_t('GridFieldAddByDBField.Add',
'Add {name}', "Add button text",
array(
'name' => $obj->i18n_singular_name(),
)
),
'add',
'add'
);
$addAction->setAttribute('data-icon', 'add');
$forTemplate = new ArrayData(array());
$forTemplate->Fields = new ArrayList();
$forTemplate->Fields->push($textField);
$forTemplate->Fields->push($addAction);
return array(
$this->targetFragment => $forTemplate->renderWith('GridFieldAddByDBField')
);
}
}

View File

@ -1,74 +1,85 @@
<?php <?php
/** /**
* Provides a component to the {@link GridField} which tells the user whether or not a * Provides a component to the {@link GridField} which tells the user whether or not a blog post
* blog post has been published and when. * has been published and when.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk>
**/
class GridFieldBlogPostState extends GridFieldSiteTreeState { class GridFieldBlogPostState extends GridFieldSiteTreeState {
/**
* {@inheritdoc}
*/
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName) {
if($columnName == "State") { if($columnName == 'State') {
Requirements::css(BLOGGER_DIR . '/css/cms.css'); Requirements::css(BLOGGER_DIR . '/css/cms.css');
if($record->hasMethod("isPublished")) { if($record->hasMethod('isPublished')) {
$modifiedLabel = ""; $modifiedLabel = '';
if($record->isModifiedOnStage) { if($record->isModifiedOnStage) {
$modifiedLabel = "<span class='modified'>" . _t("GridFieldBlogPostState.Modified") . "</span>"; $modifiedLabel = '<span class="modified">' . _t('GridFieldBlogPostState.Modified') . '</span>';
} }
$published = $record->isPublished(); if(!$record->isPublished()) {
if(!$published) {
return _t( return _t(
"GridFieldBlogPostState.Draft", 'GridFieldBlogPostState.Draft',
'<i class="btn-icon gridfield-icon btn-icon-pencil"></i> Saved as Draft on {date}', '<i class="btn-icon gridfield-icon btn-icon-pencil"></i> Saved as Draft on {date}',
"State for when a post is saved.", 'State for when a post is saved.',
array( array(
"date" => $record->dbObject("LastEdited")->Nice() 'date' => $record->dbObject('LastEdited')->Nice(),
) )
); );
} else if (strtotime($record->PublishDate) > time()) { }
if(strtotime($record->PublishDate) > time()) {
return _t( return _t(
"GridFieldBlogPostState.Timer", 'GridFieldBlogPostState.Timer',
'<i class="gridfield-icon blog-icon-timer"></i> Publish at {date}', '<i class="gridfield-icon blog-icon-timer"></i> Publish at {date}',
"State for when a post is published.", 'State for when a post is published.',
array( array(
"date" => $record->dbObject("PublishDate")->Nice() 'date' => $record->dbObject('PublishDate')->Nice(),
)
) . $modifiedLabel;
} else {
return _t(
"GridFieldBlogPostState.Published",
'<i class="btn-icon gridfield-icon btn-icon-accept"></i> Published on {date}',
"State for when a post is published.",
array(
"date" => $record->dbObject("PublishDate")->Nice()
) )
) . $modifiedLabel; ) . $modifiedLabel;
} }
return _t(
'GridFieldBlogPostState.Published',
'<i class="btn-icon gridfield-icon btn-icon-accept"></i> Published on {date}',
'State for when a post is published.',
array(
'date' => $record->dbObject('PublishDate')->Nice(),
)
) . $modifiedLabel;
} }
} }
return '';
} }
/**
* {@inheritdoc}
*/
public function getColumnAttributes($gridField, $record, $columnName) { public function getColumnAttributes($gridField, $record, $columnName) {
if($columnName == "State") { if($columnName == 'State') {
if($record->hasMethod("isPublished")) { if($record->hasMethod('isPublished')) {
$published = $record->isPublished(); $published = $record->isPublished();
if(!$published) { if(!$published) {
$class = "gridfield-icon draft"; $class = 'gridfield-icon draft';
} else if (strtotime($record->PublishDate) > time()) { } else if(strtotime($record->PublishDate) > time()) {
$class = "gridfield-icon timer"; $class = 'gridfield-icon timer';
} else { } else {
$class = "gridfield-icon published"; $class = 'gridfield-icon published';
} }
return array("class" => $class);
return array(
'class' => $class,
);
} }
} }
return array(); return array();
} }
}
}

View File

@ -5,15 +5,15 @@
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <mstrong@silverstripe.org>
**/
class GridFieldConfig_BlogPost extends GridFieldConfig_Lumberjack { class GridFieldConfig_BlogPost extends GridFieldConfig_Lumberjack {
/**
* @param null|int $itemsPerPage
*/
public function __construct($itemsPerPage = null) { public function __construct($itemsPerPage = null) {
parent::__construct($itemsPerPage); parent::__construct($itemsPerPage);
$this->removeComponentsByType('GridFieldSiteTreeState'); $this->removeComponentsByType('GridFieldSiteTreeState');
$this->addComponent(new GridFieldBlogPostState()); $this->addComponent(new GridFieldBlogPostState());
} }
}
}

View File

@ -11,73 +11,97 @@
* @method ManyManyList Editors() List of editors * @method ManyManyList Editors() List of editors
* @method ManyManyList Writers() List of writers * @method ManyManyList Writers() List of writers
* @method ManyManyList Contributors() List of contributors * @method ManyManyList Contributors() List of contributors
* */
* @author Michael Strong <github@michaelstrong.co.uk>
**/
class Blog extends Page implements PermissionProvider { class Blog extends Page implements PermissionProvider {
/** /**
* Permission for user management * Permission for user management.
* *
* @var string * @var string
*/ */
const MANAGE_USERS = 'BLOG_MANAGE_USERS'; const MANAGE_USERS = 'BLOG_MANAGE_USERS';
/** /**
* If true, users assigned as editor, writer, or contributor will be automatically * If true, users assigned as editor, writer, or contributor will be automatically granted
* granted CMS_ACCESS_CMSMain permission. If false, only users with this permission * CMS_ACCESS_CMSMain permission. If false, only users with this permission already may be
* already may be assigned. * assigned.
*
* @config
* *
* @var boolean * @var boolean
* @config
*/ */
private static $grant_user_access = true; private static $grant_user_access = true;
/** /**
* Permission to either require, or grant to users assigned to work on this blog * Permission to either require, or grant to users assigned to work on this blog.
*
* @config
* *
* @var string * @var string
* @config
*/ */
private static $grant_user_permission = 'CMS_ACCESS_CMSMain'; private static $grant_user_permission = 'CMS_ACCESS_CMSMain';
/** /**
* Group code to assign newly granted users toh * Group code to assign newly granted users to.
*
* @config
* *
* @var string * @var string
* @config
*/ */
private static $grant_user_group = 'blog-users'; private static $grant_user_group = 'blog-users';
/**
* @var array
*/
private static $db = array( private static $db = array(
"PostsPerPage" => "Int", 'PostsPerPage' => 'Int',
); );
/**
* @var array
*/
private static $has_many = array( private static $has_many = array(
"Tags" => "BlogTag", 'Tags' => 'BlogTag',
"Categories" => "BlogCategory", 'Categories' => 'BlogCategory',
); );
/**
* @var array
*/
private static $many_many = array( private static $many_many = array(
'Editors' => 'Member', 'Editors' => 'Member',
'Writers' => 'Member', 'Writers' => 'Member',
'Contributors' => 'Member', 'Contributors' => 'Member',
); );
/**
* @var array
*/
private static $allowed_children = array( private static $allowed_children = array(
"BlogPost", 'BlogPost',
); );
/**
* @var array
*/
private static $extensions = array( private static $extensions = array(
"BlogFilter", 'BlogFilter',
); );
/**
* @var array
*/
private static $defaults = array( private static $defaults = array(
"ProvideComments" => false, 'ProvideComments' => false,
); );
/**
* @var string
*/
private static $description = 'Adds a blog to your website.'; private static $description = 'Adds a blog to your website.';
/**
* {@inheritdoc}
*/
public function getCMSFields() { public function getCMSFields() {
Requirements::css(BLOGGER_DIR . '/css/cms.css'); Requirements::css(BLOGGER_DIR . '/css/cms.css');
Requirements::javascript(BLOGGER_DIR . '/js/cms.js'); Requirements::javascript(BLOGGER_DIR . '/js/cms.js');
@ -90,183 +114,163 @@ class Blog extends Page implements PermissionProvider {
} }
$categories = GridField::create( $categories = GridField::create(
"Categories", 'Categories',
_t("Blog.Categories", "Categories"), _t('Blog.Categories', 'Categories'),
$self->Categories(), $self->Categories(),
GridFieldCategorisationConfig::create(15, $self->Categories(), 'BlogCategory', 'Categories', 'BlogPosts') GridFieldCategorisationConfig::create(15, $self->Categories(), 'BlogCategory', 'Categories', 'BlogPosts')
); );
$tags = GridField::create( $tags = GridField::create(
"Tags", 'Tags',
_t("Blog.Tags", "Tags"), _t('Blog.Tags', 'Tags'),
$self->Tags(), $self->Tags(),
GridFieldCategorisationConfig::create(15, $self->Tags(), 'BlogTag', 'Tags', 'BlogPosts') GridFieldCategorisationConfig::create(15, $self->Tags(), 'BlogTag', 'Tags', 'BlogPosts')
); );
$fields->addFieldsToTab("Root.Categorisation", array( $fields->addFieldsToTab('Root.Categorisation', array(
$categories, $categories,
$tags $tags
)); ));
$fields->findOrMakeTab("Root.Categorisation")->addExtraClass('blog-cms-categorisation');
$fields->findOrMakeTab('Root.Categorisation')->addExtraClass('blog-cms-categorisation');
}); });
$fields = parent::getCMSFields(); return parent::getCMSFields();
return $fields;
} }
/** /**
* Check if this member is an editor of the blog * {@inheritdoc}
*
* @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 the role of the given member
* Call be called via template to determine the current user
*
* E.g. `Hello $RoleOf($CurrentMember.ID)`
*
* @param Member|integer $member
*
* @return string|null Author, Editor, Writer, Contributor, or null if no role
*/
public function RoleOf($member) {
if(is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
if(!$member) return null;
// Check each role
if($this->isEditor($member)) return _t('Blog.EDITOR', 'Editor');
if($this->isWriter($member)) return _t('Blog.WRITER', 'Writer');
if($this->isContributor($member)) return _t('Blog.CONTRIBUTOR', 'Contributor');
}
/**
* 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) { public function canEdit($member = null) {
$member = $member ?: Member::currentUser(); $member = $this->getMember($member);
if(is_numeric($member)) $member = Member::get()->byID($member);
// Allow editors to edit this page if($this->isEditor($member)) {
if($this->isEditor($member)) return true; return true;
}
return parent::canEdit($member); return parent::canEdit($member);
} }
/** /**
* Determine if this user can edit the editors list * @param null|int|Member $member
* *
* @param Member $member * @return null|Member
*
* @return boolean
*/ */
public function canEditEditors($member = null) { protected function getMember($member = null) {
$member = $member ?: Member::currentUser(); if(!$member) {
$member = Member::currentUser();
}
$extended = $this->extendedCan('canEditEditors', $member); if(is_int($member)) {
if($extended !== null) return $extended; $member = Member::get()->byID($member);
}
// Check permission return $member;
return Permission::checkMember($member, self::MANAGE_USERS);
} }
/** /**
* Determine if this user can edit writers list * Check if this member is an editor of the blog.
* *
* @param Member $member * @param Member $member
* *
* @return boolean * @return bool
*/ */
public function canEditWriters($member = null) { public function isEditor($member) {
$member = $member ?: Member::currentUser(); $isEditor = $this->isMemberOf($member, $this->Editors());
if(is_numeric($member)) $member = Member::get()->byID($member); $this->extend('updateIsEditor', $isEditor, $member);
$extended = $this->extendedCan('canEditWriters', $member); return $isEditor;
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 * Determine if the given member belongs to the given relation.
* *
* @param type $member * @param Member $member
* @param DataList $relation
* *
* @return boolean * @return bool
*/ */
public function canEditContributors($member = null) { protected function isMemberOf($member, $relation) {
$member = $member ?: Member::currentUser(); if(!$member || !$member->exists()) {
if(is_numeric($member)) $member = Member::get()->byID($member); return false;
}
$extended = $this->extendedCan('canEditContributors', $member); if($relation instanceof UnsavedRelationList) {
if($extended !== null) return $extended; return in_array($member->ID, $relation->getIDList());
}
// Editors can edit writers return $relation->byID($member->ID) !== null;
if($this->isEditor($member)) return true;
// Check permission
return Permission::checkMember($member, self::MANAGE_USERS);
} }
/**
* Determine the role of the given member.
*
* Call be called via template to determine the current user.
*
* @example "Hello $RoleOf($CurrentMember.ID)"
*
* @param int|Member $member
*
* @return null|string
*/
public function RoleOf($member) {
if(is_numeric($member)) {
$member = DataObject::get_by_id('Member', $member);
}
if(!$member) {
return null;
}
if($this->isEditor($member)) {
return _t('Blog.EDITOR', 'Editor');
}
if($this->isWriter($member)) {
return _t('Blog.WRITER', 'Writer');
}
if($this->isContributor($member)) {
return _t('Blog.CONTRIBUTOR', 'Contributor');
}
return null;
}
/**
* Check if this member is a writer of the blog.
*
* @param Member $member
*
* @return bool
*/
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 bool
*/
public function isContributor($member) {
$isContributor = $this->isMemberOf($member, $this->Contributors());
$this->extend('updateIsContributor', $isContributor, $member);
return $isContributor;
}
/**
* {@inheritdoc}
*/
public function canAddChildren($member = null) { public function canAddChildren($member = null) {
$member = $member ?: Member::currentUser(); $member = $this->getMember($member);
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)) { if($this->isEditor($member) || $this->isWriter($member) || $this->isContributor($member)) {
return true; return true;
} }
@ -274,17 +278,18 @@ class Blog extends Page implements PermissionProvider {
return parent::canAddChildren($member); return parent::canAddChildren($member);
} }
/**
* {@inheritdoc}
*/
public function getSettingsFields() { public function getSettingsFields() {
$fields = parent::getSettingsFields(); $fields = parent::getSettingsFields();
$fields->addFieldToTab("Root.Settings",
NumericField::create("PostsPerPage", _t("Blog.PostsPerPage", "Posts Per Page")) $fields->addFieldToTab('Root.Settings',
NumericField::create('PostsPerPage', _t('Blog.PostsPerPage', 'Posts Per Page'))
); );
// Roles and Permissions
$members = $this->getCandidateUsers()->map()->toArray(); $members = $this->getCandidateUsers()->map()->toArray();
// Editors
$editorField = ListboxField::create('Editors', 'Editors', $members) $editorField = ListboxField::create('Editors', 'Editors', $members)
->setMultiple(true) ->setMultiple(true)
->setRightTitle('<a class="toggle-description">help</a>') ->setRightTitle('<a class="toggle-description">help</a>')
@ -304,7 +309,6 @@ class Blog extends Page implements PermissionProvider {
$editorField = $editorField->performDisabledTransformation(); $editorField = $editorField->performDisabledTransformation();
} }
// Writers
$writerField = ListboxField::create('Writers', 'Writers', $members) $writerField = ListboxField::create('Writers', 'Writers', $members)
->setMultiple(true) ->setMultiple(true)
->setRightTitle('<a class="toggle-description">help</a>') ->setRightTitle('<a class="toggle-description">help</a>')
@ -321,7 +325,6 @@ class Blog extends Page implements PermissionProvider {
$writerField = $writerField->performDisabledTransformation(); $writerField = $writerField->performDisabledTransformation();
} }
// Contributors
$contributorField = ListboxField::create('Contributors', 'Contributors', $members) $contributorField = ListboxField::create('Contributors', 'Contributors', $members)
->setMultiple(true) ->setMultiple(true)
->setRightTitle('<a class="toggle-description">help</a>') ->setRightTitle('<a class="toggle-description">help</a>')
@ -346,62 +349,145 @@ class Blog extends Page implements PermissionProvider {
return $fields; return $fields;
} }
/** /**
* Return blog posts * Gets the list of user candidates to be assigned to assist with this blog.
* *
* @return DataList of BlogPost objects * @return SS_List
**/ */
public function getBlogPosts() { protected function getCandidateUsers() {
$blogPosts = BlogPost::get()->filter("ParentID", $this->ID); if($this->config()->grant_user_access) {
//Allow decorators to manipulate list return Member::get();
$this->extend('updateGetBlogPosts', $blogPosts); } else {
return $blogPosts; return Permission::get_members_by_permission(
$this->config()->grant_user_permission
);
}
} }
/**
* Determine if this user can edit the editors list.
*
* @param int|Member $member
*
* @return bool
*/
public function canEditEditors($member = null) {
$member = $this->getMember($member);
$extended = $this->extendedCan('canEditEditors', $member);
if($extended !== null) {
return $extended;
}
return Permission::checkMember($member, self::MANAGE_USERS);
}
/** /**
* Returns blogs posts for a given date period. * Determine if this user can edit writers list.
* *
* @param $year int * @param int|Member $member
* @param $month int *
* @param $day int * @return boolean
*/
public function canEditWriters($member = null) {
$member = $this->getMember($member);
$extended = $this->extendedCan('canEditWriters', $member);
if($extended !== null) {
return $extended;
}
if($this->isEditor($member)) {
return true;
}
return Permission::checkMember($member, self::MANAGE_USERS);
}
/**
* Determines if this user can edit the contributors list.
*
* @param int|Member $member
*
* @return boolean
*/
public function canEditContributors($member = null) {
$member = $this->getMember($member);
$extended = $this->extendedCan('canEditContributors', $member);
if($extended !== null) {
return $extended;
}
if($this->isEditor($member)) {
return true;
}
return Permission::checkMember($member, self::MANAGE_USERS);
}
/**
* Returns BlogPosts for a given date period.
*
* @param int $year
* @param null|int $month
* @param null|int $day
* *
* @return DataList * @return DataList
**/ */
public function getArchivedBlogPosts($year, $month = null, $day = null) { public function getArchivedBlogPosts($year, $month = null, $day = null) {
$query = $this->getBlogPosts()->dataQuery(); $query = $this->getBlogPosts()->dataQuery();
$stage = $query->getQueryParam("Versioned.stage"); $stage = $query->getQueryParam('Versioned.stage');
if($stage) $stage = '_' . Convert::raw2sql($stage);
if ($stage) {
$stage = '_' . $stage;
}
$query->innerJoin('BlogPost', sprintf('SiteTree%s.ID = BlogPost%s.ID', $stage, $stage));
$query->where(sprintf('YEAR(PublishDate) = \'%s\'', Convert::raw2sql($year)));
$query->innerJoin("BlogPost", "`SiteTree" . $stage . "`.`ID` = `BlogPost" . $stage . "`.`ID`");
$query->where("YEAR(PublishDate) = '" . Convert::raw2sql($year) . "'");
if($month) { if($month) {
$query->where("MONTH(PublishDate) = '" . Convert::raw2sql($month) . "'"); $query->where(sprintf('MONTH(PublishDate) = \'%s\'', Convert::raw2sql($month)));
if($day) { if($day) {
$query->where("DAY(PublishDate) = '" . Convert::raw2sql($day) . "'"); $query->where(sprintf('DAY(PublishDate) = \'%s\'', Convert::raw2sql($day)));
} }
} }
return $this->getBlogPosts()->setDataQuery($query); return $this->getBlogPosts()->setDataQuery($query);
} }
/**
* Return blog posts.
*
* @return DataList of BlogPost objects
*/
public function getBlogPosts() {
$blogPosts = BlogPost::get()->filter('ParentID', $this->ID);
$this->extend('updateGetBlogPosts', $blogPosts);
return $blogPosts;
}
/** /**
* Get a link to a Member profile. * Get a link to a Member profile.
* *
* @param urlSegment * @param string $urlSegment
* *
* @return String * @return string
*/ */
public function ProfileLink($urlSegment) { public function ProfileLink($urlSegment) {
return Controller::join_links($this->Link(), 'profile', $urlSegment); return Controller::join_links($this->Link(), 'profile', $urlSegment);
} }
/** /**
* This sets the title for our gridfield * This sets the title for our gridfield.
* *
* @return string * @return string
*/ */
@ -409,7 +495,6 @@ class Blog extends Page implements PermissionProvider {
return _t('Blog.LumberjackTitle', 'Blog Posts'); return _t('Blog.LumberjackTitle', 'Blog Posts');
} }
/** /**
* This overwrites lumberjacks default gridfield config. * This overwrites lumberjacks default gridfield config.
* *
@ -419,6 +504,9 @@ class Blog extends Page implements PermissionProvider {
return GridFieldConfig_BlogPost::create(); return GridFieldConfig_BlogPost::create();
} }
/**
* {@inheritdoc}
*/
public function providePermissions() { public function providePermissions() {
return array( return array(
Blog::MANAGE_USERS => array( Blog::MANAGE_USERS => array(
@ -437,112 +525,109 @@ class Blog extends Page implements PermissionProvider {
} }
/** /**
* Gets the list of user candidates to be assigned to assist with this blog * {@inheritdoc}
*
* @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() { protected function onBeforeWrite() {
parent::onBeforeWrite(); parent::onBeforeWrite();
$this->assignGroup(); $this->assignGroup();
} }
/** /**
* Assign users as necessary to the blog group * Assign users as necessary to the blog group.
*/ */
protected function assignGroup() { protected function assignGroup() {
// Ensure that any user granted editor, writer, or contributor have CMS_ACCESS_CMSMain access if(!$this->config()->grant_user_access) {
if(!$this->config()->grant_user_access) return; return;
}
// Generate or retrieve the group
$group = $this->getUserGroup(); $group = $this->getUserGroup();
foreach(array($this->Editors(), $this->Writers(), $this->Contributors()) as $userlist) {
foreach($userlist as $user) { foreach(array($this->Editors(), $this->Writers(), $this->Contributors()) as $levels) {
// Ensure user exists in the group foreach($levels as $user) {
if(!$user->inGroup($group)) $user->Groups()->add($group); if(!$user->inGroup($group)) {
$user->Groups()->add($group);
}
} }
} }
} }
/**
* 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;
}
$group = new Group();
$group->Title = 'Blog users';
$group->Code = $code;
$group->write();
$permission = new Permission();
$permission->Code = $this->config()->grant_user_permission;
$group->Permissions()->add($permission);
return $group;
}
} }
/** /**
* Blog Controller
*
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk>
**/
class Blog_Controller extends Page_Controller { class Blog_Controller extends Page_Controller {
/**
* @var array
*/
private static $allowed_actions = array( private static $allowed_actions = array(
'archive', 'archive',
'tag', 'tag',
'category', 'category',
'rss', 'rss',
'profile' 'profile',
); );
/**
* @var array
*/
private static $url_handlers = array( private static $url_handlers = array(
'tag/$Tag!' => 'tag', 'tag/$Tag!' => 'tag',
'category/$Category!' => 'category', 'category/$Category!' => 'category',
'archive/$Year!/$Month/$Day' => 'archive', 'archive/$Year!/$Month/$Day' => 'archive',
'profile/$URLSegment!' => 'profile' 'profile/$URLSegment!' => 'profile',
); );
/**
* @var array
*/
private static $casting = array( private static $casting = array(
'MetaTitle' => 'Text', 'MetaTitle' => 'Text',
'FilterDescription' => 'Text' 'FilterDescription' => 'Text',
); );
/** /**
* The current Blog Post DataList query. * The current Blog Post DataList query.
* *
* @var DataList * @var DataList
**/ */
protected $blogPosts; protected $blogPosts;
/**
* @return string
*/
public function index() { public function index() {
$this->blogPosts = $this->getBlogPosts(); $this->blogPosts = $this->getBlogPosts();
return $this->render(); return $this->render();
} }
@ -550,7 +635,7 @@ class Blog_Controller extends Page_Controller {
* Renders a Blog Member's profile. * Renders a Blog Member's profile.
* *
* @return SS_HTTPResponse * @return SS_HTTPResponse
**/ */
public function profile() { public function profile() {
$profile = $this->getCurrentProfile(); $profile = $this->getCurrentProfile();
@ -566,8 +651,8 @@ class Blog_Controller extends Page_Controller {
/** /**
* Get the Member associated with the current URL segment. * Get the Member associated with the current URL segment.
* *
* @return Member|null * @return null|Member
**/ */
public function getCurrentProfile() { public function getCurrentProfile() {
$urlSegment = $this->request->param('URLSegment'); $urlSegment = $this->request->param('URLSegment');
@ -581,10 +666,10 @@ class Blog_Controller extends Page_Controller {
} }
/** /**
* Get posts related to the current Member profile * Get posts related to the current Member profile.
* *
* @return DataList|null * @return null|DataList
**/ */
public function getCurrentProfilePosts() { public function getCurrentProfilePosts() {
$profile = $this->getCurrentProfile(); $profile = $this->getCurrentProfile();
@ -596,279 +681,297 @@ class Blog_Controller extends Page_Controller {
} }
/** /**
* Renders an archive for a specificed date. This can be by year or year/month * Renders an archive for a specified date. This can be by year or year/month.
* *
* @return SS_HTTPResponse * @return SS_HTTPResponse
**/ */
public function archive() { public function archive() {
$year = $this->getArchiveYear(); $year = $this->getArchiveYear();
$month = $this->getArchiveMonth(); $month = $this->getArchiveMonth();
$day = $this->getArchiveDay(); $day = $this->getArchiveDay();
// If an invalid month has been passed, we can return a 404. if($this->request->param('Month') && !$month) {
if($this->request->param("Month") && !$month) { return $this->httpError(404, 'Not Found');
return $this->httpError(404, "Not Found");
} }
// Check for valid day if($month && $this->request->param('Day') && !$day) {
if($month && $this->request->param("Day") && !$day) { return $this->httpError(404, 'Not Found');
return $this->httpError(404, "Not Found");
} }
if($year) { if($year) {
$this->blogPosts = $this->getArchivedBlogPosts($year, $month, $day); $this->blogPosts = $this->getArchivedBlogPosts($year, $month, $day);
return $this->render(); return $this->render();
} }
return $this->httpError(404, "Not Found");
return $this->httpError(404, 'Not Found');
} }
/**
* Fetches the archive year from the url.
*
* @return null|int
*/
public function getArchiveYear() {
$year = $this->request->param('Year');
if(preg_match('/^[0-9]{4}$/', $year)) {
return (int) $year;
}
return null;
}
/**
* Fetches the archive money from the url.
*
* @return null|int
*/
public function getArchiveMonth() {
$month = $this->request->param('Month');
if(preg_match('/^[0-9]{1,2}$/', $month)) {
if($month > 0 && $month < 13) {
if(checkdate($month, 01, $this->getArchiveYear())) {
return (int) $month;
}
}
}
return null;
}
/**
* Fetches the archive day from the url.
*
* @return null|int
*/
public function getArchiveDay() {
$day = $this->request->param('Day');
if(preg_match('/^[0-9]{1,2}$/', $day)) {
if(checkdate($this->getArchiveMonth(), $day, $this->getArchiveYear())) {
return (int) $day;
}
}
return null;
}
/** /**
* Renders the blog posts for a given tag. * Renders the blog posts for a given tag.
* *
* @return SS_HTTPResponse * @return SS_HTTPResponse
**/ */
public function tag() { public function tag() {
$tag = $this->getCurrentTag(); $tag = $this->getCurrentTag();
if($tag) { if($tag) {
$this->blogPosts = $tag->BlogPosts(); $this->blogPosts = $tag->BlogPosts();
return $this->render(); return $this->render();
} }
return $this->httpError(404, "Not Found");
return $this->httpError(404, 'Not Found');
} }
/**
* Tag Getter for use in templates.
*
* @return null|BlogTag
*/
public function getCurrentTag() {
$tag = $this->request->param('Tag');
if($tag) {
return $this->dataRecord->Tags()
->filter('URLSegment', $tag)
->first();
}
return null;
}
/** /**
* Renders the blog posts for a given category * Renders the blog posts for a given category.
* *
* @return SS_HTTPResponse * @return SS_HTTPResponse
**/ */
public function category() { public function category() {
$category = $this->getCurrentCategory(); $category = $this->getCurrentCategory();
if($category) { if($category) {
$this->blogPosts = $category->BlogPosts(); $this->blogPosts = $category->BlogPosts();
return $this->render(); return $this->render();
} }
return $this->httpError(404, "Not Found");
return $this->httpError(404, 'Not Found');
} }
/** /**
* Get the meta title for the current action * Category Getter for use in templates.
*
* @return null|BlogCategory
*/
public function getCurrentCategory() {
$category = $this->request->param('Category');
if($category) {
return $this->dataRecord->Categories()
->filter('URLSegment', $category)
->first();
}
return null;
}
/**
* Get the meta title for the current action.
* *
* @return string * @return string
*/ */
public function getMetaTitle() { public function getMetaTitle() {
$title = $this->data()->getTitle(); $title = $this->data()->getTitle();
$filter = $this->getFilterDescription(); $filter = $this->getFilterDescription();
if($filter) { if($filter) {
$title = "{$title} - {$filter}"; $title = sprintf('%s - $s', $title, $filter);
} }
$this->extend('updateMetaTitle', $title); $this->extend('updateMetaTitle', $title);
return $title; return $title;
} }
/** /**
* Returns a description of the current filter * Returns a description of the current filter.
* *
* @return string * @return string
*/ */
public function getFilterDescription() { public function getFilterDescription() {
$items = array(); $items = array();
// Check current page
$list = $this->PaginatedList(); $list = $this->PaginatedList();
$currentPage = $list->CurrentPage(); $currentPage = $list->CurrentPage();
// Build title
if($currentPage > 1) { if($currentPage > 1) {
$items[] = _t( $items[] = _t(
'Blog.FILTERDESCRIPTION_PAGE', 'Blog.FILTERDESCRIPTION_PAGE',
"Page {page}", 'Page {page}',
null, null,
array('page' => $currentPage) array(
'page' => $currentPage,
)
); );
} }
// Check author
if($author = $this->getCurrentProfile()) { if($author = $this->getCurrentProfile()) {
$items[] = _t( $items[] = _t(
'Blog.FILTERDESCRIPTION_AUTHOR', 'Blog.FILTERDESCRIPTION_AUTHOR',
"By {author}", 'By {author}',
null, null,
array('author' => $author->Title) array(
'author' => $author->Title,
)
); );
} }
// Check tag
if($tag = $this->getCurrentTag()) { if($tag = $this->getCurrentTag()) {
$items[] = _t( $items[] = _t(
'Blog.FILTERDESCRIPTION_TAG', 'Blog.FILTERDESCRIPTION_TAG',
"Tagged with {tag}", 'Tagged with {tag}',
null, null,
array('tag' => $tag->Title) array(
'tag' => $tag->Title,
)
); );
} }
// Check category
if($category = $this->getCurrentCategory()) { if($category = $this->getCurrentCategory()) {
$items[] = _t( $items[] = _t(
'Blog.FILTERDESCRIPTION_CATEGORY', 'Blog.FILTERDESCRIPTION_CATEGORY',
"In category {category}", 'In category {category}',
null, null,
array('category' => $category->Title) array(
'category' => $category->Title,
)
); );
} }
// Check archive
if($this->owner->getArchiveYear()) { if($this->owner->getArchiveYear()) {
if($this->owner->getArchiveDay()) { if($this->owner->getArchiveDay()) {
$date = $this->owner->getArchiveDate()->Nice(); $date = $this->owner->getArchiveDate()->Nice();
} elseif($this->owner->getArchiveMonth()) { } elseif($this->owner->getArchiveMonth()) {
$date = $this->owner->getArchiveDate()->format("F, Y"); $date = $this->owner->getArchiveDate()->format('F, Y');
} else { } else {
$date = $this->owner->getArchiveDate()->format("Y"); $date = $this->owner->getArchiveDate()->format('Y');
} }
$items[] = _t( $items[] = _t(
'Blog.FILTERDESCRIPTION_DATE', 'Blog.FILTERDESCRIPTION_DATE',
"In {date}", 'In {date}',
null, null,
array('date' => $date) array(
'date' => $date,
)
); );
} }
$result = ''; $result = '';
if($items) $result = implode(', ', $items);
// Allow extension if($items) {
$result = implode(', ', $items);
}
$this->extend('updateFilterDescription', $result); $this->extend('updateFilterDescription', $result);
return $result; return $result;
} }
/** /**
* Displays an RSS feed of blog posts * Returns a list of paginated blog posts based on the BlogPost dataList.
*
* @return string HTML
**/
public function rss() {
$rss = new RSSFeed($this->getBlogPosts(), $this->Link(), $this->MetaTitle, $this->MetaDescription);
$this->extend('updateRss', $rss);
return $rss->outputToBrowser();
}
/**
* Returns a list of paginated blog posts based on the blogPost dataList
* *
* @return PaginatedList * @return PaginatedList
**/ */
public function PaginatedList() { public function PaginatedList() {
$posts = new PaginatedList($this->blogPosts); $posts = new PaginatedList($this->blogPosts);
// If pagination is set to '0' then no pagination will be shown.
if($this->PostsPerPage > 0) { if($this->PostsPerPage > 0) {
$posts->setPageLength($this->PostsPerPage); $posts->setPageLength($this->PostsPerPage);
} else { } else {
$pageSize = $this->getBlogPosts()->count() ?: 99999; $pageSize = 99999;
if($count = $this->getBlogPosts()->count()) {
$pageSize = $count;
}
$posts->setPageLength($pageSize); $posts->setPageLength($pageSize);
} }
$start = $this->request->getVar($posts->getPaginationGetVar()); $start = $this->request->getVar($posts->getPaginationGetVar());
$posts->setPageStart($start); $posts->setPageStart($start);
return $posts; return $posts;
} }
/** /**
* Tag Getter for use in templates. * Displays an RSS feed of blog posts.
* *
* @return BlogTag|null * @return string
**/ */
public function getCurrentTag() { public function rss() {
$tag = $this->request->param("Tag"); $rss = new RSSFeed($this->getBlogPosts(), $this->Link(), $this->MetaTitle, $this->MetaDescription);
if($tag) {
return $this->dataRecord->Tags() $this->extend('updateRss', $rss);
->filter("URLSegment", $tag)
->first(); return $rss->outputToBrowser();
}
return null;
} }
/**
* Category Getter for use in templates.
*
* @return BlogCategory|null
**/
public function getCurrentCategory() {
$category = $this->request->param("Category");
if($category) {
return $this->dataRecord->Categories()
->filter("URLSegment", $category)
->first();
}
return null;
}
/**
* Fetches the archive year from the url
*
* @return int|null
**/
public function getArchiveYear() {
$year = $this->request->param("Year");
if(preg_match("/^[0-9]{4}$/", $year)) {
return (int) $year;
}
return null;
}
/**
* Fetches the archive money from the url.
*
* @return int|null
**/
public function getArchiveMonth() {
$month = $this->request->param("Month");
if(preg_match("/^[0-9]{1,2}$/", $month)) {
if($month > 0 && $month < 13) {
// Check that we have a valid date.
if(checkdate($month, 01, $this->getArchiveYear())) {
return (int) $month;
}
}
}
return null;
}
/**
* Fetches the archive day from the url
*
* @return int|null
**/
public function getArchiveDay() {
$day = $this->request->param("Day");
if(preg_match("/^[0-9]{1,2}$/", $day)) {
// Check that we have a valid date
if(checkdate($this->getArchiveMonth(), $day, $this->getArchiveYear())) {
return (int) $day;
}
}
return null;
}
/** /**
* Returns the current archive date. * Returns the current archive date.
* *
* @return Date * @return null|Date
**/ */
public function getArchiveDate() { public function getArchiveDate() {
$year = $this->getArchiveYear(); $year = $this->getArchiveYear();
$month = $this->getArchiveMonth(); $month = $this->getArchiveMonth();
@ -876,25 +979,27 @@ class Blog_Controller extends Page_Controller {
if($year) { if($year) {
if($month) { if($month) {
$date = $year . '-' . $month . '-01'; $date = sprintf('%s-%s-01', $year, $month);
if($day) { if($day) {
$date = $year . '-' . $month . '-' . $day; $date = sprintf('%s-%s-%s', $year, $month, $day);
} }
} else { } else {
$date = $year . '-01-01'; $date = sprintf('%s-01-01', $year);
} }
return DBField::create_field("Date", $date);
}
}
return DBField::create_field('Date', $date);
}
return null;
}
/** /**
* Returns a link to the RSS feed. * Returns a link to the RSS feed.
* *
* @return string URL * @return string
**/ */
public function getRSSLink() { public function getRSSLink() {
return $this->Link("rss"); return $this->Link('rss');
} }
} }

View File

@ -1,127 +1,135 @@
<?php <?php
/** /**
* A blog category fo generalising blog posts. * A blog category for generalising blog posts.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* *
* @author Michael Strong <github@michaelstrong.co.uk> * @method Blog Blog()
**/ * @method ManyManyList BlogPosts()
class BlogCategory extends DataObject { *
* @property string $URLSegment
* @property int $BlogID
*/
class BlogCategory extends DataObject implements CategorisationObject {
/**
* @var array
*/
private static $db = array( private static $db = array(
"Title" => "Varchar(255)", 'Title' => 'Varchar(255)',
); );
/**
* @var array
*/
private static $has_one = array( private static $has_one = array(
"Blog" => "Blog", 'Blog' => 'Blog',
); );
/**
* @var array
*/
private static $belongs_many_many = array( private static $belongs_many_many = array(
"BlogPosts" => "BlogPost", 'BlogPosts' => 'BlogPost',
); );
/**
* @var array
*/
private static $extensions = array( private static $extensions = array(
"URLSegmentExtension", 'URLSegmentExtension',
); );
/**
* {@inheritdoc}
*/
public function getCMSFields() { public function getCMSFields() {
$fields = new FieldList( $fields = new FieldList(
TextField::create("Title", _t("BlogCategory.Title", "Title")) TextField::create('Title', _t('BlogCategory.Title', 'Title'))
); );
$this->extend("updateCMSFields", $fields);
$this->extend('updateCMSFields', $fields);
return $fields; return $fields;
} }
/** /**
* Returns a relative link to this category. * Returns a relative link to this category.
* *
* @return string URL * @return string
**/ */
public function getLink() { public function getLink() {
return Controller::join_links($this->Blog()->Link(), "category", $this->URLSegment); return Controller::join_links($this->Blog()->Link(), 'category', $this->URLSegment);
} }
/** /**
* Inherits from the parent blog or can be overwritten using a DataExtension * Inherits from the parent blog or can be overwritten using a DataExtension.
* *
* @param $member Member * @param null|Member $member
* *
* @return boolean * @return bool
*/ */
public function canView($member = null) { public function canView($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member); $extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) { if($extended !== null) {
return $extended; return $extended;
} }
return $this->Blog()->canView($member); return $this->Blog()->canView($member);
} }
/** /**
* Inherits from the parent blog or can be overwritten using a DataExtension * Inherits from the parent blog or can be overwritten using a DataExtension.
* *
* @param $member Member * @param null|Member $member
* *
* @return boolean * @return bool
*/ */
public function canCreate($member = null) { public function canCreate($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member); $extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) { if($extended !== null) {
return $extended; return $extended;
} }
// Since there is no parent yet, need to make a best guess
$permission = Blog::config()->grant_user_permission; $permission = Blog::config()->grant_user_permission;
return Permission::checkMember($member, $permission); return Permission::checkMember($member, $permission);
} }
/** /**
* Inherits from the parent blog or can be overwritten using a DataExtension * Inherits from the parent blog or can be overwritten using a DataExtension.
* *
* @param $member Member * @param null|Member $member
* *
* @return boolean * @return bool
*/ */
public function canDelete($member = null) { public function canDelete($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member); $extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) { if($extended !== null) {
return $extended; return $extended;
} }
return $this->Blog()->canEdit($member); return $this->Blog()->canEdit($member);
} }
/** /**
* Inherits from the parent blog or can be overwritten using a DataExtension * Inherits from the parent blog or can be overwritten using a DataExtension.
* *
* @param $member Member * @param null|Member $member
* *
* @return boolean * @return bool
*/ */
public function canEdit($member = null) { public function canEdit($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member); $extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) { if($extended !== null) {
return $extended; return $extended;
} }
return $this->Blog()->canEdit($member); return $this->Blog()->canEdit($member);
} }
} }

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* An indivisual blog post. * An individual blog post.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
@ -9,74 +9,145 @@
* @method ManyManyList Categories() * @method ManyManyList Categories()
* @method ManyManyList Tags() * @method ManyManyList Tags()
* @method ManyManyList Authors() * @method ManyManyList Authors()
* @method Blog Parent()
* *
* @author Michael Strong <github@michaelstrong.co.uk> * @property string $PublishDate
**/ * @property string $AuthorNames
* @property int $ParentID
*/
class BlogPost extends Page { class BlogPost extends Page {
/**
* @var array
*/
private static $db = array( private static $db = array(
"PublishDate" => "SS_Datetime", 'PublishDate' => 'SS_Datetime',
"AuthorNames" => "Varchar(1024)", 'AuthorNames' => 'Varchar(1024)',
"Summary" => "HTMLText", 'Summary' => 'HTMLText',
); );
/**
* @var array
*/
private static $has_one = array( private static $has_one = array(
"FeaturedImage" => "Image", 'FeaturedImage' => 'Image',
); );
/**
* @var array
*/
private static $many_many = array( private static $many_many = array(
"Categories" => "BlogCategory", 'Categories' => 'BlogCategory',
"Tags" => "BlogTag", 'Tags' => 'BlogTag',
"Authors" => "Member", 'Authors' => 'Member',
); );
/**
* @var array
*/
private static $defaults = array( private static $defaults = array(
"ShowInMenus" => false, 'ShowInMenus' => false,
"InheritSideBar" => true, // Support for widgets 'InheritSideBar' => true,
"ProvideComments" => true, // Support for comments 'ProvideComments' => true,
); );
/**
* @var array
*/
private static $extensions = array( private static $extensions = array(
"BlogPostFilter", 'BlogPostFilter',
); );
/**
* @var array
*/
private static $searchable_fields = array( private static $searchable_fields = array(
"Title", 'Title',
); );
/**
* @var array
*/
private static $summary_fields = array( private static $summary_fields = array(
"Title", 'Title',
); );
/**
* @var array
*/
private static $casting = array( private static $casting = array(
'Excerpt' => 'Text' 'Excerpt' => 'Text',
); );
/**
* @var array
*/
private static $allowed_children = array(); private static $allowed_children = array();
private static $default_sort = "PublishDate DESC"; /**
* @var string
*/
private static $default_sort = 'PublishDate DESC';
/**
* @var bool
*/
private static $can_be_root = false; private static $can_be_root = false;
/** /**
* This will display or hide the current class from the SiteTree. This * This will display or hide the current class from the SiteTree. This variable can be
* variable can be configured using YAML. * configured using YAML.
* *
* @var boolean * @var bool
**/ */
private static $show_in_sitetree = false; private static $show_in_sitetree = false;
/**
* Determine the role of the given member.
*
* Call be called via template to determine the current user.
*
* @example "Hello $RoleOf($CurrentMember.ID)"
*
* @param null|int|Member $member
*
* @return null|string
*/
public function RoleOf($member = null) {
if(is_int($member)) {
$member = DataObject::get_by_id('Member', $member);
}
if(!$member) {
return null;
}
if($this->isAuthor($member)) {
return _t('BlogPost.AUTHOR', 'Author');
}
$parent = $this->Parent();
if($parent instanceof Blog) {
return $parent->RoleOf($member);
}
return null;
}
/** /**
* Determine if the given member is an author of this post * Determine if the given member is an author of this post.
* *
* @param Member $member * @param null|Member $member
* @return boolean *
* @return bool
*/ */
public function isAuthor($member) { public function isAuthor($member = null) {
if(!$member || !$member->exists()) return false; if(!$member || !$member->exists()) {
return false;
}
$list = $this->Authors(); $list = $this->Authors();
if($list instanceof UnsavedRelationList) { if($list instanceof UnsavedRelationList) {
return in_array($member->ID, $list->getIDList()); return in_array($member->ID, $list->getIDList());
} }
@ -85,92 +156,80 @@ class BlogPost extends Page {
} }
/** /**
* Determine the role of the given member * {@inheritdoc}
* Call be called via template to determine the current user
*
* E.g. `Hello $RoleOf($CurrentMember.ID)`
*
* @param Member|integer $member
* @return string|null Author, Editor, Writer, Contributor, or null if no role
*/ */
public function RoleOf($member) {
if(is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
if(!$member) return null;
// Check if this member is an author
if($this->isAuthor($member)) return _t("BlogPost.AUTHOR", "Author");
// Check parent role
$parent = $this->Parent();
if($parent instanceof Blog) return $parent->RoleOf($member);
}
public function getCMSFields() { public function getCMSFields() {
Requirements::css(BLOGGER_DIR . '/css/cms.css'); Requirements::css(BLOGGER_DIR . '/css/cms.css');
Requirements::javascript(BLOGGER_DIR . '/js/cms.js'); Requirements::javascript(BLOGGER_DIR . '/js/cms.js');
$self =& $this; $self =& $this;
$this->beforeUpdateCMSFields(function($fields) use ($self) {
// Add blog summary $this->beforeUpdateCMSFields(function ($fields) use ($self) {
/**
* @var FieldList $fields
*/
$summary = HtmlEditorField::create('Summary', false);
$summary->setRows(5);
$summary->setDescription(_t(
'BlogPost.SUMMARY_DESCRIPTION',
'If no summary is specified the first 30 words will be used.'
));
$summaryHolder = ToggleCompositeField::create( $summaryHolder = ToggleCompositeField::create(
'CustomSummary', 'CustomSummary',
_t('BlogPost.CUSTOMSUMMARY', 'Add A Custom Summary'), _t('BlogPost.CUSTOMSUMMARY', 'Add A Custom Summary'),
array( array(
$summary = HtmlEditorField::create("Summary", false) $summary,
) )
) );
->setHeadingLevel(4) $summaryHolder->setHeadingLevel(4);
->addExtraClass('custom-summary'); $summaryHolder->addExtraClass('custom-summary');
$summary->setRows(5);
$summary->setDescription(_t(
'BlogPost.SUMMARY_DESCRIPTION',
"If no summary is specified the first 30 words will be used."
));
$fields->insertBefore($summaryHolder, 'Content'); $fields->insertBefore($summaryHolder, 'Content');
// Add featured image $uploadField = UploadField::create('FeaturedImage', _t('BlogPost.FeaturedImage', 'Featured Image'));
$fields->insertAfter(
$uploadField = UploadField::create("FeaturedImage", _t("BlogPost.FeaturedImage", "Featured Image")),
"Content"
);
$uploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif')); $uploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
// We're going to hide MenuTitle - Its not needed in blog posts. $fields->insertAfter(
$uploadField,
'Content'
);
$fields->push(HiddenField::create('MenuTitle')); $fields->push(HiddenField::create('MenuTitle'));
// We're going to add the url segment to sidebar so we're making it a little lighter
$urlSegment = $fields->dataFieldByName('URLSegment'); $urlSegment = $fields->dataFieldByName('URLSegment');
$urlSegment->setURLPrefix($self->Parent()->RelativeLink()); $urlSegment->setURLPrefix($self->Parent()->RelativeLink());
// Remove the MenuTitle and URLSegment from the main tab
$fields->removeFieldsFromTab('Root.Main', array( $fields->removeFieldsFromTab('Root.Main', array(
'MenuTitle', 'MenuTitle',
'URLSegment', 'URLSegment',
)); ));
// Author field
$authorField = ListboxField::create( $authorField = ListboxField::create(
"Authors", 'Authors',
_t("BlogPost.Authors", "Authors"), _t('BlogPost.Authors', 'Authors'),
Member::get()->map()->toArray() Member::get()->map()->toArray()
)->setMultiple(true); )->setMultiple(true);
$authorNames = TextField::create( $authorNames = TextField::create(
"AuthorNames", 'AuthorNames',
_t("BlogPost.AdditionalCredits", "Additional Credits"), _t('BlogPost.AdditionalCredits', 'Additional Credits'),
null, null,
1024 1024
)->setDescription('If some authors of this post don\'t have CMS access, enter their name(s) here. You can separate multiple names with a comma.'); );
$authorNames->setDescription('If some authors of this post don\'t have CMS access, enter their name(s) here. You can separate multiple names with a comma.');
if(!$self->canEditAuthors()) { if(!$self->canEditAuthors()) {
$authorField = $authorField->performDisabledTransformation(); $authorField = $authorField->performDisabledTransformation();
$authorNames = $authorNames->performDisabledTransformation(); $authorNames = $authorNames->performDisabledTransformation();
} }
// Build up our sidebar $publishDate = DatetimeField::create('PublishDate', _t('BlogPost.PublishDate', 'Publish Date'));
$publishDate->getDateField()->setConfig('showcalendar', true);
$options = BlogAdminSidebar::create( $options = BlogAdminSidebar::create(
$publishDate = DatetimeField::create("PublishDate", _t("BlogPost.PublishDate", "Publish Date")), $publishDate,
$urlSegment, $urlSegment,
TagField::create( TagField::create(
'Categories', 'Categories',
@ -189,36 +248,84 @@ class BlogPost extends Page {
$authorField, $authorField,
$authorNames $authorNames
)->setTitle('Post Options'); )->setTitle('Post Options');
$publishDate->getDateField()->setConfig("showcalendar", true);
// Insert it before the TabSet
$fields->insertBefore($options, 'Root'); $fields->insertBefore($options, 'Root');
}); });
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
// We need to render an outer template to deal with our custom layout
$fields->fieldByName('Root')->setTemplate('TabSet_holder'); $fields->fieldByName('Root')->setTemplate('TabSet_holder');
return $fields; return $fields;
} }
/**
* Determine if this user can edit the authors list.
*
* @param null|int|Member $member
*
* @return bool
*/
public function canEditAuthors($member = null) {
$member = $this->getMember($member);
$extended = $this->extendedCan('canEditAuthors', $member);
if($extended !== null) {
return $extended;
}
$parent = $this->Parent();
if($parent instanceof Blog && $parent->exists()) {
if($parent->isEditor($member)) {
return true;
}
if($parent->isWriter($member) && $this->isAuthor($member)) {
return true;
}
}
return Permission::checkMember($member, Blog::MANAGE_USERS);
}
/**
* @param null|int|Member $member
*
* @return null|Member
*/
protected function getMember($member = null) {
if(!$member) {
$member = Member::currentUser();
}
if(is_int($member)) {
$member = Member::get()->byID($member);
}
return $member;
}
/** /**
* Determine whether user can create new categories. * Determine whether user can create new categories.
* *
* @param int|Member|null $member * @param null|int|Member $member
* *
* @return bool * @return bool
*/ */
public function canCreateCategories($member = null) { public function canCreateCategories($member = null) {
$member = $member ?: Member::currentUser(); $member = $member = $this->getMember($member);
if(is_numeric($member)) $member = Member::get()->byID($member);
$parent = $this->Parent(); $parent = $this->Parent();
if(!$parent || !$parent->exists() || !($parent instanceof Blog)) return false; if(!$parent || !$parent->exists() || !($parent instanceof Blog)) {
return false;
}
if($parent->isEditor($member)) return true; if($parent->isEditor($member)) {
return true;
}
return Permission::checkMember($member, 'ADMIN'); return Permission::checkMember($member, 'ADMIN');
} }
@ -226,212 +333,214 @@ class BlogPost extends Page {
/** /**
* Determine whether user can create new tags. * Determine whether user can create new tags.
* *
* @param int|Member|null $member * @param null|int|Member $member
* *
* @return bool * @return bool
*/ */
public function canCreateTags($member = null) { public function canCreateTags($member = null) {
$member = $member ?: Member::currentUser(); $member = $member = $this->getMember($member);
if(is_numeric($member)) $member = Member::get()->byID($member);
$parent = $this->Parent(); $parent = $this->Parent();
if(!$parent || !$parent->exists() || !($parent instanceof Blog)) return false; if(!$parent || !$parent->exists() || !($parent instanceof Blog)) {
return false;
}
if($parent->isEditor($member)) return true; if($parent->isEditor($member)) {
return true;
}
if($parent->isWriter($member)) return true; if($parent->isWriter($member)) {
return true;
}
return Permission::checkMember($member, 'ADMIN'); return Permission::checkMember($member, 'ADMIN');
} }
protected function onBeforeWrite() {
parent::onBeforeWrite();
// 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);
}
}
/** /**
* Update the PublishDate to now, if being published for the first time, and the date hasn't been set to the future. * {@inheritdoc}
**/ *
* Update the PublishDate to now, if being published for the first time, and the date hasn't
* been set to the future.
*/
public function onBeforePublish() { public function onBeforePublish() {
if ($this->dbObject('PublishDate')->InPast() && !$this->isPublished()) { /**
* @var SS_Datetime $publishDate
*/
$publishDate = $this->dbObject('PublishDate');
if($publishDate->InPast() && !$this->isPublished()) {
$this->PublishDate = SS_Datetime::now()->getValue(); $this->PublishDate = SS_Datetime::now()->getValue();
$this->write(); $this->write();
} }
} }
/** /**
* Sets blog relationship on all categories and tags assigned to this post. * {@inheritdoc}
* *
* @throws ValidationException * Sets blog relationship on all categories and tags assigned to this post.
*/ */
public function onAfterWrite() public function onAfterWrite() {
{
parent::onAfterWrite(); parent::onAfterWrite();
foreach ($this->Categories() as $category) { foreach($this->Categories() as $category) {
/**
* @var BlogCategory $category
*/
$category->BlogID = $this->ParentID; $category->BlogID = $this->ParentID;
$category->write(); $category->write();
} }
foreach ($this->Tags() as $tag) { foreach($this->Tags() as $tag) {
/**
* @var BlogTag $tag
*/
$tag->BlogID = $this->ParentID; $tag->BlogID = $this->ParentID;
$tag->write(); $tag->write();
} }
} }
/** /**
* Checks the publish date to see if the blog post has actually been published. * {@inheritdoc}
* */
* @param $member Member|null
*
* @return boolean
**/
public function canView($member = null) { public function canView($member = null) {
if(!parent::canView($member)) return false; if(!parent::canView($member)) {
return false;
}
if($this->PublishDate) { if($this->PublishDate) {
$publishDate = $this->dbObject("PublishDate"); /**
if($publishDate->InFuture() && !Permission::checkMember($member, "VIEW_DRAFT_CONTENT")) { * @var SS_Datetime $publishDate
*/
$publishDate = $this->dbObject('PublishDate');
if($publishDate->InFuture() && !Permission::checkMember($member, 'VIEW_DRAFT_CONTENT')) {
return false; return false;
} }
} }
return true; return true;
} }
public function canEdit($member = null) { /**
$member = $member ?: Member::currentUser(); * {@inheritdoc}
if(is_numeric($member)) $member = Member::get()->byID($member); */
public function canPublish($member = null) {
$member = $this->getMember($member);
// Inherit permission if(Permission::checkMember($member, 'ADMIN')) {
if(parent::canEdit($member)) return true; return true;
}
$extended = $this->extendedCan('canPublish', $member);
if($extended !== null) {
return $extended;
}
// Check if assigned to a blog
$parent = $this->Parent(); $parent = $this->Parent();
if(!$parent || !$parent->exists() || !($parent instanceof Blog)) return false;
// Editors have full control if($parent instanceof Blog && $parent->exists()) {
if($parent->isEditor($member)) return true; if($parent->isEditor($member)) {
return true;
}
// Only writers or contributors can edit if($parent->isWriter($member) && $this->isAuthor($member)) {
if(!$parent->isWriter($member) && !$parent->isContributor($member)) return false; return true;
}
if($parent->isContributor($member)) {
return parent::canEdit($member);
}
}
return $this->canEdit($member);
}
/**
* {@inheritdoc}
*/
public function canEdit($member = null) {
$member = $this->getMember($member);
if(parent::canEdit($member)) {
return true;
}
$parent = $this->Parent();
if(!$parent || !$parent->exists() || !($parent instanceof Blog)) {
return false;
}
if($parent->isEditor($member)) {
return true;
}
if(!$parent->isWriter($member) && !$parent->isContributor($member)) {
return false;
}
// And only if they are also authors
return $this->isAuthor($member); 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);
}
/** /**
* Returns the post excerpt. * Returns the post excerpt.
* *
* @param $wordCount int - number of words to display * @param int $wordsToDisplay
* *
* @return string * @return string
**/ */
public function Excerpt($wordCount = 30) { public function Excerpt($wordsToDisplay = 30) {
return $this->dbObject("Content")->LimitWordCount($wordCount); /**
* @var Text $content
*/
$content = $this->dbObject('Content');
return $content->LimitWordCount($wordsToDisplay);
} }
/** /**
* Returns a monthly archive link for the current blog post. * Returns a monthly archive link for the current blog post.
* *
* @param $type string day|month|year * @param string $type
* *
* @return string URL * @return string
**/ */
public function getMonthlyArchiveLink($type = "day") { public function getMonthlyArchiveLink($type = 'day') {
$date = $this->dbObject("PublishDate"); /**
if($type != "year") { * @var SS_Datetime $date
if($type == "day") { */
$date = $this->dbObject('PublishDate');
if($type != 'year') {
if($type == 'day') {
return Controller::join_links( return Controller::join_links(
$this->Parent()->Link("archive"), $this->Parent()->Link('archive'),
$date->format("Y"), $date->format('Y'),
$date->format("m"), $date->format('m'),
$date->format("d") $date->format('d')
); );
} }
return Controller::join_links($this->Parent()->Link("archive"), $date->format("Y"), $date->format("m"));
return Controller::join_links($this->Parent()->Link('archive'), $date->format('Y'), $date->format('m'));
} }
return Controller::join_links($this->Parent()->Link("archive"), $date->format("Y"));
return Controller::join_links($this->Parent()->Link('archive'), $date->format('Y'));
} }
/** /**
* Returns a yearly archive link for the current blog post. * Returns a yearly archive link for the current blog post.
* *
* @return string URL * @return string
**/ */
public function getYearlyArchiveLink() { public function getYearlyArchiveLink() {
$date = $this->dbObject("PublishDate"); /**
return Controller::join_links($this->Parent()->Link("archive"), $date->format("Y")); * @var SS_Datetime $date
*/
$date = $this->dbObject('PublishDate');
return Controller::join_links($this->Parent()->Link('archive'), $date->format('Y'));
} }
/** /**
@ -439,8 +548,7 @@ class BlogPost extends Page {
* *
* @return ArrayList * @return ArrayList
*/ */
public function getCredits() public function getCredits() {
{
$list = new ArrayList(); $list = new ArrayList();
$list->merge($this->getDynamicCredits()); $list->merge($this->getDynamicCredits());
@ -454,8 +562,7 @@ class BlogPost extends Page {
* *
* @return ArrayList * @return ArrayList
*/ */
protected function getDynamicCredits() protected function getDynamicCredits() {
{
$items = new ArrayList(); $items = new ArrayList();
foreach($this->Authors() as $author) { foreach($this->Authors() as $author) {
@ -474,13 +581,12 @@ class BlogPost extends Page {
* *
* @return ArrayList * @return ArrayList
*/ */
protected function getStaticCredits() protected function getStaticCredits() {
{
$items = new ArrayList(); $items = new ArrayList();
$authors = array_filter(preg_split('/\s*,\s*/', $this->AuthorNames)); $authors = array_filter(preg_split('/\s*,\s*/', $this->AuthorNames));
foreach ($authors as $author) { foreach($authors as $author) {
$item = new ArrayData(array( $item = new ArrayData(array(
'Name' => $author, 'Name' => $author,
)); ));
@ -492,27 +598,40 @@ class BlogPost extends Page {
} }
/** /**
* Sets the label for BlogPost.Title to 'Post Title' (Rather than 'Page name') * Sets the label for BlogPost.Title to 'Post Title' (Rather than 'Page name').
*
* @param bool $includeRelations
* *
* @return array * @return array
**/ */
public function fieldLabels($includerelations = true) { public function fieldLabels($includeRelations = true) {
$labels = parent::fieldLabels($includerelations); $labels = parent::fieldLabels($includeRelations);
$labels['Title'] = _t('BlogPost.PageTitleLabel', "Post Title");
$labels['Title'] = _t('BlogPost.PageTitleLabel', "Post Title");
return $labels; return $labels;
} }
} /**
* {@inheritdoc}
*/
protected function onBeforeWrite() {
parent::onBeforeWrite();
if(!$this->PublishDate) {
$this->PublishDate = SS_Datetime::now()->getValue();
}
if(!$this->exists() && ($member = Member::currentUser())) {
$this->Authors()->add($member);
}
}
}
/** /**
* Blog Post controller
*
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* */
* @author Michael Strong <github@michaelstrong.co.uk>
**/
class BlogPost_Controller extends Page_Controller { class BlogPost_Controller extends Page_Controller {
} }

View File

@ -1,119 +1,136 @@
<?php <?php
/** /**
* A blog tag for keyword descriptions of a Blog post * A blog tag for keyword descriptions of a blog post.
* *
* @package silverstripe * @package silverstripe
* @subpackage blog * @subpackage blog
* *
* @author Michael Strong <github@michaelstrong.co.uk> * @method Blog Blog()
**/ * @method ManyManyList BlogPosts
class BlogTag extends DataObject { *
* @property string $Title
private static $db = array( * @property string $URLSegment
"Title" => "Varchar(255)", * @property int $BlogID
); */
class BlogTag extends DataObject implements CategorisationObject {
private static $has_one = array( /**
"Blog" => "Blog", * @var array
); */
private static $db = array(
private static $belongs_many_many = array( 'Title' => 'Varchar(255)',
"BlogPosts" => "BlogPost", );
);
/**
private static $extensions = array( * @var array
"URLSegmentExtension", */
); private static $has_one = array(
'Blog' => 'Blog',
);
public function getCMSFields() {
$fields = new FieldList( /**
TextField::create("Title", _t("BlogTag.Title", "Title")) * @var array
); */
$this->extend("updateCMSFields", $fields); private static $belongs_many_many = array(
return $fields; 'BlogPosts' => 'BlogPost',
} );
/**
/** * @var array
* Returns a relative URL for the tag link. */
* private static $extensions = array(
* @return string URL 'URLSegmentExtension',
**/ );
public function getLink() {
return Controller::join_links($this->Blog()->Link(), "tag", $this->URLSegment); /**
} * {@inheritdoc}
*/
public function getCMSFields() {
$fields = new FieldList(
/** TextField::create('Title', _t('BlogTag.Title', 'Title'))
* Inherits from the parent blog or can be overwritten using a DataExtension );
*
* @param $member Member $this->extend('updateCMSFields', $fields);
*
* @return boolean return $fields;
*/ }
public function canView($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member); /**
if($extended !== null) { * Returns a relative URL for the tag link.
return $extended; *
} * @return string
return $this->Blog()->canView($member); */
} public function getLink() {
return Controller::join_links($this->Blog()->Link(), 'tag', $this->URLSegment);
}
/** /**
* Inherits from the parent blog or can be overwritten using a DataExtension * Inherits from the parent blog or can be overwritten using a DataExtension.
* *
* @param $member Member * @param null|Member $member
* *
* @return boolean * @return bool
*/ */
public function canCreate($member = null) { public function canView($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member); $extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) {
return $extended; if($extended !== null) {
} return $extended;
}
// Since there is no parent yet, need to make a best guess
$permission = Blog::config()->grant_user_permission; return $this->Blog()->canView($member);
return Permission::checkMember($member, $permission); }
}
/**
* Inherits from the parent blog or can be overwritten using a DataExtension.
*
/** * @param null|Member $member
* Inherits from the parent blog or can be overwritten using a DataExtension *
* * @return bool
* @param $member Member */
* public function canCreate($member = null) {
* @return boolean $extended = $this->extendedCan(__FUNCTION__, $member);
*/
public function canDelete($member = null) { if($extended !== null) {
$extended = $this->extendedCan(__FUNCTION__, $member); return $extended;
if($extended !== null) { }
return $extended;
} $permission = Blog::config()->grant_user_permission;
return $this->Blog()->canEdit($member);
} return Permission::checkMember($member, $permission);
}
/**
/** * Inherits from the parent blog or can be overwritten using a DataExtension.
* Inherits from the parent blog or can be overwritten using a DataExtension *
* * @param null|Member $member
* @param $member Member *
* * @return bool
* @return boolean */
*/ public function canDelete($member = null) {
public function canEdit($member = null) { $extended = $this->extendedCan(__FUNCTION__, $member);
$extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) { if($extended !== null) {
return $extended; return $extended;
} }
return $this->Blog()->canEdit($member);
} return $this->Blog()->canEdit($member);
}
}
/**
* Inherits from the parent blog or can be overwritten using a DataExtension.
*
* @param null|Member $member
*
* @return bool
*/
public function canEdit($member = null) {
$extended = $this->extendedCan(__FUNCTION__, $member);
if($extended !== null) {
return $extended;
}
return $this->Blog()->canEdit($member);
}
}

View File

@ -0,0 +1,8 @@
<?php
/**
* @method ManyManyList BlogPosts
*/
interface CategorisationObject {
}

View File

@ -1,88 +1,125 @@
<?php <?php
if(class_exists("Widget")) { if(!class_exists('Widget')) {
return;
}
class BlogArchiveWidget extends Widget { /**
* @method Blog Blog()
private static $title = "Archive"; *
* @property string $ArchiveType
* @property int $NumberToDisplay
*/
class BlogArchiveWidget extends Widget {
/**
* @var string
*/
private static $title = 'Archive';
private static $cmsTitle = "Archive"; /**
* @var string
*/
private static $cmsTitle = 'Archive';
private static $description = "Displays an archive list of posts."; /**
* @var string
*/
private static $description = 'Displays an archive list of posts.';
private static $db = array( /**
"NumberToDisplay" => "Int", * @var array
"ArchiveType" => "Enum('Monthly,Yearly', 'Monthly')" */
); private static $db = array(
'NumberToDisplay' => 'Int',
'ArchiveType' => 'Enum("Monthly,Yearly", "Monthly")',
);
private static $defaults = array( /**
"NumberOfMonths" => 12 * @var array
); */
private static $defaults = array(
'NumberOfMonths' => 12,
);
private static $has_one = array( /**
"Blog" => "Blog", * @var array
); */
private static $has_one = array(
'Blog' => 'Blog',
);
public function getCMSFields() { /**
$self = $this; * {@inheritdoc}
$this->beforeUpdateCMSFields(function($fields) use ($self) { */
$type = $self->dbObject("ArchiveType")->enumValues(); public function getCMSFields() {
foreach($type as $k => $v) { $self = &$this;
$type[$k] = _t("BlogArchiveWidget." . ucfirst(strtolower($v)), $v);
}
$fields->merge(array( $this->beforeUpdateCMSFields(function ($fields) use ($self) {
DropdownField::create("BlogID", _t("BlogArchiveWidget.Blog", "Blog"), Blog::get()->map()), $type = $self->dbObject('ArchiveType')->enumValues();
DropdownField::create("ArchiveType", _t("BlogArchiveWidget.ArchiveType", "ArchiveType"), $type),
NumericField::create("NumberToDisplay", _t("BlogArchiveWidget.NumberToDisplay", "No. to Display"))
));
});
return parent::getCMSFields();
}
foreach($type as $k => $v) {
/** $type[$k] = _t('BlogArchiveWidget.' . ucfirst(strtolower($v)), $v);
* Returns a list of months where blog posts are present.
*
* @return DataList
**/
public function getArchive() {
$query = $this->Blog()->getBlogPosts()->dataQuery();
if($this->ArchiveType == "Yearly") {
$query->groupBy("DATE_FORMAT(PublishDate, '%Y')");
} else {
$query->groupBy("DATE_FORMAT(PublishDate, '%Y-%M')");
} }
$articles = $this->Blog()->getBlogPosts()->setDataQuery($query); $fields->merge(array(
if($this->NumberToDisplay > 0) $articles = $articles->limit($this->NumberToDisplay); DropdownField::create('BlogID', _t('BlogArchiveWidget.Blog', 'Blog'), Blog::get()->map()),
DropdownField::create('ArchiveType', _t('BlogArchiveWidget.ArchiveType', 'ArchiveType'), $type),
$archive = new ArrayList(); NumericField::create('NumberToDisplay', _t('BlogArchiveWidget.NumberToDisplay', 'No. to Display'))
if($articles->count() > 0) { ));
foreach($articles as $article) { });
if($this->ArchiveType == "Yearly") {
$year = date('Y', strtotime($article->PublishDate)); return parent::getCMSFields();
$month = null; }
$title = $year;
} else { /**
$year = date('Y', strtotime($article->PublishDate)); * Returns a list of months where blog posts are present.
$month = date('m', strtotime($article->PublishDate)); *
$title = date('F Y', strtotime($article->PublishDate)); * @return DataList
} */
$archive->push(new ArrayData(array( public function getArchive() {
"Title" => $title, $query = $this->Blog()->getBlogPosts()->dataQuery();
"Link" => Controller::join_links($this->Blog()->Link("archive"), $year, $month)
))); if($this->ArchiveType == 'Yearly') {
} $query->groupBy('DATE_FORMAT(PublishDate, "%Y")');
} } else {
return $archive; $query->groupBy('DATE_FORMAT(PublishDate, "%Y-%M")');
} }
} $posts = $this->Blog()->getBlogPosts()->setDataQuery($query);
class BlogArchiveWidget_Controller extends Widget_Controller { if($this->NumberToDisplay > 0) {
$posts = $posts->limit($this->NumberToDisplay);
}
$archive = new ArrayList();
if($posts->count() > 0) {
foreach($posts as $post) {
/**
* @var BlogPost $post
*/
if($this->ArchiveType == 'Yearly') {
$year = date('Y', strtotime($post->PublishDate));
$month = null;
$title = $year;
} else {
$year = date('Y', strtotime($post->PublishDate));
$month = date('m', strtotime($post->PublishDate));
$title = date('F Y', strtotime($post->PublishDate));
}
$archive->push(new ArrayData(array(
'Title' => $title,
'Link' => Controller::join_links($this->Blog()->Link('archive'), $year, $month)
)));
}
}
return $archive;
} }
}
class BlogArchiveWidget_Controller extends Widget_Controller {
} }

View File

@ -1,42 +1,67 @@
<?php <?php
if(class_exists("Widget")) { if(!class_exists("Widget")) {
return;
}
class BlogCategoriesWidget extends Widget { /**
* @method Blog Blog()
private static $title = "Categories"; */
class BlogCategoriesWidget extends Widget {
/**
* @var string
*/
private static $title = 'Categories';
private static $cmsTitle = "Blog Categories"; /**
* @var string
*/
private static $cmsTitle = 'Blog Categories';
private static $description = "Displays a list of blog categories."; /**
* @var string
*/
private static $description = 'Displays a list of blog categories.';
private static $db = array(); /**
* @var array
*/
private static $db = array();
private static $has_one = array( /**
"Blog" => "Blog", * @var array
); */
private static $has_one = array(
'Blog' => 'Blog',
);
public function getCMSFields() { /**
$this->beforeUpdateCMSFields(function($fields) { * {@inheritdoc}
$fields->push( */
DropdownField::create("BlogID", _t("BlogCategoriesWidget.Blog", "Blog"), Blog::get()->map()) public function getCMSFields() {
); $this->beforeUpdateCMSFields(function ($fields) {
}); $fields->push(
return parent::getCMSFields(); DropdownField::create('BlogID', _t('BlogCategoriesWidget.Blog', 'Blog'), Blog::get()->map())
} );
});
public function getCategories() {
$blog = $this->Blog();
if($blog) {
return $blog->Categories();
}
return array();
}
return parent::getCMSFields();
} }
class BlogCategoriesWidget_Controller extends Widget_Controller { /**
* @return array
*/
public function getCategories() {
$blog = $this->Blog();
if($blog) {
return $blog->Categories();
}
return array();
} }
}
class BlogCategoriesWidget_Controller extends Widget_Controller {
} }

View File

@ -1,47 +1,74 @@
<?php <?php
if(class_exists("Widget")) { if(!class_exists("Widget")) {
return;
}
class BlogRecentPostsWidget extends Widget { /**
* @method Blog Blog()
private static $title = "Recent Posts"; *
* @property int $NumberOfPosts
*/
class BlogRecentPostsWidget extends Widget {
/**
* @var string
*/
private static $title = 'Recent Posts';
private static $cmsTitle = "Recent Posts"; /**
* @var string
*/
private static $cmsTitle = 'Recent Posts';
private static $description = "Displays a list of recent blog posts."; /**
* @var string
*/
private static $description = 'Displays a list of recent blog posts.';
private static $db = array( /**
"NumberOfPosts" => "Int", * @var array
); */
private static $db = array(
'NumberOfPosts' => 'Int',
);
private static $has_one = array( /**
"Blog" => "Blog", * @var array
); */
private static $has_one = array(
'Blog' => 'Blog',
);
public function getCMSFields() { /**
$this->beforeUpdateCMSFields(function($fields) { * {@inheritdoc}
$fields->merge(array( */
DropdownField::create("BlogID", _t("BlogRecentPostsWidget.Blog", "Blog"), Blog::get()->map()), public function getCMSFields() {
NumericField::create("NumberOfPosts", _t("BlogRecentPostsWidget.NumberOfPosts", "Number of Posts")) $this->beforeUpdateCMSFields(function ($fields) {
)); $fields->merge(array(
}); DropdownField::create('BlogID', _t('BlogRecentPostsWidget.Blog', 'Blog'), Blog::get()->map()),
return parent::getCMSFields(); NumericField::create('NumberOfPosts', _t('BlogRecentPostsWidget.NumberOfPosts', 'Number of Posts'))
} ));
});
public function getPosts() {
$blog = $this->Blog();
if($blog) {
return $blog->getBlogPosts()
->sort("PublishDate DESC")
->limit($this->NumberOfPosts);
}
return array();
}
return parent::getCMSFields();
} }
class BlogRecentPostsWidget_Controller extends Widget_Controller { /**
* @return array
} */
public function getPosts() {
$blog = $this->Blog();
} if($blog) {
return $blog->getBlogPosts()
->sort('PublishDate DESC')
->limit($this->NumberOfPosts);
}
return array();
}
}
class BlogRecentPostsWidget_Controller extends Widget_Controller {
}

View File

@ -1,40 +1,65 @@
<?php <?php
if(class_exists("Widget")) { if(!class_exists("Widget")) {
return;
}
class BlogTagsWidget extends Widget { /**
* @method Blog Blog()
private static $title = "Tags"; */
class BlogTagsWidget extends Widget {
/**
* @var string
*/
private static $title = 'Tags';
private static $cmsTitle = "Blog Tags"; /**
* @var string
*/
private static $cmsTitle = 'Blog Tags';
private static $description = "Displays a list of blog tags."; /**
* @var string
*/
private static $description = 'Displays a list of blog tags.';
private static $db = array(); /**
* @var array
*/
private static $db = array();
private static $has_one = array( /**
"Blog" => "Blog", * @var array
); */
private static $has_one = array(
'Blog' => 'Blog',
);
public function getCMSFields() { /**
$this->beforeUpdateCMSFields(function($fields) { * {@inheritdoc}
$fields->push(DropdownField::create("BlogID", _t("BlogTagsWidget.Blog", "Blog"), Blog::get()->map())); */
}); public function getCMSFields() {
return parent::getCMSFields(); $this->beforeUpdateCMSFields(function ($fields) {
} $fields->push(DropdownField::create('BlogID', _t('BlogTagsWidget.Blog', 'Blog'), Blog::get()->map()));
});
public function getTags() {
$blog = $this->Blog();
if($blog) {
return $blog->Tags();
}
return array();
}
return parent::getCMSFields();
} }
class BlogTagsWidget_Controller extends Widget_Controller { /**
* @return array
} */
public function getTags() {
$blog = $this->Blog();
} if($blog) {
return $blog->Tags();
}
return array();
}
}
class BlogTagsWidget_Controller extends Widget_Controller {
}

View File

@ -1,107 +1,118 @@
<?php <?php
/**
* @mixin PHPUnit_Framework_TestCase
*/
class BlogCategoryTest extends FunctionalTest { class BlogCategoryTest extends FunctionalTest {
/**
static $fixture_file = "blog.yml"; * @var string
*/
static $fixture_file = 'blog.yml';
/**
* {@inheritdoc}
*/
public function setUp() { public function setUp() {
SS_Datetime::set_mock_now("2013-10-10 20:00:00");
parent::setUp(); parent::setUp();
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
} }
/**
* {@inheritdoc}
*/
public function tearDown() { public function tearDown() {
SS_Datetime::clear_mock_now(); SS_Datetime::clear_mock_now();
parent::tearDown(); parent::tearDown();
} }
/** /**
* Tests that any blog posts returned from $category->BlogPosts() many_many are published, * Tests that any blog posts returned from $category->BlogPosts() many_many are published,
* both by normal 'save & publish' functionality and by publish date. * both by normal 'save & publish' functionality and by publish date.
**/ */
public function testBlogPosts() { public function testBlogPosts() {
// Ensure the user is not logged in as admin (or anybody)
$member = Member::currentUser(); $member = Member::currentUser();
if($member) $member->logout();
$post = $this->objFromFixture("BlogPost", "blogpost1"); if($member) {
$category = $this->objFromFixture("BlogCategory", "firstcategory"); $member->logout();
$this->assertEquals(1, $category->BlogPosts()->count(), "Category blog post count"); }
$this->objFromFixture('BlogPost', 'FirstBlogPost');
/**
* @var BlogCategory $category
*/
$category = $this->objFromFixture('BlogCategory', 'FirstCategory');
$this->assertEquals(1, $category->BlogPosts()->count(), 'Category blog post count');
} }
public function testCanView() { public function testCanView() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor');
// The first blog can bew viewed by anybody $editor = $this->objFromFixture('Member', 'Editor');
// $category = $this->objFromFixture("BlogCategory", "firstcategory"); $category = $this->objFromFixture('BlogCategory', 'SecondCategory');
// $this->assertTrue($category->canView($admin), "Admin should be able to view category.");
// $this->assertTrue($category->canView($editor), "Editor should be able to view category.");
$category = $this->objFromFixture("BlogCategory", "secondcategory"); $this->assertFalse($category->canView($editor), 'Editor should not be able to view category.');
// $this->assertTrue($category->canView($admin), "Admin should be able to view category.");
$this->assertFalse($category->canView($editor), "Editor should not be able to view category.");
} }
/**
* The first blog can be viewed by anybody.
*/
public function testCanEdit() { public function testCanEdit() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody $category = $this->objFromFixture('BlogCategory', 'FirstCategory');
$category = $this->objFromFixture("BlogCategory", "firstcategory");
$this->assertTrue($category->canEdit($admin), "Admin should be able to edit category.");
$this->assertTrue($category->canEdit($editor), "Editor should be able to edit category.");
$category = $this->objFromFixture("BlogCategory", "secondcategory"); $this->assertTrue($category->canEdit($admin), 'Admin should be able to edit category.');
$this->assertTrue($category->canEdit($admin), "Admin should be able to edit category."); $this->assertTrue($category->canEdit($editor), 'Editor should be able to edit category.');
$this->assertFalse($category->canEdit($editor), "Editor should not be able to edit category.");
$category = $this->objFromFixture("BlogCategory", "thirdcategory"); $category = $this->objFromFixture('BlogCategory', 'SecondCategory');
$this->assertTrue($category->canEdit($admin), "Admin should always be able to edit category.");
$this->assertTrue($category->canEdit($editor), "Editor should be able to edit category."); $this->assertTrue($category->canEdit($admin), 'Admin should be able to edit category.');
$this->assertFalse($category->canEdit($editor), 'Editor should not be able to edit category.');
$category = $this->objFromFixture('BlogCategory', 'ThirdCategory');
$this->assertTrue($category->canEdit($admin), 'Admin should always be able to edit category.');
$this->assertTrue($category->canEdit($editor), 'Editor should be able to edit category.');
} }
public function testCanCreate() { public function testCanCreate() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody
$category = singleton('BlogCategory'); $category = singleton('BlogCategory');
$this->assertTrue($category->canCreate($admin), "Admin should be able to create category.");
$this->assertTrue($category->canCreate($editor), "Editor should be able to create category."); $this->assertTrue($category->canCreate($admin), 'Admin should be able to create category.');
$this->assertTrue($category->canCreate($editor), 'Editor should be able to create category.');
} }
public function testCanDelete() { public function testCanDelete() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody $category = $this->objFromFixture('BlogCategory', 'FirstCategory');
$category = $this->objFromFixture("BlogCategory", "firstcategory");
$this->assertTrue($category->canDelete($admin), "Admin should be able to delete category.");
$this->assertTrue($category->canDelete($editor), "Editor should be able to category category.");
$category = $this->objFromFixture("BlogCategory", "secondcategory"); $this->assertTrue($category->canDelete($admin), 'Admin should be able to delete category.');
$this->assertTrue($category->canDelete($admin), "Admin should be able to delete category."); $this->assertTrue($category->canDelete($editor), 'Editor should be able to category category.');
$this->assertFalse($category->canDelete($editor), "Editor should not be able to delete category.");
$category = $this->objFromFixture("BlogCategory", "thirdcategory"); $category = $this->objFromFixture('BlogCategory', 'SecondCategory');
$this->assertTrue($category->canDelete($admin), "Admin should always be able to delete category."); $this->assertTrue($category->canDelete($admin), 'Admin should be able to delete category.');
$this->assertTrue($category->canDelete($editor), "Editor should be able to delete category."); $this->assertFalse($category->canDelete($editor), 'Editor should not be able to delete category.');
$category = $this->objFromFixture('BlogCategory', 'ThirdCategory');
$this->assertTrue($category->canDelete($admin), 'Admin should always be able to delete category.');
$this->assertTrue($category->canDelete($editor), 'Editor should be able to delete category.');
} }
} }

View File

@ -1,31 +1,42 @@
<?php <?php
/**
* @mixin PHPUnit_Framework_TestCase
*/
class BlogPostFilterTest extends SapphireTest { class BlogPostFilterTest extends SapphireTest {
/**
static $fixture_file = "blog.yml"; * @var string
*/
static $fixture_file = 'blog.yml';
public function setUp() { public function setUp() {
SS_Datetime::set_mock_now("2013-10-10 20:00:00");
parent::setUp(); parent::setUp();
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
} }
public function tearDown() { public function tearDown() {
SS_Datetime::clear_mock_now(); SS_Datetime::clear_mock_now();
parent::tearDown(); parent::tearDown();
} }
public function testFilter() { public function testFilter() {
$member = Member::currentUser(); $member = Member::currentUser();
if($member) $member->logout();
$blog = $this->objFromFixture('Blog', 'firstblog'); if($member) {
$member->logout();
}
$count = $blog->AllChildren()->Count(); /**
$this->assertEquals(3, $count, "Filtered blog posts"); * @var Blog $blog
*/
$blog = $this->objFromFixture('Blog', 'FirstBlog');
SS_Datetime::set_mock_now("2020-01-01 00:00:00"); $this->assertEquals(3, $blog->AllChildren()->Count(), 'Filtered blog posts');
$count = $blog->AllChildren()->Count();
$this->assertEquals(5, $count, "Unfiltered blog posts"); SS_Datetime::set_mock_now('2020-01-01 00:00:00');
$this->assertEquals(5, $blog->AllChildren()->Count(), 'Unfiltered blog posts');
} }
} }

View File

@ -1,107 +1,126 @@
<?php <?php
/**
* @mixin PHPUnit_Framework_TestCase
*/
class BlogTagTest extends FunctionalTest { class BlogTagTest extends FunctionalTest {
/**
static $fixture_file = "blog.yml"; * @var string
*/
static $fixture_file = 'blog.yml';
/**
* {@inheritdoc}
*/
public function setUp() { public function setUp() {
SS_Datetime::set_mock_now("2013-10-10 20:00:00");
parent::setUp(); parent::setUp();
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
} }
/**
* {@inheritdoc}
*/
public function tearDown() { public function tearDown() {
SS_Datetime::clear_mock_now(); SS_Datetime::clear_mock_now();
parent::tearDown(); parent::tearDown();
} }
/** /**
* Tests that any blog posts returned from $tag->BlogPosts() many_many are published, * Tests that any blog posts returned from $tag->BlogPosts() many_many are published, both by
* both by normal 'save & publish' functionality and by publish date. * normal 'save & publish' functionality and by publish date.
**/ */
public function testBlogPosts() { public function testBlogPosts() {
// Ensure the user is not logged in as admin (or anybody)
$member = Member::currentUser(); $member = Member::currentUser();
if($member) $member->logout();
$post = $this->objFromFixture("BlogPost", "blogpost1"); if($member) {
$tag = $this->objFromFixture("BlogTag", "firsttag"); $member->logout();
$this->assertEquals(1, $tag->BlogPosts()->count(), "Tag blog post count"); }
$this->objFromFixture('BlogPost', 'FirstBlogPost');
/**
* @var BlogTag $tag
*/
$tag = $this->objFromFixture('BlogTag', 'FirstTag');
$this->assertEquals(1, $tag->BlogPosts()->count(), 'Tag blog post count');
} }
/**
* The first blog can be viewed by anybody.
*/
public function testCanView() { public function testCanView() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody $tag = $this->objFromFixture('BlogTag', 'FirstTag');
$tag = $this->objFromFixture("BlogTag", "firsttag");
$this->assertTrue($tag->canView($admin), "Admin should be able to view tag.");
$this->assertTrue($tag->canView($editor), "Editor should be able to view tag.");
$tag = $this->objFromFixture("BlogTag", "secondtag"); $this->assertTrue($tag->canView($admin), 'Admin should be able to view tag.');
$this->assertTrue($tag->canView($admin), "Admin should be able to view tag."); $this->assertTrue($tag->canView($editor), 'Editor should be able to view tag.');
$this->assertFalse($tag->canView($editor), "Editor should not be able to view tag.");
$tag = $this->objFromFixture('BlogTag', 'SecondTag');
$this->assertTrue($tag->canView($admin), 'Admin should be able to view tag.');
$this->assertFalse($tag->canView($editor), 'Editor should not be able to view tag.');
} }
public function testCanEdit() { public function testCanEdit() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody $tag = $this->objFromFixture('BlogTag', 'FirstTag');
$tag = $this->objFromFixture("BlogTag", "firsttag");
$this->assertTrue($tag->canEdit($admin), "Admin should be able to edit tag.");
$this->assertTrue($tag->canEdit($editor), "Editor should be able to edit tag.");
$tag = $this->objFromFixture("BlogTag", "secondtag"); $this->assertTrue($tag->canEdit($admin), 'Admin should be able to edit tag.');
$this->assertTrue($tag->canEdit($admin), "Admin should be able to edit tag."); $this->assertTrue($tag->canEdit($editor), 'Editor should be able to edit tag.');
$this->assertFalse($tag->canEdit($editor), "Editor should not be able to edit tag.");
$tag = $this->objFromFixture("BlogTag", "thirdtag"); $tag = $this->objFromFixture('BlogTag', 'SecondTag');
$this->assertTrue($tag->canEdit($admin), "Admin should always be able to edit tags.");
$this->assertTrue($tag->canEdit($editor), "Editor should be able to edit tag."); $this->assertTrue($tag->canEdit($admin), 'Admin should be able to edit tag.');
$this->assertFalse($tag->canEdit($editor), 'Editor should not be able to edit tag.');
$tag = $this->objFromFixture('BlogTag', 'ThirdTag');
$this->assertTrue($tag->canEdit($admin), 'Admin should always be able to edit tags.');
$this->assertTrue($tag->canEdit($editor), 'Editor should be able to edit tag.');
} }
public function testCanCreate() { public function testCanCreate() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody $tag = singleton('BlogTag');
$tag = singleton("BlogTag");
$this->assertTrue($tag->canCreate($admin), "Admin should be able to create tag."); $this->assertTrue($tag->canCreate($admin), 'Admin should be able to create tag.');
$this->assertTrue($tag->canCreate($editor), "Editor should be able to create tag."); $this->assertTrue($tag->canCreate($editor), 'Editor should be able to create tag.');
} }
public function testCanDelete() { public function testCanDelete() {
$this->useDraftSite(); $this->useDraftSite();
$admin = $this->objFromFixture("Member", "admin"); $admin = $this->objFromFixture('Member', 'Admin');
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture('Member', 'Editor');
// The first blog can bew viewed by anybody $tag = $this->objFromFixture('BlogTag', 'FirstTag');
$tag = $this->objFromFixture("BlogTag", "firsttag");
$this->assertTrue($tag->canDelete($admin), "Admin should be able to delete tag.");
$this->assertTrue($tag->canDelete($editor), "Editor should be able to delete tag.");
$tag = $this->objFromFixture("BlogTag", "secondtag"); $this->assertTrue($tag->canDelete($admin), 'Admin should be able to delete tag.');
$this->assertTrue($tag->canDelete($admin), "Admin should be able to delete tag."); $this->assertTrue($tag->canDelete($editor), 'Editor should be able to delete tag.');
$this->assertFalse($tag->canDelete($editor), "Editor should not be able to delete tag.");
$tag = $this->objFromFixture("BlogTag", "thirdtag"); $tag = $this->objFromFixture('BlogTag', 'SecondTag');
$this->assertTrue($tag->canDelete($admin), "Admin should always be able to delete tags.");
$this->assertTrue($tag->canDelete($editor), "Editor should be able to delete tag."); $this->assertTrue($tag->canDelete($admin), 'Admin should be able to delete tag.');
$this->assertFalse($tag->canDelete($editor), 'Editor should not be able to delete tag.');
$tag = $this->objFromFixture('BlogTag', 'ThirdTag');
$this->assertTrue($tag->canDelete($admin), 'Admin should always be able to delete tags.');
$this->assertTrue($tag->canDelete($editor), 'Editor should be able to delete tag.');
} }
} }

View File

@ -1,129 +1,198 @@
<?php <?php
/**
* @mixin PHPUnit_Framework_TestCase
*/
class BlogTest extends SapphireTest { class BlogTest extends SapphireTest {
/**
* @var string
*/
static $fixture_file = 'blog.yml';
static $fixture_file = "blog.yml"; /**
* {@inheritdoc}
*/
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
Config::nest(); Config::nest();
SS_Datetime::set_mock_now("2013-10-10 20:00:00"); SS_Datetime::set_mock_now('2013-10-10 20:00:00');
$this->objFromFixture("Blog", "firstblog")->publish("Stage", "Live");
/**
* @var Blog $blog
*/
$blog = $this->objFromFixture('Blog', 'FirstBlog');
$blog->publish('Stage', 'Live');
} }
/**
* {@inheritdoc}
*/
public function tearDown() { public function tearDown() {
SS_Datetime::clear_mock_now(); SS_Datetime::clear_mock_now();
Config::unnest(); Config::unnest();
parent::tearDown(); parent::tearDown();
} }
public function testGetExcludedSiteTreeClassNames() { public function testGetExcludedSiteTreeClassNames() {
$member = Member::currentUser(); $member = Member::currentUser();
if($member) $member->logout();
$blog = $this->objFromFixture("Blog", 'firstblog'); if($member) {
$member->logout();
}
Config::inst()->update("BlogPost", "show_in_sitetree", true); /**
* @var Blog $blog
*/
$blog = $this->objFromFixture('Blog', 'FirstBlog');
Config::inst()->update('BlogPost', 'show_in_sitetree', true);
$classes = $blog->getExcludedSiteTreeClassNames(); $classes = $blog->getExcludedSiteTreeClassNames();
$this->assertNotContains('BlogPost', $classes, "BlogPost class should be hidden.");
Config::inst()->update("BlogPost", "show_in_sitetree", false); $this->assertNotContains('BlogPost', $classes, 'BlogPost class should be hidden.');
Config::inst()->update('BlogPost', 'show_in_sitetree', false);
$classes = $blog->getExcludedSiteTreeClassNames(); $classes = $blog->getExcludedSiteTreeClassNames();
$this->assertContains('BlogPost', $classes, "BlogPost class should be hidden.");
$this->assertContains('BlogPost', $classes, 'BlogPost class should be hidden.');
} }
public function testGetArchivedBlogPosts() { public function testGetArchivedBlogPosts() {
$member = Member::currentUser(); $member = Member::currentUser();
if($member) $member->logout();
$blog = $this->objFromFixture("Blog", "firstblog"); if($member) {
$member->logout();
}
/**
* @var Blog $blog
*/
$blog = $this->objFromFixture('Blog', 'FirstBlog');
// Test yearly
$archive = $blog->getArchivedBlogPosts(2013); $archive = $blog->getArchivedBlogPosts(2013);
$this->assertEquals(2, $archive->count(), "Incorrect Yearly Archive count for 2013");
$this->assertEquals("First post", $archive->first()->Title, "Incorrect First Blog post");
$this->assertEquals("Second post", $archive->last()->Title, "Incorrect Last Blog post");
// Test monthly $this->assertEquals(2, $archive->count(), 'Incorrect Yearly Archive count for 2013');
$this->assertEquals('First Post', $archive->first()->Title, 'Incorrect First Blog post');
$this->assertEquals('Second Post', $archive->last()->Title, 'Incorrect Last Blog post');
$archive = $blog->getArchivedBlogPosts(2013, 10); $archive = $blog->getArchivedBlogPosts(2013, 10);
$this->assertEquals(1, $archive->count(), "Incorrect monthly acrhive count.");
// Test daily $this->assertEquals(1, $archive->count(), 'Incorrect monthly archive count.');
$archive = $blog->getArchivedBlogPosts(2013, 10, 01); $archive = $blog->getArchivedBlogPosts(2013, 10, 01);
$this->assertEquals(1, $archive->count(), "Incorrect daily archive count.");
$this->assertEquals(1, $archive->count(), 'Incorrect daily archive count.');
} }
public function testArchiveLinks() { public function testArchiveLinks() {
$blog = $this->objFromFixture("Blog", "firstblog"); /**
* @var Blog $blog
*/
$blog = $this->objFromFixture('Blog', 'FirstBlog');
// Test valid links $link = Controller::join_links($blog->Link('archive'), '2013', '10', '01');
$archiveLink = Controller::join_links($blog->Link("archive"), 2013, 10, 01);
$response = Director::test($archiveLink);
$this->assertEquals(200, $response->getStatusCode(), "HTTP Status should be 200");
$archiveLink = Controller::join_links($blog->Link("archive"), 2013, 10); $this->assertEquals(200, $this->getStatusOf($link), 'HTTP Status should be 200');
$response = Director::test($archiveLink);
$this->assertEquals(200, $response->getStatusCode(), "HTTP Status should be 200");
$archiveLink = Controller::join_links($blog->Link("archive"), 2013); $link = Controller::join_links($blog->Link('archive'), '2013', '10');
$response = Director::test($archiveLink);
$this->assertEquals(200, $response->getStatusCode(), "HTTP Status should be 200");
$archiveLink = Controller::join_links($blog->Link("archive"), 2011, 10, 01); $this->assertEquals(200, $this->getStatusOf($link), 'HTTP Status should be 200');
$response = Director::test($archiveLink); // No posts on this date, but a valid entry.
$this->assertEquals(200, $response->getStatusCode(), "HTTP Status should be 200");
$link = Controller::join_links($blog->Link('archive'), '2013');
// Test invalid links & dates $this->assertEquals(200, $this->getStatusOf($link), 'HTTP Status should be 200');
$response = Director::test($blog->Link("archive")); // 404 when no date is set
$this->assertEquals(404, $response->getStatusCode(), "HTTP Status should be 404");
// Invalid year $link = Controller::join_links($blog->Link('archive'), '2011', '10', '01');
$archiveLink = Controller::join_links($blog->Link("archive"), "invalid-year");
$response = Director::test($archiveLink); // 404 when an invalid yer is set
$this->assertEquals(404, $response->getStatusCode(), "HTTP Status should be 404");
// Invalid month $this->assertEquals(200, $this->getStatusOf($link), 'HTTP Status should be 200');
$archiveLink = Controller::join_links($blog->Link("archive"), "2013", "99");
$response = Director::test($archiveLink); // 404 when an invalid month is set
$this->assertEquals(404, $response->getStatusCode(), "HTTP Status should be 404");
// Invalid day $link = Controller::join_links($blog->Link('archive'));
$archiveLink = Controller::join_links($blog->Link("archive"), "2013", "10", "99");
$response = Director::test($archiveLink); // 404 when an invalid day is set
$this->assertEquals(404, $response->getStatusCode(), "HTTP Status should be 404");
$this->assertEquals(404, $this->getStatusOf($link), 'HTTP Status should be 404');
$link = Controller::join_links($blog->Link('archive'), 'invalid-year');
$this->assertEquals(404, $this->getStatusOf($link), 'HTTP Status should be 404');
$link = Controller::join_links($blog->Link('archive'), '2013', '99');
$this->assertEquals(404, $this->getStatusOf($link), 'HTTP Status should be 404');
$link = Controller::join_links($blog->Link('archive'), '2013', '10', '99');
$this->assertEquals(404, $this->getStatusOf($link), 'HTTP Status should be 404');
}
/**
* @param string $link
*
* @return int
*/
protected function getStatusOf($link) {
return Director::test($link)->getStatusCode();
} }
public function testRoles() { public function testRoles() {
$blog = $this->objFromFixture('Blog', 'fourthblog'); /**
$blog2 = $this->objFromFixture('Blog', 'firstblog'); * @var Blog $firstBlog
$postA = $this->objFromFixture('BlogPost', 'post-a'); */
$postB = $this->objFromFixture('BlogPost', 'post-b'); $firstBlog = $this->objFromFixture('Blog', 'FirstBlog');
$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 roleof /**
$this->assertEquals('Editor', $blog->RoleOf($editor)); * @var Blog $fourthBlog
$this->assertEquals('Contributor', $blog->RoleOf($contributor)); */
$this->assertEquals('Writer', $blog->RoleOf($writer)); $fourthBlog = $this->objFromFixture('Blog', 'FourthBlog');
$this->assertEmpty($blog->RoleOf($visitor));
/**
* @var BlogPost $postA
*/
$postA = $this->objFromFixture('BlogPost', 'PostA');
/**
* @var BlogPost $postB
*/
$postB = $this->objFromFixture('BlogPost', 'PostB');
/**
* @var BlogPost $postC
*/
$postC = $this->objFromFixture('BlogPost', 'PostC');
/**
* @var Member $editor
*/
$editor = $this->objFromFixture('Member', 'BlogEditor');
/**
* @var Member $writer
*/
$writer = $this->objFromFixture('Member', 'Writer');
/**
* @var Member $contributor
*/
$contributor = $this->objFromFixture('Member', 'Contributor');
/**
* @var Member $visitor
*/
$visitor = $this->objFromFixture('Member', 'Visitor');
$this->assertEquals('Editor', $fourthBlog->RoleOf($editor));
$this->assertEquals('Contributor', $fourthBlog->RoleOf($contributor));
$this->assertEquals('Writer', $fourthBlog->RoleOf($writer));
$this->assertEmpty($fourthBlog->RoleOf($visitor));
$this->assertEquals('Author', $postA->RoleOf($writer)); $this->assertEquals('Author', $postA->RoleOf($writer));
$this->assertEquals('Author', $postA->RoleOf($contributor)); $this->assertEquals('Author', $postA->RoleOf($contributor));
$this->assertEquals('Editor', $postA->RoleOf($editor)); $this->assertEquals('Editor', $postA->RoleOf($editor));
$this->assertEmpty($postA->RoleOf($visitor)); $this->assertEmpty($postA->RoleOf($visitor));
// Check that editors have all permissions on their own blog $this->assertTrue($fourthBlog->canEdit($editor));
$this->assertTrue($blog->canEdit($editor)); $this->assertFalse($firstBlog->canEdit($editor));
$this->assertFalse($blog2->canEdit($editor)); $this->assertTrue($fourthBlog->canAddChildren($editor));
$this->assertTrue($blog->canAddChildren($editor)); $this->assertFalse($firstBlog->canAddChildren($editor));
$this->assertFalse($blog2->canAddChildren($editor));
$this->assertTrue($postA->canEdit($editor)); $this->assertTrue($postA->canEdit($editor));
$this->assertTrue($postB->canEdit($editor)); $this->assertTrue($postB->canEdit($editor));
$this->assertTrue($postC->canEdit($editor)); $this->assertTrue($postC->canEdit($editor));
@ -131,11 +200,10 @@ class BlogTest extends SapphireTest {
$this->assertTrue($postB->canPublish($editor)); $this->assertTrue($postB->canPublish($editor));
$this->assertTrue($postC->canPublish($editor)); $this->assertTrue($postC->canPublish($editor));
// check rights of writers $this->assertFalse($fourthBlog->canEdit($writer));
$this->assertFalse($blog->canEdit($writer)); $this->assertFalse($firstBlog->canEdit($writer));
$this->assertFalse($blog2->canEdit($writer)); $this->assertTrue($fourthBlog->canAddChildren($writer));
$this->assertTrue($blog->canAddChildren($writer)); $this->assertFalse($firstBlog->canAddChildren($writer));
$this->assertFalse($blog2->canAddChildren($writer));
$this->assertTrue($postA->canEdit($writer)); $this->assertTrue($postA->canEdit($writer));
$this->assertFalse($postB->canEdit($writer)); $this->assertFalse($postB->canEdit($writer));
$this->assertTrue($postC->canEdit($writer)); $this->assertTrue($postC->canEdit($writer));
@ -143,11 +211,10 @@ class BlogTest extends SapphireTest {
$this->assertFalse($postB->canPublish($writer)); $this->assertFalse($postB->canPublish($writer));
$this->assertTrue($postC->canPublish($writer)); $this->assertTrue($postC->canPublish($writer));
// Check rights of contributors $this->assertFalse($fourthBlog->canEdit($contributor));
$this->assertFalse($blog->canEdit($contributor)); $this->assertFalse($firstBlog->canEdit($contributor));
$this->assertFalse($blog2->canEdit($contributor)); $this->assertTrue($fourthBlog->canAddChildren($contributor));
$this->assertTrue($blog->canAddChildren($contributor)); $this->assertFalse($firstBlog->canAddChildren($contributor));
$this->assertFalse($blog2->canAddChildren($contributor));
$this->assertTrue($postA->canEdit($contributor)); $this->assertTrue($postA->canEdit($contributor));
$this->assertFalse($postB->canEdit($contributor)); $this->assertFalse($postB->canEdit($contributor));
$this->assertTrue($postC->canEdit($contributor)); $this->assertTrue($postC->canEdit($contributor));
@ -155,11 +222,10 @@ class BlogTest extends SapphireTest {
$this->assertFalse($postB->canPublish($contributor)); $this->assertFalse($postB->canPublish($contributor));
$this->assertFalse($postC->canPublish($contributor)); $this->assertFalse($postC->canPublish($contributor));
// Check rights of non-cms user $this->assertFalse($fourthBlog->canEdit($visitor));
$this->assertFalse($blog->canEdit($visitor)); $this->assertFalse($firstBlog->canEdit($visitor));
$this->assertFalse($blog2->canEdit($visitor)); $this->assertFalse($fourthBlog->canAddChildren($visitor));
$this->assertFalse($blog->canAddChildren($visitor)); $this->assertFalse($firstBlog->canAddChildren($visitor));
$this->assertFalse($blog2->canAddChildren($visitor));
$this->assertFalse($postA->canEdit($visitor)); $this->assertFalse($postA->canEdit($visitor));
$this->assertFalse($postB->canEdit($visitor)); $this->assertFalse($postB->canEdit($visitor));
$this->assertFalse($postC->canEdit($visitor)); $this->assertFalse($postC->canEdit($visitor));
@ -167,5 +233,4 @@ class BlogTest extends SapphireTest {
$this->assertFalse($postB->canPublish($visitor)); $this->assertFalse($postB->canPublish($visitor));
$this->assertFalse($postC->canPublish($visitor)); $this->assertFalse($postC->canPublish($visitor));
} }
}
}

View File

@ -1,150 +1,148 @@
#####################################################
# Mock date is set to 2013-10-01 20:00:00 # Mock date is set to 2013-10-01 20:00:00
#####################################################
Group: Group:
admins: Administrators:
Title: Administrators Title: Administrators
editors: Editors:
Title: Editors Title: Editors
blog-users: BlogUsers:
Title: Blog Users Title: Blog Users
Code: blog-users Code: BlogUsers
Permission: Permission:
admins: Administrators:
Code: ADMIN Code: ADMIN
Group: =>Group.admins Group: =>Group.Administrators
editors: Editors:
Code: CMS_ACCESS_CMSMain Code: CMS_ACCESS_CMSMain
Group: =>Group.editors Group: =>Group.Editors
blog-users: BlogUsers:
Code: CMS_ACCESS_CMSMain Code: CMS_ACCESS_CMSMain
Group: =>Group.blog-users Group: =>Group.BlogUsers
SiteConfig: SiteConfig:
default: Default:
CanEditType: 'OnlyTheseUsers' CanEditType: 'OnlyTheseUsers'
CanCreateTopLevelType: 'OnlyTheseUsers' CanCreateTopLevelType: 'OnlyTheseUsers'
EditorGroups: =>Group.admins,=>Group.editors EditorGroups: =>Group.Administrators,=>Group.Editors
CreateTopLevelGroups: =>Group.admins,=>Group.editors CreateTopLevelGroups: =>Group.Administrators,=>Group.Editors
Member: Member:
admin: Admin:
FirstName: Test FirstName: Test
Surname: Administrator Surname: Administrator
Groups: =>Group.admins Groups: =>Group.Administrators
editor: Editor:
FirstName: Test FirstName: Test
Surname: Editor Surname: Editor
Groups: =>Group.editors Groups: =>Group.Editors
blogeditor: BlogEditor:
FirstName: Blog FirstName: Blog
Surname: Editor Surname: Editor
Groups: =>Group.blog-users Groups: =>Group.BlogUsers
writer: Writer:
FirstName: Blog FirstName: Blog
Surname: Writer Surname: Writer
Groups: =>Group.blog-users Groups: =>Group.BlogUsers
contributor: Contributor:
FirstName: Blog FirstName: Blog
Surname: Contributor Surname: Contributor
Groups: =>Group.blog-users Groups: =>Group.BlogUsers
visitor: Visitor:
FirstName: Blog FirstName: Blog
Surname: Visitor Surname: Visitor
Blog: Blog:
firstblog: FirstBlog:
Title: 'First Blog' Title: 'First Blog'
secondblog: SecondBlog:
Title: 'Second Blog' Title: 'Second Blog'
CanViewType: 'OnlyTheseUsers' CanViewType: 'OnlyTheseUsers'
CanEditType: 'OnlyTheseUsers' CanEditType: 'OnlyTheseUsers'
ViewerGroups: =>Group.admins ViewerGroups: =>Group.Administrators
EditorGroups: =>Group.admins EditorGroups: =>Group.Administrators
thirdblog: ThirdBlog:
Title: 'Third Blog' Title: 'Third Blog'
CanEditType: 'OnlyTheseUsers' CanEditType: 'OnlyTheseUsers'
EditorGroups: =>Group.editors EditorGroups: =>Group.Editors
fourthblog: FourthBlog:
Title: 'Fourth Blog' Title: 'Fourth Blog'
Editors: =>Member.blogeditor Editors: =>Member.BlogEditor
Writers: =>Member.writer Writers: =>Member.Writer
Contributors: =>Member.contributor Contributors: =>Member.Contributor
BlogTag: BlogTag:
firsttag: FirstTag:
Title: 'First Tag' Title: 'First Tag'
URLSegment: 'first-tag' URLSegment: 'first-tag'
Blog: =>Blog.firstblog Blog: =>Blog.FirstBlog
secondtag: SecondTag:
Title: 'Second Tag' Title: 'Second Tag'
URLSegment: 'second-tag' URLSegment: 'second-tag'
Blog: =>Blog.secondblog Blog: =>Blog.SecondBlog
thirdtag: ThirdTag:
Title: 'Third Tag' Title: 'Third Tag'
URLSegment: 'third-tag' URLSegment: 'third-tag'
Blog: =>Blog.thirdblog Blog: =>Blog.ThirdBlog
BlogCategory: BlogCategory:
firstcategory: FirstCategory:
Title: 'First Category' Title: 'First Category'
URLSegment: 'first-category' URLSegment: 'first-category'
Blog: =>Blog.firstblog Blog: =>Blog.FirstBlog
secondcategory: SecondCategory:
Title: 'Second Category' Title: 'Second Category'
URLSegment: 'second-category' URLSegment: 'second-category'
Blog: =>Blog.secondblog Blog: =>Blog.SecondBlog
thirdcategory: ThirdCategory:
Title: 'Third Category' Title: 'Third Category'
URLSegment: 'third-category' URLSegment: 'third-category'
Blog: =>Blog.thirdblog Blog: =>Blog.ThirdBlog
BlogPost: BlogPost:
blogpost1: FirstBlogPost:
Title: 'First post' Title: 'First Post'
URLSegment: first-post URLSegment: first-post
PublishDate: '2013-10-01 15:00:00' PublishDate: '2013-10-01 15:00:00'
Parent: =>Blog.firstblog Parent: =>Blog.FirstBlog
Tags: =>BlogTag.firsttag Tags: =>BlogTag.FirstTag
Categories: =>BlogCategory.firstcategory Categories: =>BlogCategory.FirstCategory
blogpost2: SecondBlogPost:
Title: 'Second post' Title: 'Second Post'
URLSegment: second-post URLSegment: second-post
PublishDate: '2013-09-01 15:00:00' PublishDate: '2013-09-01 15:00:00'
Parent: =>Blog.firstblog Parent: =>Blog.FirstBlog
blogpost3: ThirdBlogPost:
Title: 'Old post' Title: 'Old Post'
URLSegment: old-post URLSegment: old-post
PublishDate: '2012-01-09 15:00:00' PublishDate: '2012-01-09 15:00:00'
Parent: =>Blog.firstblog Parent: =>Blog.FirstBlog
futurepost: FirstFutureBlogPost:
Title: 'Future Post' Title: 'Future Post'
URLSegment: future-post URLSegment: future-post
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 Parent: =>Blog.FirstBlog
futurepost2: SecondFutureBlogPost:
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 Parent: =>Blog.FirstBlog
post-a: PostA:
Title: 'One Post' Title: 'One Post'
PublishDate: '2012-01-09 15:00:00' PublishDate: '2012-01-09 15:00:00'
Parent: =>Blog.fourthblog Parent: =>Blog.FourthBlog
Authors: =>Member.writer,=>Member.contributor Authors: =>Member.Writer,=>Member.Contributor
post-b: PostB:
Title: 'Second Post' Title: 'Second Post'
PublishDate: '2012-01-09 15:00:00' PublishDate: '2012-01-09 15:00:00'
Parent: =>Blog.fourthblog Parent: =>Blog.FourthBlog
Authors: =>Member.blogeditor Authors: =>Member.BlogEditor
post-c: PostC:
Title: 'Third Post' Title: 'Third Post'
PublishDate: '2012-01-09 15:00:00' PublishDate: '2012-01-09 15:00:00'
Parent: =>Blog.fourthblog Parent: =>Blog.FourthBlog
Authors: =>Member.blogeditor,=>Member.writer,=>Member.contributor Authors: =>Member.BlogEditor,=>Member.Writer,=>Member.Contributor