mirror of
https://github.com/silverstripe/silverstripe-blog
synced 2024-10-22 11:05:58 +02:00
Merge pull request #421 from robbieaverill/ss4
SilverStripe 4 compatibility
This commit is contained in:
commit
d5a8761acf
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
.ssh/*
|
||||
.bash*
|
||||
.profile
|
||||
vendor/*
|
||||
/vendor/*
|
||||
.sass-cache/*
|
||||
/node_modules/
|
||||
|
@ -6,4 +6,4 @@ checks:
|
||||
duplication: true
|
||||
|
||||
filter:
|
||||
paths: [code/*, tests/*]
|
||||
paths: [src/*, tests/*]
|
||||
|
44
.travis.yml
44
.travis.yml
@ -12,8 +12,6 @@ before_install:
|
||||
|
||||
env:
|
||||
global:
|
||||
- DB=MYSQL CORE_RELEASE=3.1
|
||||
|
||||
# Turn coverage off by default, as it's expensive time wise
|
||||
- COVERAGE=0
|
||||
|
||||
@ -21,33 +19,30 @@ env:
|
||||
- MODULE_PATH=blog
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm-nightly
|
||||
include:
|
||||
- php: 5.6
|
||||
env: DB=MYSQL COVERAGE=1
|
||||
- php: 5.5
|
||||
env: DB=MYSQL
|
||||
- php: 5.6
|
||||
env: DB=PGSQL
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=3.2
|
||||
- php: 5.6
|
||||
env: DB=PGSQL CORE_RELEASE=3.2
|
||||
- php: 5.4
|
||||
env: DB=SQLITE
|
||||
- php: 5.3
|
||||
env: DB=MYSQL
|
||||
- php: hhvm
|
||||
env: DB=MYSQL
|
||||
- php: 7.1
|
||||
env: DB=MYSQL CORE_RELEASE=4 COVERAGE=1
|
||||
|
||||
- php: 5.5
|
||||
env: DB=MYSQL CORE_RELEASE=4
|
||||
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=4
|
||||
- php: 5.6
|
||||
env: DB=PGSQL CORE_RELEASE=4
|
||||
- php: 5.6
|
||||
env: DB=SQLITE CORE_RELEASE=4
|
||||
|
||||
- php: 7.0
|
||||
env: DB=MYSQL CORE_RELEASE=4
|
||||
|
||||
before_script:
|
||||
- phpenv rehash
|
||||
- composer self-update || true
|
||||
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
|
||||
- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support
|
||||
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss --require "silverstripe/comments" --require "silverstripe/widgets"
|
||||
- cd ~/builds/ss
|
||||
- mv "$MODULE_PATH/phpunit.xml.dist" .
|
||||
|
||||
#Execute tests with or without coverage
|
||||
script:
|
||||
@ -57,9 +52,8 @@ script:
|
||||
# Execute tests with coverage. Do this for a small
|
||||
- "if [ \"$COVERAGE\" = \"1\" ]; then vendor/bin/phpunit --coverage-clover=coverage.clover $MODULE_PATH/tests/; fi"
|
||||
|
||||
#Upload coverage even if there is a failure
|
||||
after_script:
|
||||
# Upload code coverage when tests pass
|
||||
after_success:
|
||||
- "if [ \"$COVERAGE\" = \"1\" ]; then mv coverage.clover ~/build/$TRAVIS_REPO_SLUG/; fi"
|
||||
- cd ~/build/$TRAVIS_REPO_SLUG
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- "if [ \"$COVERAGE\" = \"1\" ]; then travis_retry codecov && travis_retry php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi"
|
||||
- "if [ \"$COVERAGE\" = \"1\" ]; then travis_retry codecov; fi"
|
||||
|
33
.upgrade.yml
Normal file
33
.upgrade.yml
Normal file
@ -0,0 +1,33 @@
|
||||
mappings:
|
||||
GridFieldCategorisationConfig: SilverStripe\Blog\Admin\GridFieldCategorisationConfig
|
||||
GridFieldFormAction: SilverStripe\Blog\Admin\GridFieldFormAction
|
||||
GridFieldMergeAction: SilverStripe\Blog\Admin\GridFieldMergeAction
|
||||
BlogCommentExtension: SilverStripe\Blog\Model\BlogCommentExtension
|
||||
BlogFilter: SilverStripe\Blog\Model\BlogFilter
|
||||
BlogFilter_GridField: SilverStripe\Blog\Model\BlogFilter\BlogFilterGridField
|
||||
BlogMemberExtension: SilverStripe\Blog\Model\BlogMemberExtension
|
||||
BlogPostFilter: SilverStripe\Blog\Model\BlogPostFilter
|
||||
BlogPostNotifications: SilverStripe\Blog\Model\BlogPostNotifications
|
||||
Blog: SilverStripe\Blog\Model\Blog
|
||||
Blog_Controller: SilverStripe\Blog\Controllers\BlogController
|
||||
BlogController: SilverStripe\Blog\Controllers\BlogController
|
||||
BlogCategory: SilverStripe\Blog\Model\BlogCategory
|
||||
BlogPost: SilverStripe\Blog\Model\BlogPost
|
||||
BlogPost_Controller: SilverStripe\Blog\Controllers\BlogPostController
|
||||
BlogPostController: SilverStripe\Blog\Controllers\BlogPostController
|
||||
BlogTag: SilverStripe\Blog\Model\BlogTag
|
||||
CategorisationObject: SilverStripe\Blog\Model\CategorisationObject
|
||||
BlogAdminSidebar: SilverStripe\Blog\Forms\BlogAdminSidebar
|
||||
GridFieldAddByDBField: SilverStripe\Blog\Forms\GridField\GridFieldAddByDBField
|
||||
GridFieldBlogPostState: SilverStripe\Blog\Forms\GridField\GridFieldBlogPostState
|
||||
GridFieldConfig_BlogPost: SilverStripe\Blog\Forms\GridField\GridFieldConfig_BlogPost
|
||||
BlogArchiveWidget: SilverStripe\Blog\Widgets\BlogArchiveWidget
|
||||
BlogArchiveWidget_Controller: SilverStripe\Blog\Widgets\BlogArchiveWidgetController
|
||||
BlogCategoriesWidget: SilverStripe\Blog\Widgets\BlogCategoriesWidget
|
||||
BlogCategoriesWidget_Controller: SilverStripe\Blog\Widgets\BlogCategoriesWidgetController
|
||||
BlogRecentPostsWidget: SilverStripe\Blog\Widgets\BlogRecentPostsWidget
|
||||
BlogRecentPostsWidget_Controller: SilverStripe\Blog\Widgets\BlogRecentPostsWidgetController
|
||||
BlogTagsCloudWidget: SilverStripe\Blog\Widgets\BlogTagsCloudWidget
|
||||
BlogTagsCloudWidget_Controller: SilverStripe\Blog\Widgets\BlogTagsCloudWidgetController
|
||||
BlogTagsWidget: SilverStripe\Blog\Widgets\BlogTagsWidget
|
||||
BlogTagsWidget_Controller: SilverStripe\Blog\Widgets\BlogTagsWidgetController
|
20
README.md
20
README.md
@ -10,16 +10,16 @@
|
||||
|
||||
|
||||
## Documentation
|
||||
[User guide](docs/en/userguide/index.md)
|
||||
|
||||
[Developer documentation](docs/en/index.md)
|
||||
* [User guide](docs/en/userguide/index.md)
|
||||
* [Developer documentation](docs/en/index.md)
|
||||
|
||||
## Requirements
|
||||
|
||||
```
|
||||
silverstripe/cms: ^3.1
|
||||
silverstripe/lumberjack: ^1.1
|
||||
silverstripe/tagfield: ^1.0
|
||||
silverstripe/cms: ^4.0
|
||||
silverstripe/lumberjack: ^2.0
|
||||
silverstripe/tagfield: ^2.0
|
||||
```
|
||||
|
||||
### Suggested Modules
|
||||
@ -35,8 +35,12 @@ silverstripe/comments: *
|
||||
composer require silverstripe/blog
|
||||
```
|
||||
|
||||
## Upgrading legacy blog to 2.x
|
||||
## Upgrading
|
||||
|
||||
### Upgrading from 2.x to 3.x
|
||||
|
||||
Aside from the framework and CMS upgrades required the blog module should not require anything extra to be completed.
|
||||
|
||||
### Upgrading legacy blog to 2.x
|
||||
|
||||
If you're upgrading from blog version 1.0 to 2.x you will need to run the `BlogMigrationTask`. Run the task using `dev/tasks/BlogMigrationTask` either via the browser or sake CLI to migrate your legacy blog to the new version data structure.
|
||||
|
||||
|
||||
|
@ -5,4 +5,4 @@
|
||||
*
|
||||
* @return string
|
||||
**/
|
||||
define('BLOGGER_DIR', ltrim(Director::makeRelative(realpath(__DIR__)), DIRECTORY_SEPARATOR));
|
||||
define('BLOGGER_DIR', basename(dirname(__FILE__)));
|
||||
|
@ -3,15 +3,15 @@ Name: blogcommentsconfig
|
||||
Only:
|
||||
moduleexists: comments
|
||||
---
|
||||
Comment:
|
||||
SilverStripe\Comments\Model\Comment:
|
||||
extensions:
|
||||
- BlogCommentExtension
|
||||
- SilverStripe\Blog\Model\BlogCommentExtension
|
||||
|
||||
---
|
||||
Name: blogcommentnotifications
|
||||
Only:
|
||||
moduleexists: 'comment-notifications'
|
||||
---
|
||||
BlogPost:
|
||||
SilverStripe\Blog\Model\BlogPost:
|
||||
extensions:
|
||||
- BlogPostNotifications
|
||||
- SilverStripe\Blog\Model\BlogPostNotifications
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
Name: blogconfig
|
||||
---
|
||||
Member:
|
||||
SilverStripe\Security\Member:
|
||||
extensions:
|
||||
- BlogMemberExtension
|
||||
- SilverStripe\Blog\Model\BlogMemberExtension
|
||||
|
9
_config/legacy.yml
Normal file
9
_config/legacy.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
Name: bloglegacy
|
||||
---
|
||||
SilverStripe\ORM\DatabaseAdmin:
|
||||
classname_value_remapping:
|
||||
Blog: SilverStripe\Blog\Model\Blog
|
||||
BlogCategory: SilverStripe\Blog\Model\BlogCategory
|
||||
BlogPost: SilverStripe\Blog\Model\BlogPost
|
||||
BlogTag: SilverStripe\Blog\Model\BlogTag
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
class GridFieldCategorisationConfig extends GridFieldConfig_RecordEditor
|
||||
{
|
||||
/**
|
||||
* @param int $itemsPerPage
|
||||
* @param array|SS_List $mergeRecords
|
||||
* @param string $parentType
|
||||
* @param string $parentMethod
|
||||
* @param string $childMethod
|
||||
*/
|
||||
public function __construct($itemsPerPage = 15, $mergeRecords, $parentType, $parentMethod, $childMethod)
|
||||
{
|
||||
parent::__construct($itemsPerPage);
|
||||
|
||||
$this->removeComponentsByType('GridFieldAddNewButton');
|
||||
|
||||
$this->addComponent(
|
||||
new GridFieldAddByDBField('buttons-before-left')
|
||||
);
|
||||
|
||||
$this->addComponent(
|
||||
new GridFieldMergeAction($mergeRecords, $parentType, $parentMethod, $childMethod)
|
||||
);
|
||||
|
||||
/**
|
||||
* @var GridFieldDataColumns $columns
|
||||
*/
|
||||
$columns = $this->getComponentByType('GridFieldDataColumns');
|
||||
|
||||
$columns->setFieldFormatting(array(
|
||||
'BlogPostsCount' => function ($value, CategorisationObject $item) {
|
||||
return $item->BlogPosts()->Count();
|
||||
}
|
||||
));
|
||||
|
||||
$this->changeColumnOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders GridField columns so that Actions is last.
|
||||
*/
|
||||
protected function changeColumnOrder()
|
||||
{
|
||||
/**
|
||||
* @var GridFieldDataColumns $columns
|
||||
*/
|
||||
$columns = $this->getComponentByType('GridFieldDataColumns');
|
||||
|
||||
$columns->setDisplayFields(array(
|
||||
'Title' => 'Title',
|
||||
'BlogPostsCount' => 'Posts',
|
||||
'MergeAction' => 'MergeAction',
|
||||
'Actions' => 'Actions',
|
||||
));
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @property int $ParentID
|
||||
* @property string $Date
|
||||
* @property string $PublishDate
|
||||
* @property string $Tags
|
||||
*/
|
||||
class BlogEntry extends BlogPost implements MigratableObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $hide_ancestor = 'BlogEntry';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Date' => 'SS_Datetime',
|
||||
'Author' => 'Text',
|
||||
'Tags' => 'Text',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
//Migrate comma separated tags into BlogTag objects.
|
||||
foreach ($this->TagNames() as $tag) {
|
||||
$existingTag = BlogTag::get()->filter(array('Title' => $tag, 'BlogID' => $this->ParentID));
|
||||
if ($existingTag->count()) {
|
||||
//if tag already exists we will simply add it to this post.
|
||||
$tagObject = $existingTag->First();
|
||||
} else {
|
||||
//if the tag is now we create it and add it to this post.
|
||||
$tagObject = new BlogTag();
|
||||
$tagObject->Title = $tag;
|
||||
$tagObject->BlogID = $this->ParentID;
|
||||
$tagObject->write();
|
||||
}
|
||||
|
||||
if ($tagObject) {
|
||||
$this->Tags()->add($tagObject);
|
||||
}
|
||||
}
|
||||
|
||||
//Store if the original entity was published or not (draft)
|
||||
$published = $this->IsPublished();
|
||||
// If a user has subclassed BlogEntry, it should not be turned into a BlogPost.
|
||||
if ($this->ClassName === 'BlogEntry') {
|
||||
$this->ClassName = 'BlogPost';
|
||||
$this->RecordClassName = 'BlogPost';
|
||||
}
|
||||
//Migrate these key data attributes
|
||||
$this->PublishDate = $this->Date;
|
||||
$this->AuthorNames = $this->Author;
|
||||
$this->InheritSideBar = true;
|
||||
|
||||
//Write and additionally publish the item if it was published before.
|
||||
$this->write();
|
||||
if ($published) {
|
||||
$this->publish('Stage', 'Live');
|
||||
$message = "PUBLISHED: ";
|
||||
} else {
|
||||
$message = "DRAFT: ";
|
||||
}
|
||||
|
||||
return $message . $this->Title;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*/
|
||||
class BlogEntry_Controller extends BlogPost_Controller
|
||||
{
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*/
|
||||
class BlogHolder extends BlogTree implements MigratableObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $hide_ancestor = 'BlogHolder';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'AllowCustomAuthors' => 'Boolean',
|
||||
'ShowFullEntry' => 'Boolean',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Owner' => 'Member',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//Overload these to stop the Uncaught Exception: Object->__call(): the method 'parent' does not exist on 'BlogHolder' error.
|
||||
public function validURLSegment()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public function syncLinkTracking()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$published = $this->IsPublished();
|
||||
|
||||
if ($this->ClassName === 'BlogHolder') {
|
||||
$this->ClassName = 'Blog';
|
||||
$this->RecordClassName = 'Blog';
|
||||
$this->PostsPerPage = 10;
|
||||
$this->write();
|
||||
}
|
||||
|
||||
if ($published) {
|
||||
$this->publish('Stage', 'Live');
|
||||
$message = "PUBLISHED: ";
|
||||
} else {
|
||||
$message = "DRAFT: ";
|
||||
}
|
||||
|
||||
return $message . $this->Title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*/
|
||||
class BlogHolder_Controller extends BlogTree_Controller
|
||||
{
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*/
|
||||
class BlogTree extends Page implements MigratableObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $hide_ancestor = 'BlogTree';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar(255)',
|
||||
'LandingPageFreshness' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$published = $this->IsPublished();
|
||||
if ($this->ClassName === 'BlogTree') {
|
||||
$this->ClassName = 'Page';
|
||||
$this->RecordClassName = 'Page';
|
||||
$this->write();
|
||||
}
|
||||
if ($published) {
|
||||
$this->publish('Stage', 'Live');
|
||||
$message = "PUBLISHED: ";
|
||||
} else {
|
||||
$message = "DRAFT: ";
|
||||
}
|
||||
|
||||
return $message . $this->Title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*/
|
||||
class BlogTree_Controller extends Page_Controller
|
||||
{
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
class BlogMigrationTask extends MigrationTask
|
||||
{
|
||||
/**
|
||||
* Should this task be invoked automatically via dev/build?
|
||||
*
|
||||
* @config
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $run_during_dev_build = true;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$classes = ClassInfo::implementorsOf('MigratableObject');
|
||||
|
||||
$this->message('Migrating legacy blog records');
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$this->upClass($class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
*/
|
||||
protected function message($text)
|
||||
{
|
||||
if (Controller::curr() instanceof DatabaseAdmin) {
|
||||
DB::alteration_message($text, 'obsolete');
|
||||
} else {
|
||||
echo $text . "<br/>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate records of a single class
|
||||
*
|
||||
* @param string $class
|
||||
* @param null|string $stage
|
||||
*/
|
||||
protected function upClass($class)
|
||||
{
|
||||
if (!class_exists($class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_subclass_of($class, 'SiteTree')) {
|
||||
$items = SiteTree::get()->filter('ClassName', $class);
|
||||
} else {
|
||||
$items = $class::get();
|
||||
}
|
||||
|
||||
if ($count = $items->count()) {
|
||||
$this->message(
|
||||
sprintf(
|
||||
'Migrating %s legacy %s records.',
|
||||
$count,
|
||||
$class
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($items as $item) {
|
||||
$cancel = $item->extend('onBeforeUp');
|
||||
|
||||
if ($cancel && min($cancel) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var MigratableObject $item
|
||||
*/
|
||||
$result = $item->up();
|
||||
$this->message($result);
|
||||
|
||||
$item->extend('onAfterUp');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$this->message('BlogMigrationTask::down() not implemented');
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
interface MigratableObject
|
||||
{
|
||||
/**
|
||||
* Migrate the object up to the current version.
|
||||
*/
|
||||
public function up();
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists('Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 2.0
|
||||
*
|
||||
* @property string $DisplayMode
|
||||
* @property string $ArchiveType
|
||||
*/
|
||||
class ArchiveWidget extends BlogArchiveWidget implements MigratableObject
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'DisplayMode' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $only_available_in = array(
|
||||
'none',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ($this->DisplayMode) {
|
||||
$this->ArchiveType = 'Monthly';
|
||||
|
||||
if ($this->DisplayMode === 'year') {
|
||||
$this->ArchiveType = 'Yearly';
|
||||
}
|
||||
}
|
||||
|
||||
$this->ClassName = 'BlogArchiveWidget';
|
||||
$this->write();
|
||||
return "Migrated " . $this->ArchiveType . " archive widget";
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists('Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of tags associated with blog posts.
|
||||
*
|
||||
* @package blog
|
||||
*/
|
||||
class TagCloudWidget extends BlogTagsWidget implements MigratableObject
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar',
|
||||
'Limit' => 'Int',
|
||||
'Sortby' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $only_available_in = array(
|
||||
'none',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$this->ClassName = 'BlogTagsWidget';
|
||||
$this->write();
|
||||
return "Migrated " . $this->Title . " widget";
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This is responsible for filtering only published posts to users who do not have permission to
|
||||
* view non-published posts.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class BlogPostFilter extends DataExtension
|
||||
{
|
||||
/**
|
||||
* Augment queries so that we don't fetch unpublished articles.
|
||||
*
|
||||
* @param SQLQuery $query
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query)
|
||||
{
|
||||
$stage = Versioned::current_stage();
|
||||
|
||||
if (Controller::curr() instanceof LeftAndMain) {
|
||||
return;
|
||||
}
|
||||
|
||||
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, lazy loading includes the
|
||||
* BlogPost table in its query. Leaving this table out means the default sort order column
|
||||
* PublishDate causes an error.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$dataQuery->innerJoin('BlogPost', '"SiteTree"."ID" = "BlogPost"."ID"');
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Adds URLSegment functionality to Tags & Categories.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class URLSegmentExtension extends DataExtension
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'URLSegment' => 'Varchar(255)',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if ($this->owner->BlogID) {
|
||||
$this->owner->generateURLSegment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique URLSegment from the title.
|
||||
*
|
||||
* @param int $increment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateURLSegment($increment = null)
|
||||
{
|
||||
$filter = new URLSegmentFilter();
|
||||
|
||||
// Setting this to on. Because of the UI flow, it would be quite a lot of work
|
||||
// to support turning this off. (ie. the add by title flow would not work).
|
||||
// If this becomes a problem we can approach it then.
|
||||
// @see https://github.com/silverstripe/silverstripe-blog/issues/376
|
||||
$filter->setAllowMultibyte(true);
|
||||
|
||||
$this->owner->URLSegment = $filter->filter($this->owner->Title);
|
||||
|
||||
if (is_int($increment)) {
|
||||
$this->owner->URLSegment .= '-' . $increment;
|
||||
}
|
||||
|
||||
// Postgres use '' instead of 0 as an emtpy blog ID
|
||||
// Without this all the tests fail
|
||||
if (!$this->owner->BlogID) {
|
||||
$this->owner->BlogID = 0;
|
||||
}
|
||||
|
||||
$duplicate = DataList::create($this->owner->ClassName)->filter(array(
|
||||
'URLSegment' => $this->owner->URLSegment,
|
||||
'BlogID' => $this->owner->BlogID,
|
||||
));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A blog category for generalising blog posts.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*
|
||||
* @method Blog Blog()
|
||||
*
|
||||
* @property string $URLSegment
|
||||
* @property int $BlogID
|
||||
*/
|
||||
class BlogCategory extends DataObject implements CategorisationObject
|
||||
{
|
||||
|
||||
/**
|
||||
* Use an exception code so that attempted writes can continue on
|
||||
* duplicate errors.
|
||||
*
|
||||
* @const string
|
||||
* This must be a string because ValidationException has decided we can't use int
|
||||
*/
|
||||
const DUPLICATE_EXCEPTION = "DUPLICATE";
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array(
|
||||
'BlogPosts' => 'BlogPost',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $extensions = array(
|
||||
'URLSegmentExtension',
|
||||
);
|
||||
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
public function BlogPosts()
|
||||
{
|
||||
$blogPosts = parent::BlogPosts();
|
||||
|
||||
$this->extend("updateGetBlogPosts", $blogPosts);
|
||||
|
||||
return $blogPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
TextField::create('Title', _t('BlogCategory.Title', 'Title'))
|
||||
);
|
||||
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function validate()
|
||||
{
|
||||
$validation = parent::validate();
|
||||
if($validation->valid()) {
|
||||
// Check for duplicate categories
|
||||
$blog = $this->Blog();
|
||||
if($blog && $blog->exists()) {
|
||||
$existing = $blog->Categories()->filter('Title', $this->Title);
|
||||
if($this->ID) {
|
||||
$existing = $existing->exclude('ID', $this->ID);
|
||||
}
|
||||
if($existing->count() > 0) {
|
||||
$validation->error(_t(
|
||||
'BlogCategory.Duplicate',
|
||||
'A blog category already exists with that name'
|
||||
), BlogCategory::DUPLICATE_EXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $validation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a relative link to this category.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
return Controller::join_links($this->Blog()->Link(), 'category', $this->URLSegment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canView($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
return $this->Blog()->canView($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
$permission = Blog::config()->grant_user_permission;
|
||||
|
||||
return Permission::checkMember($member, $permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A blog tag for keyword descriptions of a blog post.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*
|
||||
* @method Blog Blog()
|
||||
*
|
||||
* @property string $Title
|
||||
* @property string $URLSegment
|
||||
* @property int $BlogID
|
||||
*/
|
||||
class BlogTag extends DataObject implements CategorisationObject
|
||||
{
|
||||
|
||||
/**
|
||||
* Use an exception code so that attempted writes can continue on
|
||||
* duplicate errors.
|
||||
*
|
||||
* @const string
|
||||
* This must be a string because ValidationException has decided we can't use int
|
||||
*/
|
||||
const DUPLICATE_EXCEPTION = "DUPLICATE";
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array(
|
||||
'BlogPosts' => 'BlogPost',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $extensions = array(
|
||||
'URLSegmentExtension',
|
||||
);
|
||||
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
public function BlogPosts()
|
||||
{
|
||||
$blogPosts = parent::BlogPosts();
|
||||
|
||||
$this->extend("updateGetBlogPosts", $blogPosts);
|
||||
|
||||
return $blogPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
TextField::create('Title', _t('BlogTag.Title', 'Title'))
|
||||
);
|
||||
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function validate()
|
||||
{
|
||||
$validation = parent::validate();
|
||||
if($validation->valid()) {
|
||||
// Check for duplicate tags
|
||||
$blog = $this->Blog();
|
||||
if($blog && $blog->exists()) {
|
||||
$existing = $blog->Tags()->filter('Title', $this->Title);
|
||||
if($this->ID) {
|
||||
$existing = $existing->exclude('ID', $this->ID);
|
||||
}
|
||||
if($existing->count() > 0) {
|
||||
$validation->error(_t(
|
||||
'BlogTag.Duplicate',
|
||||
'A blog tags already exists with that name'
|
||||
), BlogTag::DUPLICATE_EXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $validation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a relative URL for the tag link.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canView($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
return $this->Blog()->canView($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
$permission = Blog::config()->grant_user_permission;
|
||||
|
||||
return Permission::checkMember($member, $permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists("Widget")) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method Blog Blog()
|
||||
*/
|
||||
class BlogCategoriesWidget extends Widget
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $title = 'Categories';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $cmsTitle = 'Blog Categories';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $description = 'Displays a list of blog categories.';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Limit' => 'Int',
|
||||
'Order' => 'Varchar',
|
||||
'Direction' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$this->beforeUpdateCMSFields(function (FieldList $fields) {
|
||||
$fields[] = DropdownField::create(
|
||||
'BlogID', _t('BlogCategoriesWidget.Blog', 'Blog'), Blog::get()->map()
|
||||
);
|
||||
|
||||
$fields[] = NumericField::create(
|
||||
'Limit', _t('BlogCategoriesWidget.Limit.Label', 'Limit'), 0
|
||||
)
|
||||
->setDescription(_t('BlogCategoriesWidget.Limit.Description', 'Limit the number of categories shown by this widget (set to 0 to show all categories).'))
|
||||
->setMaxLength(3);
|
||||
|
||||
$fields[] = DropdownField::create(
|
||||
'Order', _t('BlogCategoriesWidget.Sort.Label', 'Sort'), array('Title' => 'Title', 'Created' => 'Created', 'LastEdited' => 'Updated')
|
||||
)
|
||||
->setDescription(_t('BlogCategoriesWidget.Sort.Description', 'Change the order of categories shown by this widget.'));
|
||||
|
||||
$fields[] = DropdownField::create(
|
||||
'Direction', _t('BlogCategoriesWidget.Direction.Label', 'Direction'), array('ASC' => 'Ascending', 'DESC' => 'Descending')
|
||||
)
|
||||
->setDescription(_t('BlogCategoriesWidget.Direction.Description', 'Change the direction of ordering of categories shown by this widget.'));
|
||||
});
|
||||
|
||||
return parent::getCMSFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
$blog = $this->Blog();
|
||||
|
||||
if (!$blog) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$query = $blog->Categories();
|
||||
|
||||
if ($this->Limit) {
|
||||
$query = $query->limit(Convert::raw2sql($this->Limit));
|
||||
}
|
||||
|
||||
if ($this->Order && $this->Direction) {
|
||||
$query = $query->sort(Convert::raw2sql($this->Order), Convert::raw2sql($this->Direction));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
class BlogCategoriesWidget_Controller extends Widget_Controller
|
||||
{
|
||||
}
|
@ -1,39 +1,45 @@
|
||||
{
|
||||
"name": "silverstripe/blog",
|
||||
"description": "A fresh take on blogging in Silverstripe set out to tackle the issue of a cluttered Site Tree.",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"blog",
|
||||
"news"
|
||||
],
|
||||
"type": "silverstripe-module",
|
||||
"require": {
|
||||
"silverstripe/cms": "^3.1.0",
|
||||
"silverstripe/lumberjack": "~1.1",
|
||||
"silverstripe/tagfield": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~3.7@stable"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"license": "BSD-2-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Strong",
|
||||
"email": "github@michaelstrong.co.uk"
|
||||
}
|
||||
],
|
||||
"suggest": {
|
||||
"silverstripe/widgets": "Some widgets come with the blog which are compatible with the widgets module.",
|
||||
"silverstripe/comments": "This module adds comments to your blog."
|
||||
},
|
||||
"replace": {
|
||||
"micmania1/silverstripe-blog": "*"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
"name": "silverstripe/blog",
|
||||
"description": "A fresh take on blogging in Silverstripe set out to tackle the issue of a cluttered Site Tree.",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"blog",
|
||||
"news"
|
||||
],
|
||||
"type": "silverstripe-module",
|
||||
"require": {
|
||||
"silverstripe/cms": "^4.0",
|
||||
"silverstripe/lumberjack": "^2.0",
|
||||
"silverstripe/tagfield": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~4.8"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SilverStripe\\Blog\\": "src/",
|
||||
"SilverStripe\\Blog\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"license": "BSD-2-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Strong",
|
||||
"email": "github@michaelstrong.co.uk"
|
||||
}
|
||||
],
|
||||
"suggest": {
|
||||
"silverstripe/widgets": "Some widgets come with the blog which are compatible with the widgets module.",
|
||||
"silverstripe/comments": "This module adds comments to your blog."
|
||||
},
|
||||
"replace": {
|
||||
"micmania1/silverstripe-blog": "*"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
|
23
config.rb
23
config.rb
@ -1,23 +0,0 @@
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "css"
|
||||
sass_dir = "scss"
|
||||
javascripts_dir = "javascript"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
line_comments = false
|
||||
|
||||
|
||||
# If you prefer the indented syntax, you might want to regenerate this
|
||||
# project again passing --syntax sass, or you can uncomment this:
|
||||
# preferred_syntax = :sass
|
||||
# and then run:
|
||||
# sass-convert -R --from scss --to sass scss scss && rm -rf sass && mv scss sass
|
85
css/blog.css
85
css/blog.css
@ -1,58 +1,43 @@
|
||||
.no-sidebar .content-container.size3of4 {
|
||||
width: 75%;
|
||||
}
|
||||
width: 75%; }
|
||||
|
||||
.blog-entry .post-image img {
|
||||
width: 98.75%;
|
||||
}
|
||||
width: 98.75%; }
|
||||
|
||||
.blog-sidebar .WidgetHolder ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
.blog-sidebar .WidgetHolder ul li {
|
||||
list-style-type: none;
|
||||
}
|
||||
margin-left: 0; }
|
||||
.blog-sidebar .WidgetHolder ul li {
|
||||
list-style-type: none; }
|
||||
|
||||
ul.blogTagCloud {
|
||||
list-style-type: none;
|
||||
clear: both;
|
||||
}
|
||||
ul.blogTagCloud li {
|
||||
float: left;
|
||||
display: inline;
|
||||
padding-right: 8px;
|
||||
}
|
||||
ul.blogTagCloud li a span {
|
||||
clear: both; }
|
||||
ul.blogTagCloud li {
|
||||
float: left;
|
||||
line-height: 30px; text-align: center;
|
||||
padding: 0px;
|
||||
}
|
||||
ul.blogTagCloud .tagCount10 {
|
||||
font-size: 26pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount9 {
|
||||
font-size: 24pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount8 {
|
||||
font-size: 22pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount7 {
|
||||
font-size: 20pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount6 {
|
||||
font-size: 18pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount5 {
|
||||
font-size: 16pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount4 {
|
||||
font-size: 14pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount3 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount2 {
|
||||
font-size: 10pt;
|
||||
}
|
||||
ul.blogTagCloud .tagCount1 {
|
||||
font-size: 8pt;
|
||||
}
|
||||
display: inline;
|
||||
padding-right: 8px; }
|
||||
ul.blogTagCloud li a span {
|
||||
float: left;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
padding: 0px; }
|
||||
ul.blogTagCloud .tagCount10 {
|
||||
font-size: 26pt; }
|
||||
ul.blogTagCloud .tagCount9 {
|
||||
font-size: 24pt; }
|
||||
ul.blogTagCloud .tagCount8 {
|
||||
font-size: 22pt; }
|
||||
ul.blogTagCloud .tagCount7 {
|
||||
font-size: 20pt; }
|
||||
ul.blogTagCloud .tagCount6 {
|
||||
font-size: 18pt; }
|
||||
ul.blogTagCloud .tagCount5 {
|
||||
font-size: 16pt; }
|
||||
ul.blogTagCloud .tagCount4 {
|
||||
font-size: 14pt; }
|
||||
ul.blogTagCloud .tagCount3 {
|
||||
font-size: 12pt; }
|
||||
ul.blogTagCloud .tagCount2 {
|
||||
font-size: 10pt; }
|
||||
ul.blogTagCloud .tagCount1 {
|
||||
font-size: 8pt; }
|
||||
|
175
css/cms.css
175
css/cms.css
@ -7,81 +7,66 @@
|
||||
/*
|
||||
* Sprite maps & Icons
|
||||
*/
|
||||
.blog-icon-sprite, .gridfield-icon .blog-icon-timer {
|
||||
background-image: url('../images/blog-icon-s0a5ab5f851.png');
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.gridfield-icon .blog-icon-timer {
|
||||
background-position: 0 0;
|
||||
}
|
||||
.gridfield-icon.blog-icon-timer {
|
||||
background: url("../images/blog-icon/timer.png") center no-repeat; }
|
||||
|
||||
#FeaturedImage .middleColumn {
|
||||
clear: none;
|
||||
float: left;
|
||||
}
|
||||
float: left; }
|
||||
|
||||
.blog-admin-sidebar {
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar {
|
||||
width: 280px;
|
||||
border-right: none;
|
||||
border-left: 1px solid #C0C0C2;
|
||||
position: absolute;
|
||||
position: absolute !important;
|
||||
/* overrides cms !imporant style */
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.blog-admin-sidebar .cms-panel-toggle a {
|
||||
text-align: left;
|
||||
}
|
||||
.blog-admin-sidebar ~ .blog-admin-outer {
|
||||
width: 100%;
|
||||
padding-right: 280px;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title label {
|
||||
float: none;
|
||||
}
|
||||
.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title .middleColumn, .blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title input {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
.blog-admin-sidebar .cms-content-view > .field + .field {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.blog-admin-sidebar .cms-content-view > .field.urlsegment .preview {
|
||||
padding-top: 0;
|
||||
line-height: 25px;
|
||||
}
|
||||
.blog-admin-sidebar .cms-content-view > .field.urlsegment .edit {
|
||||
float: right;
|
||||
}
|
||||
.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .date {
|
||||
width: 60%;
|
||||
}
|
||||
.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .time {
|
||||
width: 36%;
|
||||
float: right;
|
||||
}
|
||||
.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn .middleColumn, .blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn input {
|
||||
width: 100%;
|
||||
}
|
||||
.blog-admin-sidebar.collapsed ~ .blog-admin-outer {
|
||||
padding-right: 41px;
|
||||
}
|
||||
.blog-admin-sidebar.cms-content-tools .cms-panel-content {
|
||||
width: auto;
|
||||
}
|
||||
top: 0;
|
||||
height: 100%; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle a {
|
||||
text-align: left;
|
||||
margin: 0; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-panel-toggle.south {
|
||||
border-top: 1px solid #aaaaaa; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer {
|
||||
width: 100%;
|
||||
padding-right: 280px;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title label {
|
||||
float: none; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title .middleColumn, .has-panel .cms-content-tools.blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title input {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-left: 0; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field + .field {
|
||||
margin-top: 10px; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.urlsegment .preview {
|
||||
padding-top: 0;
|
||||
line-height: 25px; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.urlsegment .edit {
|
||||
float: right; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .date {
|
||||
width: 60%; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .time {
|
||||
width: 36%;
|
||||
float: right; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn .middleColumn, .has-panel .cms-content-tools.blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn input {
|
||||
width: 100%; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar.collapsed ~ .blog-admin-outer {
|
||||
padding-right: 41px; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar.collapsed ~ .blog-admin-outer #Root_Main {
|
||||
margin-right: 15px; }
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar.cms-content-tools .cms-panel-content {
|
||||
width: auto; }
|
||||
|
||||
.toggle-description {
|
||||
text-indent: -1000000px;
|
||||
@ -89,63 +74,59 @@
|
||||
background: url("../images/information.png") no-repeat center center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
margin-left: 4px; }
|
||||
|
||||
.middleColumn.toggle-description-correct-middle {
|
||||
margin-left: 0;
|
||||
float: left;
|
||||
width: 416px;
|
||||
}
|
||||
width: 416px; }
|
||||
|
||||
label.right.toggle-description-correct-right {
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
clear: none;
|
||||
float: left;
|
||||
}
|
||||
float: left; }
|
||||
|
||||
.description.toggle-description-correct-description {
|
||||
width: 416px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
padding: 12px 0; }
|
||||
|
||||
.custom-summary .ui-accordion-content .field {
|
||||
margin: 0;
|
||||
}
|
||||
margin: 0; }
|
||||
|
||||
.custom-summary .ui-accordion-content,
|
||||
.custom-summary .ui-accordion-content .field {
|
||||
padding: 0;
|
||||
}
|
||||
padding: 0; }
|
||||
|
||||
.custom-summary .ui-icon-triangle-1-e {
|
||||
background-position: -16px -128px;
|
||||
}
|
||||
background-position: -16px -128px; }
|
||||
|
||||
.cms table.ss-gridfield-table tr td.MergeAction {
|
||||
width: 225px;
|
||||
}
|
||||
.cms table.ss-gridfield-table tr td.MergeAction a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.cms table.ss-gridfield-table tr td.MergeAction select {
|
||||
width: 150px;
|
||||
}
|
||||
width: 225px; }
|
||||
.cms table.ss-gridfield-table tr td.MergeAction a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%; }
|
||||
.cms table.ss-gridfield-table tr td.MergeAction select {
|
||||
width: 150px; }
|
||||
|
||||
.cms-content-actions,
|
||||
.cms-preview-controls {
|
||||
z-index: 999;
|
||||
}
|
||||
z-index: 999; }
|
||||
|
||||
.blog-cms-categorisation .MergeActionReveal {
|
||||
margin-left: 10px;
|
||||
}
|
||||
margin-left: 10px; }
|
||||
|
||||
.blog-cms-categorisation .toolbar--content {
|
||||
margin-top: 0; }
|
||||
|
||||
.blog-cms-categorisation .MergeActionReveal:after {
|
||||
content: '';
|
||||
background: url("../images/move-icon.png");
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
margin-left: 4px; }
|
||||
|
||||
.blog-cms-categorisation button.action {
|
||||
margin-left: 5px; }
|
||||
|
17
gulpfile.js
Normal file
17
gulpfile.js
Normal file
@ -0,0 +1,17 @@
|
||||
var sass = require("gulp-sass");
|
||||
var gulp = require("gulp");
|
||||
var watch = require('gulp-watch');
|
||||
|
||||
gulp.task("scss", function () {
|
||||
gulp.src("./scss/*.scss")
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
.pipe(gulp.dest("./css"));
|
||||
});
|
||||
|
||||
gulp.task('watch', ['scss'], function() {
|
||||
gulp.watch('./scss/*.scss', ['scss']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['scss'], function() {
|
||||
// noop
|
||||
});
|
11
js/cms.js
11
js/cms.js
@ -113,6 +113,10 @@
|
||||
if(!this.hasClass('collapsed') && ($(".blog-admin-outer").width() < this.getMinInnerWidth())) {
|
||||
this.collapsePanel();
|
||||
}
|
||||
|
||||
window.onresize = function() {
|
||||
this.updateLayout();
|
||||
}.bind(this);
|
||||
},
|
||||
togglePanel: function(bool, silent) {
|
||||
this._super(bool, silent);
|
||||
@ -124,9 +128,16 @@
|
||||
* @returns {undefined}
|
||||
*/
|
||||
updateLayout: function() {
|
||||
$(this).css('height', '100%');
|
||||
var currentHeight = $(this).outerHeight();
|
||||
var bottomHeight = $('.cms-content-actions').eq(0).outerHeight();
|
||||
$(this).css('height', (currentHeight - bottomHeight) + "px");
|
||||
$(this).css('bottom', bottomHeight + "px");
|
||||
|
||||
$('.cms-container').updateLayoutOptions({
|
||||
minContentWidth: 820 + this.width()
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,20 +1,16 @@
|
||||
(function ($) {
|
||||
|
||||
$.entwine('ss', function ($) {
|
||||
|
||||
/**
|
||||
* Prevent the CMS hijacking the return key
|
||||
*/
|
||||
$('.add-existing-autocompleter input.text').entwine({
|
||||
'onkeydown': function (e) {
|
||||
if(e.which == 13) {
|
||||
if (e.which == 13) {
|
||||
$parent = $(this).parents('.add-existing-autocompleter');
|
||||
$parent.find('button[type="submit"]').click();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
|
@ -87,7 +87,7 @@ en:
|
||||
PLURALNAME: 'Blog Recent Posts Widgets'
|
||||
SINGULARNAME: 'Blog Recent Posts Widget'
|
||||
BlogTag:
|
||||
Duplicate: 'A blog tags already exists with that name'
|
||||
Duplicate: 'A blog tag already exists with that name'
|
||||
PLURALNAME: 'Blog Tags'
|
||||
SINGULARNAME: 'Blog Tag'
|
||||
Title: Title
|
||||
|
28
package.json
Normal file
28
package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "silverstripe-blog",
|
||||
"version": "3.0.0",
|
||||
"description": "Silverstripe blog module",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/silverstripe/silverstripe-blog.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "BSD-3",
|
||||
"bugs": {
|
||||
"url": "https://github.com/silverstripe/silverstripe-blog/issues"
|
||||
},
|
||||
"homepage": "https://github.com/silverstripe/silverstripe-blog#readme",
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-sass": "^2.3.1",
|
||||
"gulp-watch": "^4.3.6"
|
||||
}
|
||||
}
|
18
phpunit.xml.dist
Normal file
18
phpunit.xml.dist
Normal file
@ -0,0 +1,18 @@
|
||||
<phpunit bootstrap="framework/tests/bootstrap.php" colors="true">
|
||||
<testsuite name="blog">
|
||||
<directory>blog/tests</directory>
|
||||
</testsuite>
|
||||
|
||||
<listeners>
|
||||
<listener class="SilverStripe\Dev\TestListener" />
|
||||
</listeners>
|
||||
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">blog/src/</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">blog/tests/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
279
scss/cms.scss
279
scss/cms.scss
@ -4,188 +4,205 @@
|
||||
/**
|
||||
* Include Compass framework
|
||||
*/
|
||||
@import "compass";
|
||||
// @import "compass";
|
||||
/*
|
||||
* Sprite maps & Icons
|
||||
*/
|
||||
@import "compass/utilities/sprites/base";
|
||||
@import "blog-icon/*.png";
|
||||
// @import "compass/utilities/sprites/base";
|
||||
// @import "blog-icon/*.png";
|
||||
// buttons
|
||||
|
||||
.gridfield-icon {
|
||||
@include all-blog-icon-sprites;
|
||||
.gridfield-icon.blog-icon-timer {
|
||||
background: url('../images/blog-icon/timer.png') center no-repeat;
|
||||
}
|
||||
|
||||
#FeaturedImage .middleColumn {
|
||||
clear: none;
|
||||
float: left;
|
||||
clear: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.blog-admin-sidebar {
|
||||
width: 280px;
|
||||
border-right: none;
|
||||
border-left: 1px solid #C0C0C2;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
height: 100%;
|
||||
.has-panel .cms-content-tools.blog-admin-sidebar {
|
||||
width: 280px;
|
||||
border-right: none;
|
||||
border-left: 1px solid #C0C0C2;
|
||||
position: absolute !important; /* overrides cms !imporant style */
|
||||
right: 0px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
|
||||
.cms-panel-toggle a {
|
||||
text-align: left;
|
||||
}
|
||||
.cms-panel-toggle a {
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
~ .blog-admin-outer {
|
||||
width: 100%;
|
||||
padding-right: 280px;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
.cms-panel-toggle.south {
|
||||
border-top: 1px solid #aaaaaa;
|
||||
}
|
||||
|
||||
> .ss-tabset {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
~ .blog-admin-outer {
|
||||
width: 100%;
|
||||
padding-right: 280px;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
#Title {
|
||||
label {
|
||||
float: none;
|
||||
}
|
||||
.middleColumn, input {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .ss-tabset {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.cms-content-view {
|
||||
> .field {
|
||||
+ .field {
|
||||
margin-top: 10px;
|
||||
}
|
||||
#Title {
|
||||
label {
|
||||
float: none;
|
||||
}
|
||||
.middleColumn, input {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.urlsegment {
|
||||
.preview {
|
||||
padding-top: 0;
|
||||
line-height: 25px;
|
||||
}
|
||||
.cms-content-view {
|
||||
> .field {
|
||||
+ .field {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.edit {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
&.urlsegment {
|
||||
.preview {
|
||||
padding-top: 0;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
&.datetime {
|
||||
> .middleColumn {
|
||||
> .date {
|
||||
width: 60%;
|
||||
}
|
||||
.edit {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
> .time {
|
||||
width: 36%;
|
||||
float: right;
|
||||
}
|
||||
&.datetime {
|
||||
> .middleColumn {
|
||||
> .date {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.middleColumn, input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .time {
|
||||
width: 36%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
~ .blog-admin-outer {
|
||||
padding-right: 41px;
|
||||
}
|
||||
}
|
||||
.middleColumn, input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.cms-content-tools {
|
||||
.cms-panel-content {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
&.collapsed {
|
||||
~ .blog-admin-outer {
|
||||
padding-right: 41px;
|
||||
|
||||
#Root_Main {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.cms-content-tools {
|
||||
.cms-panel-content {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-description {
|
||||
text-indent: -1000000px;
|
||||
display: inline-block;
|
||||
background: url("../images/information.png") no-repeat center center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 4px;
|
||||
text-indent: -1000000px;
|
||||
display: inline-block;
|
||||
background: url("../images/information.png") no-repeat center center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.middleColumn.toggle-description-correct-middle {
|
||||
margin-left: 0;
|
||||
float: left;
|
||||
width: 416px;
|
||||
margin-left: 0;
|
||||
float: left;
|
||||
width: 416px;
|
||||
}
|
||||
|
||||
label.right.toggle-description-correct-right {
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
clear: none;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
clear: none;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.description.toggle-description-correct-description {
|
||||
width: 416px;
|
||||
padding: 12px 0;
|
||||
width: 416px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.custom-summary {
|
||||
.ui-accordion-content .field {
|
||||
margin: 0;
|
||||
}
|
||||
.ui-accordion-content .field {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ui-accordion-content,
|
||||
.ui-accordion-content .field {
|
||||
padding: 0;
|
||||
}
|
||||
.ui-accordion-content,
|
||||
.ui-accordion-content .field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-icon-triangle-1-e {
|
||||
background-position: -16px -128px;
|
||||
}
|
||||
.ui-icon-triangle-1-e {
|
||||
background-position: -16px -128px;
|
||||
}
|
||||
}
|
||||
|
||||
.cms table.ss-gridfield-table {
|
||||
tr td.MergeAction {
|
||||
width: 225px;
|
||||
tr td.MergeAction {
|
||||
width: 225px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
select {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cms-content-actions,
|
||||
.cms-preview-controls {
|
||||
z-index: 999;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.blog-cms-categorisation {
|
||||
.MergeActionReveal {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.MergeActionReveal {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.toolbar--content {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.MergeActionReveal:after {
|
||||
content: '';
|
||||
background: url('../images/move-icon.png');
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.MergeActionReveal:after {
|
||||
content: '';
|
||||
background: url('../images/move-icon.png');
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
button.action {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
68
src/Admin/GridFieldCategorisationConfig.php
Normal file
68
src/Admin/GridFieldCategorisationConfig.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Admin;
|
||||
|
||||
use SilverStripe\Blog\Forms\GridField\GridFieldAddByDBField;
|
||||
use SilverStripe\Blog\Admin\GridFieldMergeAction;
|
||||
use SilverStripe\Blog\Model\CategorisationObject;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
|
||||
|
||||
class GridFieldCategorisationConfig extends GridFieldConfig_RecordEditor
|
||||
{
|
||||
/**
|
||||
* @param int $itemsPerPage
|
||||
* @param array|SS_List $mergeRecords
|
||||
* @param string $parentType
|
||||
* @param string $parentMethod
|
||||
* @param string $childMethod
|
||||
*/
|
||||
public function __construct($itemsPerPage = 15, $mergeRecords, $parentType, $parentMethod, $childMethod)
|
||||
{
|
||||
parent::__construct($itemsPerPage);
|
||||
|
||||
$this->removeComponentsByType('SilverStripe\\Forms\\GridField\\GridFieldAddNewButton');
|
||||
|
||||
$this->addComponent(
|
||||
new GridFieldAddByDBField('buttons-before-left')
|
||||
);
|
||||
|
||||
$this->addComponent(
|
||||
new GridFieldMergeAction($mergeRecords, $parentType, $parentMethod, $childMethod)
|
||||
);
|
||||
|
||||
/**
|
||||
* @var GridFieldDataColumns $columns
|
||||
*/
|
||||
$columns = $this->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns');
|
||||
|
||||
$columns->setFieldFormatting(
|
||||
array(
|
||||
'BlogPostsCount' => function ($value, CategorisationObject $item) {
|
||||
return $item->BlogPosts()->Count();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
$this->changeColumnOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders GridField columns so that Actions is last.
|
||||
*/
|
||||
protected function changeColumnOrder()
|
||||
{
|
||||
/**
|
||||
* @var GridFieldDataColumns $columns
|
||||
*/
|
||||
$columns = $this->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns');
|
||||
|
||||
$columns->setDisplayFields(
|
||||
array(
|
||||
'Title' => 'Title',
|
||||
'BlogPostsCount' => 'Posts',
|
||||
'MergeAction' => 'MergeAction',
|
||||
'Actions' => 'Actions'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Admin;
|
||||
|
||||
use SilverStripe\Forms\GridField\GridField_FormAction;
|
||||
|
||||
class GridFieldFormAction extends GridField_FormAction
|
||||
{
|
||||
/**
|
@ -1,5 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Admin;
|
||||
|
||||
use SilverStripe\Blog\Admin\GridFieldFormAction;
|
||||
use SilverStripe\Control\Controller;
|
||||
use Silverstripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_ActionProvider;
|
||||
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
|
||||
|
||||
/**
|
||||
* @package blog
|
||||
*/
|
||||
class GridFieldMergeAction implements GridField_ColumnProvider, GridField_ActionProvider
|
||||
{
|
||||
/**
|
||||
@ -70,11 +82,11 @@ class GridFieldMergeAction implements GridField_ColumnProvider, GridField_Action
|
||||
public function getColumnContent($gridField, $record, $columnName)
|
||||
{
|
||||
if ($columnName === 'MergeAction' && $record->{$this->childMethod}()->Count() > 0) {
|
||||
$dropdown = new DropdownField('Target', 'Target', $this->records->exclude('ID', $record->ID)->map());
|
||||
$dropdown = DropdownField::create('Target', 'Target', $this->records->exclude('ID', $record->ID)->map());
|
||||
$dropdown->setAttribute('id', 'Target_'.$record->ID);
|
||||
$prefix = strtolower($this->parentMethod . '-' . $this->childMethod);
|
||||
|
||||
$action = GridFieldFormAction::create(
|
||||
$action = new GridFieldFormAction(
|
||||
$gridField,
|
||||
'MergeAction' . $record->ID,
|
||||
'Move',
|
@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Forms;
|
||||
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Forms\FieldGroup;
|
||||
|
||||
class BlogAdminSidebar extends FieldGroup
|
||||
{
|
||||
/**
|
@ -1,5 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Forms\GridField;
|
||||
|
||||
use UnexpectedValueException;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_ActionProvider;
|
||||
use SilverStripe\Forms\GridField\GridField_FormAction;
|
||||
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
* Adds a component which allows a user to add a new DataObject by database field.
|
||||
*
|
||||
@ -79,7 +94,7 @@ class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLP
|
||||
if ($obj->canCreate()) {
|
||||
$id = $gridField->getList()->add($obj);
|
||||
if (!$id) {
|
||||
$gridField->setError(
|
||||
$gridField->setCustomValidationMessage(
|
||||
_t(
|
||||
'GridFieldAddByDBField.AddFail',
|
||||
'Unable to save {class} to the database.',
|
||||
@ -87,8 +102,7 @@ class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLP
|
||||
array(
|
||||
'class' => get_class($obj),
|
||||
)
|
||||
),
|
||||
'error'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -159,7 +173,7 @@ class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLP
|
||||
$obj = singleton($dataClass);
|
||||
|
||||
if (!$obj->canCreate()) {
|
||||
return "";
|
||||
return '';
|
||||
}
|
||||
|
||||
$dbField = $this->getDataObjectField();
|
||||
@ -177,16 +191,17 @@ class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLP
|
||||
$addAction = new GridField_FormAction(
|
||||
$gridField,
|
||||
'add',
|
||||
_t('GridFieldAddByDBField.Add',
|
||||
'Add {name}', "Add button text",
|
||||
array(
|
||||
'name' => $obj->i18n_singular_name(),
|
||||
)
|
||||
_t(
|
||||
'GridFieldAddByDBField.Add',
|
||||
'Add {name}',
|
||||
'Add button text',
|
||||
['name' => $obj->i18n_singular_name()]
|
||||
),
|
||||
'add',
|
||||
'add'
|
||||
);
|
||||
$addAction->setAttribute('data-icon', 'add');
|
||||
$addAction->addExtraClass('btn btn-primary');
|
||||
|
||||
$forTemplate = new ArrayData(array());
|
||||
|
||||
@ -194,8 +209,6 @@ class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLP
|
||||
$forTemplate->Fields->push($textField);
|
||||
$forTemplate->Fields->push($addAction);
|
||||
|
||||
return array(
|
||||
$this->targetFragment => $forTemplate->renderWith('GridFieldAddByDBField')
|
||||
);
|
||||
return array($this->targetFragment => $forTemplate->renderWith(self::class));
|
||||
}
|
||||
}
|
@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Forms\GridField;
|
||||
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Lumberjack\Forms\GridFieldSiteTreeState;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
|
||||
/**
|
||||
* Provides a component to the {@link GridField} which tells the user whether or not a blog post
|
||||
* has been published and when.
|
@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Forms\GridField;
|
||||
|
||||
use SilverStripe\Lumberjack\Forms\GridFieldConfig_Lumberjack;
|
||||
use SilverStripe\Lumberjack\Forms\GridFieldSiteTreeState;
|
||||
|
||||
/**
|
||||
* GridField config necessary for managing a SiteTree object.
|
||||
*
|
||||
@ -15,7 +20,7 @@ class GridFieldConfig_BlogPost extends GridFieldConfig_Lumberjack
|
||||
{
|
||||
parent::__construct($itemsPerPage);
|
||||
|
||||
$this->removeComponentsByType('GridFieldSiteTreeState');
|
||||
$this->removeComponentsByType(GridFieldSiteTreeState::class);
|
||||
$this->addComponent(new GridFieldBlogPostState());
|
||||
}
|
||||
}
|
@ -1,5 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use Page;
|
||||
use PageController;
|
||||
use SilverStripe\Blog\Admin\GridFieldCategorisationConfig;
|
||||
use SilverStripe\Blog\Forms\GridField\GridFieldConfig_BlogPost;
|
||||
use SilverStripe\Blog\Model\BlogCategory;
|
||||
use SilverStripe\Blog\Model\BlogFilter;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Blog\Model\BlogTag;
|
||||
use SilverStripe\CMS\Controllers\RootURLController;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\RSS\RSSFeed;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\ListboxField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\PaginatedList;
|
||||
use SilverStripe\ORM\UnsavedRelationList;
|
||||
use SilverStripe\Security\Group;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\PermissionProvider;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
* Blog Holder
|
||||
*
|
||||
@ -50,6 +79,12 @@ class Blog extends Page implements PermissionProvider
|
||||
*/
|
||||
private static $grant_user_group = 'blog-users';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
private static $table_name = 'Blog';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -61,31 +96,31 @@ class Blog extends Page implements PermissionProvider
|
||||
* @var array
|
||||
*/
|
||||
private static $has_many = array(
|
||||
'Tags' => 'BlogTag',
|
||||
'Categories' => 'BlogCategory',
|
||||
'Tags' => BlogTag::class,
|
||||
'Categories' => BlogCategory::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $many_many = array(
|
||||
'Editors' => 'Member',
|
||||
'Writers' => 'Member',
|
||||
'Contributors' => 'Member',
|
||||
'Editors' => Member::class,
|
||||
'Writers' => Member::class,
|
||||
'Contributors' => Member::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_children = array(
|
||||
'BlogPost',
|
||||
BlogPost::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $extensions = array(
|
||||
'BlogFilter',
|
||||
BlogFilter::class,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -93,7 +128,7 @@ class Blog extends Page implements PermissionProvider
|
||||
*/
|
||||
private static $defaults = array(
|
||||
'ProvideComments' => false,
|
||||
'PostsPerPage' => 10,
|
||||
'PostsPerPage' => 10
|
||||
);
|
||||
|
||||
/**
|
||||
@ -111,34 +146,47 @@ class Blog extends Page implements PermissionProvider
|
||||
Requirements::css(BLOGGER_DIR . '/css/cms.css');
|
||||
Requirements::javascript(BLOGGER_DIR . '/js/cms.js');
|
||||
|
||||
$self =& $this;
|
||||
|
||||
$this->beforeUpdateCMSFields(function ($fields) use ($self) {
|
||||
if (!$self->canEdit()) {
|
||||
$this->beforeUpdateCMSFields(function ($fields) {
|
||||
if (!$this->canEdit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$categories = GridField::create(
|
||||
'Categories',
|
||||
_t('Blog.Categories', 'Categories'),
|
||||
$self->Categories(),
|
||||
GridFieldCategorisationConfig::create(15, $self->Categories()->sort('Title'), 'BlogCategory', 'Categories', 'BlogPosts')
|
||||
$this->Categories(),
|
||||
GridFieldCategorisationConfig::create(
|
||||
15,
|
||||
$this->Categories()->sort('Title'),
|
||||
BlogCategory::class,
|
||||
'Categories',
|
||||
'BlogPosts'
|
||||
)
|
||||
);
|
||||
|
||||
$tags = GridField::create(
|
||||
'Tags',
|
||||
_t('Blog.Tags', 'Tags'),
|
||||
$self->Tags(),
|
||||
GridFieldCategorisationConfig::create(15, $self->Tags()->sort('Title'), 'BlogTag', 'Tags', 'BlogPosts')
|
||||
$this->Tags(),
|
||||
GridFieldCategorisationConfig::create(
|
||||
15,
|
||||
$this->Tags()->sort('Title'),
|
||||
BlogTag::class,
|
||||
'Tags',
|
||||
'BlogPosts'
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* @var FieldList $fields
|
||||
*/
|
||||
$fields->addFieldsToTab('Root.Categorisation', array(
|
||||
$categories,
|
||||
$tags
|
||||
));
|
||||
$fields->addFieldsToTab(
|
||||
'Root.Categorisation',
|
||||
array(
|
||||
$categories,
|
||||
$tags
|
||||
)
|
||||
);
|
||||
|
||||
$fields->findOrMakeTab('Root.Categorisation')->addExtraClass('blog-cms-categorisation');
|
||||
});
|
||||
@ -228,7 +276,7 @@ class Blog extends Page implements PermissionProvider
|
||||
public function RoleOf($member)
|
||||
{
|
||||
if (is_numeric($member)) {
|
||||
$member = DataObject::get_by_id('Member', $member);
|
||||
$member = Member::get()->byId($member);
|
||||
}
|
||||
|
||||
if (!$member) {
|
||||
@ -301,14 +349,15 @@ class Blog extends Page implements PermissionProvider
|
||||
{
|
||||
$fields = parent::getSettingsFields();
|
||||
|
||||
$fields->addFieldToTab('Root.Settings',
|
||||
$fields->addFieldToTab(
|
||||
'Root.Settings',
|
||||
NumericField::create('PostsPerPage', _t('Blog.PostsPerPage', 'Posts Per Page'))
|
||||
);
|
||||
|
||||
$members = $this->getCandidateUsers()->map()->toArray();
|
||||
|
||||
$editorField = ListboxField::create('Editors', 'Editors', $members)
|
||||
->setMultiple(true)
|
||||
// ->setMultiple(true)
|
||||
->setRightTitle('<a class="toggle-description">help</a>')
|
||||
->setDescription('
|
||||
An editor has control over specific Blogs, and all posts included within it. Short of being able to assign other editors to a blog, they are able to handle most changes to their assigned blog.<br />
|
||||
@ -327,7 +376,7 @@ class Blog extends Page implements PermissionProvider
|
||||
}
|
||||
|
||||
$writerField = ListboxField::create('Writers', 'Writers', $members)
|
||||
->setMultiple(true)
|
||||
// ->setMultiple(true)
|
||||
->setRightTitle('<a class="toggle-description">help</a>')
|
||||
->setDescription('
|
||||
A writer has full control over creating, editing and publishing BlogPosts they have authored or have been assigned to. Writers are unable to edit BlogPosts to which they are not assigned.<br />
|
||||
@ -343,7 +392,7 @@ class Blog extends Page implements PermissionProvider
|
||||
}
|
||||
|
||||
$contributorField = ListboxField::create('Contributors', 'Contributors', $members)
|
||||
->setMultiple(true)
|
||||
// ->setMultiple(true)
|
||||
->setRightTitle('<a class="toggle-description">help</a>')
|
||||
->setDescription('
|
||||
Contributors have the ability to create or edit BlogPosts, but are unable to publish without authorisation of an editor. They are also unable to assign other contributing authors to any of their BlogPosts.<br />
|
||||
@ -357,11 +406,14 @@ class Blog extends Page implements PermissionProvider
|
||||
$contributorField = $contributorField->performDisabledTransformation();
|
||||
}
|
||||
|
||||
$fields->addFieldsToTab('Root.Users', array(
|
||||
$editorField,
|
||||
$writerField,
|
||||
$contributorField
|
||||
));
|
||||
$fields->addFieldsToTab(
|
||||
'Root.Users',
|
||||
array(
|
||||
$editorField,
|
||||
$writerField,
|
||||
$contributorField
|
||||
)
|
||||
);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
@ -471,7 +523,10 @@ class Blog extends Page implements PermissionProvider
|
||||
$stage = '_' . $stage;
|
||||
}
|
||||
|
||||
$query->innerJoin('BlogPost', sprintf('"SiteTree%s"."ID" = "BlogPost%s"."ID"', $stage, $stage));
|
||||
$query->innerJoin(
|
||||
DataObject::getSchema()->tableName(BlogPost::class),
|
||||
sprintf('"SiteTree%s"."ID" = "BlogPost%s"."ID"', $stage, $stage)
|
||||
);
|
||||
|
||||
$conn = DB::getConn();
|
||||
|
||||
@ -517,7 +572,13 @@ class Blog extends Page implements PermissionProvider
|
||||
*/
|
||||
public function ProfileLink($urlSegment)
|
||||
{
|
||||
return Controller::join_links($this->Link(), 'profile', $urlSegment);
|
||||
$baseLink = $this->Link();
|
||||
if ($baseLink === '/') {
|
||||
// Handle homepage blogs
|
||||
$baseLink = RootURLController::get_homepage_link();
|
||||
}
|
||||
|
||||
return Controller::join_links($baseLink, 'profile', $urlSegment);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -608,13 +669,13 @@ class Blog extends Page implements PermissionProvider
|
||||
return $group;
|
||||
}
|
||||
|
||||
$group = new Group();
|
||||
$group = Group::create();
|
||||
$group->Title = 'Blog users';
|
||||
$group->Code = $code;
|
||||
|
||||
$group->write();
|
||||
|
||||
$permission = new Permission();
|
||||
$permission = Permission::create();
|
||||
$permission->Code = $this->config()->grant_user_permission;
|
||||
|
||||
$group->Permissions()->add($permission);
|
||||
@ -622,510 +683,3 @@ class Blog extends Page implements PermissionProvider
|
||||
return $group;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class Blog_Controller extends Page_Controller
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'archive',
|
||||
'tag',
|
||||
'category',
|
||||
'rss',
|
||||
'profile',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = array(
|
||||
'tag/$Tag!/$Rss' => 'tag',
|
||||
'category/$Category!/$Rss' => 'category',
|
||||
'archive/$Year!/$Month/$Day' => 'archive',
|
||||
'profile/$URLSegment!' => 'profile',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = array(
|
||||
'MetaTitle' => 'Text',
|
||||
'FilterDescription' => 'Text',
|
||||
);
|
||||
|
||||
/**
|
||||
* The current Blog Post DataList query.
|
||||
*
|
||||
* @var DataList
|
||||
*/
|
||||
protected $blogPosts;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
|
||||
$this->blogPosts = $dataRecord->getBlogPosts();
|
||||
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a Blog Member's profile.
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function profile()
|
||||
{
|
||||
$profile = $this->getCurrentProfile();
|
||||
|
||||
if (!$profile) {
|
||||
return $this->httpError(404, 'Not Found');
|
||||
}
|
||||
|
||||
$this->blogPosts = $this->getCurrentProfilePosts();
|
||||
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Member associated with the current URL segment.
|
||||
*
|
||||
* @return null|Member
|
||||
*/
|
||||
public function getCurrentProfile()
|
||||
{
|
||||
$urlSegment = $this->request->param('URLSegment');
|
||||
|
||||
if ($urlSegment) {
|
||||
return Member::get()
|
||||
->filter('URLSegment', $urlSegment)
|
||||
->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get posts related to the current Member profile.
|
||||
*
|
||||
* @return null|DataList
|
||||
*/
|
||||
public function getCurrentProfilePosts()
|
||||
{
|
||||
$profile = $this->getCurrentProfile();
|
||||
|
||||
if ($profile) {
|
||||
return $profile->BlogPosts()->filter('ParentID', $this->ID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an archive for a specified date. This can be by year or year/month.
|
||||
*
|
||||
* @return null|SS_HTTPResponse
|
||||
*/
|
||||
public function archive()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
|
||||
$year = $this->getArchiveYear();
|
||||
$month = $this->getArchiveMonth();
|
||||
$day = $this->getArchiveDay();
|
||||
|
||||
if ($this->request->param('Month') && !$month) {
|
||||
$this->httpError(404, 'Not Found');
|
||||
}
|
||||
|
||||
if ($month && $this->request->param('Day') && !$day) {
|
||||
$this->httpError(404, 'Not Found');
|
||||
}
|
||||
|
||||
if ($year) {
|
||||
$this->blogPosts = $dataRecord->getArchivedBlogPosts($year, $month, $day);
|
||||
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
$this->httpError(404, 'Not Found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the archive year from the url.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getArchiveYear()
|
||||
{
|
||||
if ($this->request->param('Year')) {
|
||||
if (preg_match('/^[0-9]{4}$/', $year = $this->request->param('Year'))) {
|
||||
return (int) $year;
|
||||
}
|
||||
} elseif ($this->request->param('Action') == 'archive') {
|
||||
return SS_Datetime::now()->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.
|
||||
*
|
||||
* @return null|SS_HTTPResponse
|
||||
*/
|
||||
public function tag()
|
||||
{
|
||||
$tag = $this->getCurrentTag();
|
||||
|
||||
if ($tag) {
|
||||
$this->blogPosts = $tag->BlogPosts();
|
||||
|
||||
if($this->isRSS()) {
|
||||
return $this->rssFeed($this->blogPosts, $tag->getLink());
|
||||
} else {
|
||||
return $this->render();
|
||||
}
|
||||
}
|
||||
|
||||
$this->httpError(404, 'Not Found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag Getter for use in templates.
|
||||
*
|
||||
* @return null|BlogTag
|
||||
*/
|
||||
public function getCurrentTag()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
$tag = $this->request->param('Tag');
|
||||
if ($tag) {
|
||||
return $dataRecord->Tags()
|
||||
->filter('URLSegment', array($tag, rawurlencode($tag)))
|
||||
->first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the blog posts for a given category.
|
||||
*
|
||||
* @return null|SS_HTTPResponse
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
$category = $this->getCurrentCategory();
|
||||
|
||||
if ($category) {
|
||||
$this->blogPosts = $category->BlogPosts();
|
||||
|
||||
if($this->isRSS()) {
|
||||
return $this->rssFeed($this->blogPosts, $category->getLink());
|
||||
} else {
|
||||
return $this->render();
|
||||
}
|
||||
}
|
||||
|
||||
$this->httpError(404, 'Not Found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Category Getter for use in templates.
|
||||
*
|
||||
* @return null|BlogCategory
|
||||
*/
|
||||
public function getCurrentCategory()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
$category = $this->request->param('Category');
|
||||
if ($category) {
|
||||
return $dataRecord->Categories()
|
||||
->filter('URLSegment', array($category, rawurlencode($category)))
|
||||
->first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta title for the current action.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMetaTitle()
|
||||
{
|
||||
$title = $this->data()->getTitle();
|
||||
$filter = $this->getFilterDescription();
|
||||
|
||||
if ($filter) {
|
||||
$title = sprintf('%s - %s', $title, $filter);
|
||||
}
|
||||
|
||||
$this->extend('updateMetaTitle', $title);
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the current filter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFilterDescription()
|
||||
{
|
||||
$items = array();
|
||||
|
||||
$list = $this->PaginatedList();
|
||||
$currentPage = $list->CurrentPage();
|
||||
|
||||
if ($currentPage > 1) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_PAGE',
|
||||
'Page {page}',
|
||||
null,
|
||||
array(
|
||||
'page' => $currentPage,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($author = $this->getCurrentProfile()) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_AUTHOR',
|
||||
'By {author}',
|
||||
null,
|
||||
array(
|
||||
'author' => $author->Title,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($tag = $this->getCurrentTag()) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_TAG',
|
||||
'Tagged with {tag}',
|
||||
null,
|
||||
array(
|
||||
'tag' => $tag->Title,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($category = $this->getCurrentCategory()) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_CATEGORY',
|
||||
'In category {category}',
|
||||
null,
|
||||
array(
|
||||
'category' => $category->Title,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->owner->getArchiveYear()) {
|
||||
if ($this->owner->getArchiveDay()) {
|
||||
$date = $this->owner->getArchiveDate()->Nice();
|
||||
} elseif ($this->owner->getArchiveMonth()) {
|
||||
$date = $this->owner->getArchiveDate()->format('F, Y');
|
||||
} else {
|
||||
$date = $this->owner->getArchiveDate()->format('Y');
|
||||
}
|
||||
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_DATE',
|
||||
'In {date}',
|
||||
null,
|
||||
array(
|
||||
'date' => $date,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
if ($items) {
|
||||
$result = implode(', ', $items);
|
||||
}
|
||||
|
||||
$this->extend('updateFilterDescription', $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of paginated blog posts based on the BlogPost dataList.
|
||||
*
|
||||
* @return PaginatedList
|
||||
*/
|
||||
public function PaginatedList()
|
||||
{
|
||||
$allPosts = $this->blogPosts ?: new ArrayList();
|
||||
|
||||
$posts = new PaginatedList($allPosts);
|
||||
|
||||
// Set appropriate page size
|
||||
if ($this->PostsPerPage > 0) {
|
||||
$pageSize = $this->PostsPerPage;
|
||||
} elseif ($count = $allPosts->count()) {
|
||||
$pageSize = $count;
|
||||
} else {
|
||||
$pageSize = 99999;
|
||||
}
|
||||
$posts->setPageLength($pageSize);
|
||||
|
||||
// Set current page
|
||||
$start = $this->request->getVar($posts->getPaginationGetVar());
|
||||
$posts->setPageStart($start);
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an RSS feed of blog posts.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function rss()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
|
||||
$this->blogPosts = $dataRecord->getBlogPosts();
|
||||
|
||||
return $this->rssFeed($this->blogPosts, $this->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current archive date.
|
||||
*
|
||||
* @return null|Date
|
||||
*/
|
||||
public function getArchiveDate()
|
||||
{
|
||||
$year = $this->getArchiveYear();
|
||||
$month = $this->getArchiveMonth();
|
||||
$day = $this->getArchiveDay();
|
||||
|
||||
if ($year) {
|
||||
if ($month) {
|
||||
$date = sprintf('%s-%s-01', $year, $month);
|
||||
|
||||
if ($day) {
|
||||
$date = sprintf('%s-%s-%s', $year, $month, $day);
|
||||
}
|
||||
} else {
|
||||
$date = sprintf('%s-01-01', $year);
|
||||
}
|
||||
|
||||
return DBField::create_field('Date', $date);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a link to the RSS feed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRSSLink()
|
||||
{
|
||||
return $this->Link('rss');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an RSS feed of the given blog posts.
|
||||
*
|
||||
* @param DataList $blogPosts
|
||||
* @param string $link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function rssFeed($blogPosts, $link)
|
||||
{
|
||||
$rss = new RSSFeed($blogPosts, $link, $this->MetaTitle, $this->MetaDescription);
|
||||
|
||||
$this->extend('updateRss', $rss);
|
||||
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the $Rss sub-action for categories/tags has been set to "rss"
|
||||
*/
|
||||
private function isRSS()
|
||||
{
|
||||
$rss = $this->request->param('Rss');
|
||||
if(is_string($rss) && strcasecmp($rss, "rss") == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
79
src/Model/BlogCategory.php
Normal file
79
src/Model/BlogCategory.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Model\BlogObject;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Blog\Model\CategorisationObject;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
/**
|
||||
* A blog category for generalising blog posts.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*
|
||||
* @method Blog Blog()
|
||||
*
|
||||
* @property string $Title
|
||||
* @property string $URLSegment
|
||||
* @property int $BlogID
|
||||
*/
|
||||
class BlogCategory extends DataObject implements CategorisationObject
|
||||
{
|
||||
use BlogObject;
|
||||
|
||||
/**
|
||||
* Use an exception code so that attempted writes can continue on
|
||||
* duplicate errors.
|
||||
*
|
||||
* @const string
|
||||
* This must be a string because ValidationException has decided we can't use int
|
||||
*/
|
||||
const DUPLICATE_EXCEPTION = 'DUPLICATE';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
private static $table_name = 'BlogCategory';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)',
|
||||
'URLSegment' => 'Varchar(255)'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => Blog::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array(
|
||||
'BlogPosts' => BlogPost::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getListUrlSegment()
|
||||
{
|
||||
return 'category';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDuplicateError()
|
||||
{
|
||||
return _t('BlogCategory.Duplicate', 'A blog category already exists with that name.');
|
||||
}
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
/**
|
||||
* Adds Blog specific behaviour to Comment.
|
||||
*/
|
516
src/Model/BlogController.php
Normal file
516
src/Model/BlogController.php
Normal file
@ -0,0 +1,516 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use PageController;
|
||||
use SilverStripe\Control\RSS\RSSFeed;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\PaginatedList;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
/**
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class BlogController extends PageController
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'archive',
|
||||
'tag',
|
||||
'category',
|
||||
'rss',
|
||||
'profile'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = array(
|
||||
'tag/$Tag!/$Rss' => 'tag',
|
||||
'category/$Category!/$Rss' => 'category',
|
||||
'archive/$Year!/$Month/$Day' => 'archive',
|
||||
'profile/$URLSegment!' => 'profile'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = array(
|
||||
'MetaTitle' => 'Text',
|
||||
'FilterDescription' => 'Text'
|
||||
);
|
||||
|
||||
/**
|
||||
* The current Blog Post DataList query.
|
||||
*
|
||||
* @var DataList
|
||||
*/
|
||||
protected $blogPosts;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
|
||||
$this->blogPosts = $dataRecord->getBlogPosts();
|
||||
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a Blog Member's profile.
|
||||
*
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function profile()
|
||||
{
|
||||
$profile = $this->getCurrentProfile();
|
||||
|
||||
if (!$profile) {
|
||||
return $this->httpError(404, 'Not Found');
|
||||
}
|
||||
|
||||
$this->blogPosts = $this->getCurrentProfilePosts();
|
||||
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Member associated with the current URL segment.
|
||||
*
|
||||
* @return null|Member
|
||||
*/
|
||||
public function getCurrentProfile()
|
||||
{
|
||||
$urlSegment = $this->request->param('URLSegment');
|
||||
|
||||
if ($urlSegment) {
|
||||
return Member::get()
|
||||
->filter('URLSegment', $urlSegment)
|
||||
->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get posts related to the current Member profile.
|
||||
*
|
||||
* @return null|DataList
|
||||
*/
|
||||
public function getCurrentProfilePosts()
|
||||
{
|
||||
$profile = $this->getCurrentProfile();
|
||||
|
||||
if ($profile) {
|
||||
return $profile->BlogPosts()->filter('ParentID', $this->ID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an archive for a specified date. This can be by year or year/month.
|
||||
*
|
||||
* @return null|HTTPResponse
|
||||
*/
|
||||
public function archive()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
|
||||
$year = $this->getArchiveYear();
|
||||
$month = $this->getArchiveMonth();
|
||||
$day = $this->getArchiveDay();
|
||||
|
||||
if ($this->request->param('Month') && !$month) {
|
||||
$this->httpError(404, 'Not Found');
|
||||
}
|
||||
|
||||
if ($month && $this->request->param('Day') && !$day) {
|
||||
$this->httpError(404, 'Not Found');
|
||||
}
|
||||
|
||||
if ($year) {
|
||||
$this->blogPosts = $dataRecord->getArchivedBlogPosts($year, $month, $day);
|
||||
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
$this->httpError(404, 'Not Found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the archive year from the url.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getArchiveYear()
|
||||
{
|
||||
if ($this->request->param('Year')) {
|
||||
if (preg_match('/^[0-9]{4}$/', $year = $this->request->param('Year'))) {
|
||||
return (int) $year;
|
||||
}
|
||||
} elseif ($this->request->param('Action') == 'archive') {
|
||||
return DBDatetime::now()->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.
|
||||
*
|
||||
* @return null|HTTPResponse
|
||||
*/
|
||||
public function tag()
|
||||
{
|
||||
$tag = $this->getCurrentTag();
|
||||
|
||||
if ($tag) {
|
||||
$this->blogPosts = $tag->BlogPosts();
|
||||
|
||||
if($this->isRSS()) {
|
||||
return $this->rssFeed($this->blogPosts, $tag->getLink());
|
||||
} else {
|
||||
return $this->render();
|
||||
}
|
||||
}
|
||||
|
||||
$this->httpError(404, 'Not Found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag Getter for use in templates.
|
||||
*
|
||||
* @return null|BlogTag
|
||||
*/
|
||||
public function getCurrentTag()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
$tag = $this->request->param('Tag');
|
||||
if ($tag) {
|
||||
return $dataRecord->Tags()
|
||||
->filter('URLSegment', array($tag, rawurlencode($tag)))
|
||||
->first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the blog posts for a given category.
|
||||
*
|
||||
* @return null|HTTPResponse
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
$category = $this->getCurrentCategory();
|
||||
|
||||
if ($category) {
|
||||
$this->blogPosts = $category->BlogPosts();
|
||||
|
||||
if($this->isRSS()) {
|
||||
return $this->rssFeed($this->blogPosts, $category->getLink());
|
||||
} else {
|
||||
return $this->render();
|
||||
}
|
||||
}
|
||||
|
||||
$this->httpError(404, 'Not Found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Category Getter for use in templates.
|
||||
*
|
||||
* @return null|BlogCategory
|
||||
*/
|
||||
public function getCurrentCategory()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
$category = $this->request->param('Category');
|
||||
if ($category) {
|
||||
return $dataRecord->Categories()
|
||||
->filter('URLSegment', array($category, rawurlencode($category)))
|
||||
->first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta title for the current action.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMetaTitle()
|
||||
{
|
||||
$title = $this->data()->getTitle();
|
||||
$filter = $this->getFilterDescription();
|
||||
|
||||
if ($filter) {
|
||||
$title = sprintf('%s - %s', $title, $filter);
|
||||
}
|
||||
|
||||
$this->extend('updateMetaTitle', $title);
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the current filter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFilterDescription()
|
||||
{
|
||||
$items = array();
|
||||
|
||||
$list = $this->PaginatedList();
|
||||
$currentPage = $list->CurrentPage();
|
||||
|
||||
if ($currentPage > 1) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_PAGE',
|
||||
'Page {page}',
|
||||
null,
|
||||
array(
|
||||
'page' => $currentPage
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($author = $this->getCurrentProfile()) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_AUTHOR',
|
||||
'By {author}',
|
||||
null,
|
||||
array(
|
||||
'author' => $author->Title
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($tag = $this->getCurrentTag()) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_TAG',
|
||||
'Tagged with {tag}',
|
||||
null,
|
||||
array(
|
||||
'tag' => $tag->Title
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($category = $this->getCurrentCategory()) {
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_CATEGORY',
|
||||
'In category {category}',
|
||||
null,
|
||||
array(
|
||||
'category' => $category->Title
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->owner->getArchiveYear()) {
|
||||
if ($this->owner->getArchiveDay()) {
|
||||
$date = $this->owner->getArchiveDate()->Nice();
|
||||
} elseif ($this->owner->getArchiveMonth()) {
|
||||
$date = $this->owner->getArchiveDate()->format('F, Y');
|
||||
} else {
|
||||
$date = $this->owner->getArchiveDate()->format('Y');
|
||||
}
|
||||
|
||||
$items[] = _t(
|
||||
'Blog.FILTERDESCRIPTION_DATE',
|
||||
'In {date}',
|
||||
null,
|
||||
array(
|
||||
'date' => $date,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
if ($items) {
|
||||
$result = implode(', ', $items);
|
||||
}
|
||||
|
||||
$this->extend('updateFilterDescription', $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of paginated blog posts based on the BlogPost dataList.
|
||||
*
|
||||
* @return PaginatedList
|
||||
*/
|
||||
public function PaginatedList()
|
||||
{
|
||||
$allPosts = $this->blogPosts ?: ArrayList::create();
|
||||
$posts = PaginatedList::create($allPosts);
|
||||
|
||||
// Set appropriate page size
|
||||
if ($this->PostsPerPage > 0) {
|
||||
$pageSize = $this->PostsPerPage;
|
||||
} elseif ($count = $allPosts->count()) {
|
||||
$pageSize = $count;
|
||||
} else {
|
||||
$pageSize = 99999;
|
||||
}
|
||||
$posts->setPageLength($pageSize);
|
||||
|
||||
// Set current page
|
||||
$start = $this->request->getVar($posts->getPaginationGetVar());
|
||||
$posts->setPageStart($start);
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an RSS feed of blog posts.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function rss()
|
||||
{
|
||||
/**
|
||||
* @var Blog $dataRecord
|
||||
*/
|
||||
$dataRecord = $this->dataRecord;
|
||||
|
||||
$this->blogPosts = $dataRecord->getBlogPosts();
|
||||
|
||||
return $this->rssFeed($this->blogPosts, $this->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current archive date.
|
||||
*
|
||||
* @return null|Date
|
||||
*/
|
||||
public function getArchiveDate()
|
||||
{
|
||||
$year = $this->getArchiveYear();
|
||||
$month = $this->getArchiveMonth();
|
||||
$day = $this->getArchiveDay();
|
||||
|
||||
if ($year) {
|
||||
if ($month) {
|
||||
$date = sprintf('%s-%s-01', $year, $month);
|
||||
|
||||
if ($day) {
|
||||
$date = sprintf('%s-%s-%s', $year, $month, $day);
|
||||
}
|
||||
} else {
|
||||
$date = sprintf('%s-01-01', $year);
|
||||
}
|
||||
|
||||
$obj = DBDatetime::create('date');
|
||||
$obj->setValue($date);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a link to the RSS feed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRSSLink()
|
||||
{
|
||||
return $this->Link('rss');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an RSS feed of the given blog posts.
|
||||
*
|
||||
* @param DataList $blogPosts
|
||||
* @param string $link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function rssFeed($blogPosts, $link)
|
||||
{
|
||||
$rss = new RSSFeed($blogPosts, $link, $this->MetaTitle, $this->MetaDescription);
|
||||
|
||||
$this->extend('updateRss', $rss);
|
||||
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the $Rss sub-action for categories/tags has been set to "rss"
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRSS()
|
||||
{
|
||||
$rss = $this->request->param('Rss');
|
||||
return (is_string($rss) && strcasecmp($rss, 'rss') == 0);
|
||||
}
|
||||
}
|
@ -1,5 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Model\BlogFilter\BlogFilterGridField;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Tab;
|
||||
use SilverStripe\Lumberjack\Model\Lumberjack;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\Versioning\Versioned;
|
||||
use SilverStripe\Security\Permission;
|
||||
|
||||
/**
|
||||
* This class is responsible for filtering the SiteTree when necessary and also overlaps into
|
||||
* filtering only published posts.
|
||||
@ -17,7 +32,7 @@ class BlogFilter extends Lumberjack
|
||||
$staged = parent::stageChildren($showAll);
|
||||
|
||||
if (!$this->shouldFilter() && $this->subclassForBlog() && !Permission::check('VIEW_DRAFT_CONTENT')) {
|
||||
$stage = Versioned::current_stage();
|
||||
$stage = Versioned::get_stage();
|
||||
|
||||
if ($stage == 'Stage') {
|
||||
$stage = '';
|
||||
@ -26,8 +41,11 @@ class BlogFilter extends Lumberjack
|
||||
}
|
||||
|
||||
$dataQuery = $staged->dataQuery()
|
||||
->innerJoin('BlogPost', sprintf('"BlogPost%s"."ID" = "SiteTree%s"."ID"', $stage, $stage))
|
||||
->where(sprintf('"PublishDate" < \'%s\'', Convert::raw2sql(SS_Datetime::now())));
|
||||
->innerJoin(
|
||||
DataObject::getSchema()->tableName(BlogPost::class),
|
||||
sprintf('"BlogPost%s"."ID" = "SiteTree%s"."ID"', $stage, $stage)
|
||||
)
|
||||
->where(sprintf('"PublishDate" < \'%s\'', Convert::raw2sql(DBDatetime::now())));
|
||||
|
||||
$staged = $staged->setDataQuery($dataQuery);
|
||||
}
|
||||
@ -40,7 +58,7 @@ class BlogFilter extends Lumberjack
|
||||
*/
|
||||
protected function subclassForBlog()
|
||||
{
|
||||
return in_array(get_class($this->owner), ClassInfo::subClassesFor('Blog'));
|
||||
return in_array(get_class($this->owner), ClassInfo::subclassesFor(Blog::class));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,8 +70,11 @@ class BlogFilter extends Lumberjack
|
||||
|
||||
if (!$this->shouldFilter() && $this->isBlog() && !Permission::check('VIEW_DRAFT_CONTENT')) {
|
||||
$dataQuery = $staged->dataQuery()
|
||||
->innerJoin('BlogPost', '"BlogPost_Live"."ID" = "SiteTree_Live"."ID"')
|
||||
->where(sprintf('"PublishDate" < \'%s\'', Convert::raw2sql(SS_Datetime::now())));
|
||||
->innerJoin(
|
||||
DataObject::getSchema()->tableName(BlogPost::class),
|
||||
'"BlogPost_Live"."ID" = "SiteTree_Live"."ID"'
|
||||
)
|
||||
->where(sprintf('"PublishDate" < \'%s\'', Convert::raw2sql(DBDatetime::now())));
|
||||
|
||||
$staged = $staged->setDataQuery($dataQuery);
|
||||
}
|
||||
@ -82,33 +103,16 @@ class BlogFilter extends Lumberjack
|
||||
'ClassName' => $excluded
|
||||
));
|
||||
|
||||
$gridField = new BlogFilter_GridField(
|
||||
$gridField = BlogFilterGridField::create(
|
||||
'ChildPages',
|
||||
$this->getLumberjackTitle(),
|
||||
$pages,
|
||||
$this->getLumberjackGridFieldConfig()
|
||||
);
|
||||
|
||||
$tab = new Tab('ChildPages', $this->getLumberjackTitle(), $gridField);
|
||||
$tab = Tab::create('ChildPages', $this->getLumberjackTitle(), $gridField);
|
||||
|
||||
$fields->insertBefore($tab, 'Main');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables children of non-editable pages to be edited.
|
||||
*/
|
||||
class BlogFilter_GridField extends GridField
|
||||
{
|
||||
/**
|
||||
* @param FormTransformation $transformation
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function transform(FormTransformation $transformation)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
22
src/Model/BlogFilter/BlogFilterGridField.php
Normal file
22
src/Model/BlogFilter/BlogFilterGridField.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model\BlogFilter;
|
||||
|
||||
use SilverStripe\Forms\FormTransformation;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
|
||||
/**
|
||||
* Enables children of non-editable pages to be edited.
|
||||
*/
|
||||
class BlogFilterGridField extends GridField
|
||||
{
|
||||
/**
|
||||
* @param FormTransformation $transformation
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function transform(FormTransformation $transformation)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,5 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Blog\Forms\GridField\GridFieldConfig_BlogPost;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\Tab;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
* This class is responsible for add Blog specific behaviour to Members.
|
||||
*
|
||||
@ -12,22 +25,22 @@ class BlogMemberExtension extends DataExtension
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'URLSegment' => 'Varchar',
|
||||
'BlogProfileSummary' => 'Text',
|
||||
'URLSegment' => 'Varchar',
|
||||
'BlogProfileSummary' => 'Text'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'BlogProfileImage' => 'Image',
|
||||
'BlogProfileImage' => Image::class
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array(
|
||||
'BlogPosts' => 'BlogPost',
|
||||
'BlogPosts' => BlogPost::class
|
||||
);
|
||||
|
||||
/**
|
||||
@ -80,7 +93,6 @@ class BlogMemberExtension extends DataExtension
|
||||
return $conflict->count() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -97,13 +109,13 @@ class BlogMemberExtension extends DataExtension
|
||||
Requirements::css(BLOGGER_DIR . '/css/cms.css');
|
||||
Requirements::javascript(BLOGGER_DIR . '/js/cms.js');
|
||||
|
||||
$tab = new Tab('BlogPosts', 'Blog Posts');
|
||||
$tab = Tab::create('BlogPosts', 'Blog Posts');
|
||||
|
||||
$gridField = new GridField(
|
||||
$gridField = GridField::create(
|
||||
'BlogPosts',
|
||||
'Blog Posts',
|
||||
$this->owner->BlogPosts(),
|
||||
new GridFieldConfig_BlogPost()
|
||||
GridFieldConfig_BlogPost::create()
|
||||
);
|
||||
|
||||
$tab->Fields()->add($gridField);
|
246
src/Model/BlogObject.php
Normal file
246
src/Model/BlogObject.php
Normal file
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Tab;
|
||||
use SilverStripe\Forms\TabSet;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||
|
||||
/**
|
||||
* An object shared by BlogTag and BlogCategory.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
trait BlogObject
|
||||
{
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
public function BlogPosts()
|
||||
{
|
||||
$blogPosts = parent::BlogPosts();
|
||||
|
||||
$this->extend('updateGetBlogPosts', $blogPosts);
|
||||
|
||||
return $blogPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = TabSet::create(
|
||||
'Root',
|
||||
Tab::create(
|
||||
'Main',
|
||||
TextField::create('Title', _t(self::class . '.Title', 'Title'))
|
||||
)
|
||||
);
|
||||
|
||||
$fields = FieldList::create($fields);
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return ValidationResult
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
/** @var ValidationResult $validation */
|
||||
$validation = parent::validate();
|
||||
if (!$validation->isValid()) {
|
||||
return $validation;
|
||||
}
|
||||
|
||||
$blog = $this->Blog();
|
||||
if (!$blog || !$blog->exists()) {
|
||||
return $validation;
|
||||
}
|
||||
|
||||
if ($this->getDuplicatesByField('Title')->count() > 0) {
|
||||
$validation->addError($this->getDuplicateError(), self::DUPLICATE_EXCEPTION);
|
||||
}
|
||||
|
||||
return $validation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a relative link to this category.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
return Controller::join_links(
|
||||
$this->Blog()->Link(),
|
||||
$this->getListUrlSegment(),
|
||||
$this->URLSegment
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canView($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
return $this->Blog()->canView($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canCreate($member = null, $context = array())
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
$permission = Blog::config()->grant_user_permission;
|
||||
|
||||
return Permission::checkMember($member, $permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from the parent blog or can be overwritten using a DataExtension.
|
||||
*
|
||||
* @param null|Member $member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete($member = null)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
return $this->Blog()->canDelete($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function onBeforeWrite()
|
||||
{
|
||||
parent::onBeforeWrite();
|
||||
if ($this->exists() || empty($this->URLSegment)) {
|
||||
return $this->generateURLSegment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique URLSegment from the title.
|
||||
*
|
||||
* @param int $increment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateURLSegment($increment = 0)
|
||||
{
|
||||
$increment = (int) $increment;
|
||||
$filter = URLSegmentFilter::create();
|
||||
|
||||
// Setting this to on. Because of the UI flow, it would be quite a lot of work
|
||||
// to support turning this off. (ie. the add by title flow would not work).
|
||||
// If this becomes a problem we can approach it then.
|
||||
// @see https://github.com/silverstripe/silverstripe-blog/issues/376
|
||||
$filter->setAllowMultibyte(true);
|
||||
|
||||
$this->URLSegment = $filter->filter($this->Title);
|
||||
|
||||
if ($increment > 0) {
|
||||
$this->URLSegment .= '-' . $increment;
|
||||
}
|
||||
|
||||
if ($this->getDuplicatesByField('URLSegment')->count() > 0) {
|
||||
$this->generateURLSegment($increment + 1);
|
||||
}
|
||||
|
||||
return $this->URLSegment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for objects o the same type and the same value by the given Field
|
||||
*
|
||||
* @param string $field E.g. URLSegment or Title
|
||||
* @return DataList
|
||||
*/
|
||||
protected function getDuplicatesByField($field)
|
||||
{
|
||||
$duplicates = DataList::create(self::class)
|
||||
->filter(
|
||||
[
|
||||
$field => $this->$field,
|
||||
'BlogID' => (int) $this->BlogID
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->ID) {
|
||||
$duplicates = $duplicates->exclude('ID', $this->ID);
|
||||
}
|
||||
|
||||
return $duplicates;
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the url segment for the listing page.
|
||||
* eg. 'categories' in /my-blog/categories/category-url
|
||||
*
|
||||
* This is not editable at the moment, but a method is being used incase we want
|
||||
* to make it editable in the future. We can use this method to provide logic
|
||||
* without replacing multiple areas of the code base. We're also not being opinionated
|
||||
* about how the segment should be obtained at the moment and allowing for the
|
||||
* implementation to decide.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getListUrlSegment();
|
||||
|
||||
/**
|
||||
* Returns an error message for this object when it tries to write a duplicate.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getDuplicateError();
|
||||
}
|
@ -1,5 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Assets\Image;
|
||||
use SilverStripe\Blog\Forms\BlogAdminSidebar;
|
||||
use SilverStripe\Blog\Model\BlogCategory;
|
||||
use SilverStripe\Blog\Model\BlogPostFilter;
|
||||
use SilverStripe\Blog\Model\BlogTag;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Forms\DatetimeField;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||
use SilverStripe\Forms\ListboxField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Forms\ToggleCompositeField;
|
||||
use SilverStripe\Forms\UploadField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\UnsavedRelationList;
|
||||
use SilverStripe\Security\Group;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\TagField\TagField;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
* An individual blog post.
|
||||
*
|
||||
@ -24,59 +50,65 @@ class BlogPost extends Page
|
||||
*/
|
||||
private static $restrict_authors_to_group = false;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
private static $table_name = 'BlogPost';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'PublishDate' => 'SS_Datetime',
|
||||
'PublishDate' => 'Datetime',
|
||||
'AuthorNames' => 'Varchar(1024)',
|
||||
'Summary' => 'HTMLText',
|
||||
'Summary' => 'HTMLText'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'FeaturedImage' => 'Image',
|
||||
'FeaturedImage' => Image::class
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $many_many = array(
|
||||
'Categories' => 'BlogCategory',
|
||||
'Tags' => 'BlogTag',
|
||||
'Authors' => 'Member',
|
||||
'Categories' => BlogCategory::class,
|
||||
'Tags' => BlogTag::class,
|
||||
'Authors' => Member::class
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = array(
|
||||
'ShowInMenus' => false,
|
||||
'InheritSideBar' => true,
|
||||
'ProvideComments' => true,
|
||||
'ShowInMenus' => false,
|
||||
'InheritSideBar' => true,
|
||||
'ProvideComments' => true
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $extensions = array(
|
||||
'BlogPostFilter',
|
||||
BlogPostFilter::class
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $searchable_fields = array(
|
||||
'Title',
|
||||
'Title'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $summary_fields = array(
|
||||
'Title',
|
||||
'Title'
|
||||
);
|
||||
|
||||
/**
|
||||
@ -84,7 +116,7 @@ class BlogPost extends Page
|
||||
*/
|
||||
private static $casting = array(
|
||||
'Excerpt' => 'HTMLText',
|
||||
'Date' => 'SS_Datetime',
|
||||
'Date' => 'DBDatetime'
|
||||
);
|
||||
|
||||
/**
|
||||
@ -174,9 +206,7 @@ class BlogPost extends Page
|
||||
Requirements::css(BLOGGER_DIR . '/css/cms.css');
|
||||
Requirements::javascript(BLOGGER_DIR . '/js/cms.js');
|
||||
|
||||
$self =& $this;
|
||||
|
||||
$this->beforeUpdateCMSFields(function ($fields) use ($self) {
|
||||
$this->beforeUpdateCMSFields(function ($fields) {
|
||||
$uploadField = UploadField::create('FeaturedImage', _t('BlogPost.FeaturedImage', 'Featured Image'));
|
||||
$uploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
|
||||
|
||||
@ -204,10 +234,8 @@ class BlogPost extends Page
|
||||
|
||||
$fields->insertAfter($summaryHolder, 'FeaturedImage');
|
||||
|
||||
$fields->push(HiddenField::create('MenuTitle'));
|
||||
|
||||
$urlSegment = $fields->dataFieldByName('URLSegment');
|
||||
$urlSegment->setURLPrefix($self->Parent()->RelativeLink());
|
||||
$urlSegment->setURLPrefix($this->Parent()->RelativeLink());
|
||||
|
||||
$fields->removeFieldsFromTab('Root.Main', array(
|
||||
'MenuTitle',
|
||||
@ -217,35 +245,39 @@ class BlogPost extends Page
|
||||
$authorField = ListboxField::create(
|
||||
'Authors',
|
||||
_t('BlogPost.Authors', 'Authors'),
|
||||
$self->getCandidateAuthors()->map()->toArray()
|
||||
)->setMultiple(true);
|
||||
$this->getCandidateAuthors()->map()->toArray()
|
||||
);
|
||||
|
||||
$authorNames = TextField::create(
|
||||
'AuthorNames',
|
||||
_t('BlogPost.AdditionalCredits', 'Additional Credits'),
|
||||
null,
|
||||
1024
|
||||
)->setDescription(_t(
|
||||
)->setDescription(
|
||||
_t(
|
||||
'BlogPost.AdditionalCredits_Description',
|
||||
'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 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 (!$this->canEditAuthors()) {
|
||||
$authorField = $authorField->performDisabledTransformation();
|
||||
$authorNames = $authorNames->performDisabledTransformation();
|
||||
}
|
||||
|
||||
$publishDate = DatetimeField::create('PublishDate', _t('BlogPost.PublishDate', 'Publish Date'));
|
||||
$publishDate->getDateField()->setConfig('showcalendar', true);
|
||||
if (!$self->PublishDate) {
|
||||
$publishDate->setDescription(_t(
|
||||
if (!$this->PublishDate) {
|
||||
$publishDate->setDescription(
|
||||
_t(
|
||||
'BlogPost.PublishDate_Description',
|
||||
'Will be set to "now" if published without a value.')
|
||||
'Will be set to "now" if published without a value.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get categories and tags
|
||||
$parent = $self->Parent();
|
||||
$parent = $this->Parent();
|
||||
$categories = $parent instanceof Blog
|
||||
? $parent->Categories()
|
||||
: BlogCategory::get();
|
||||
@ -253,32 +285,36 @@ class BlogPost extends Page
|
||||
? $parent->Tags()
|
||||
: BlogTag::get();
|
||||
|
||||
$options = BlogAdminSidebar::create(
|
||||
$publishDate,
|
||||
$urlSegment,
|
||||
TagField::create(
|
||||
'Categories',
|
||||
_t('BlogPost.Categories', 'Categories'),
|
||||
$categories,
|
||||
$self->Categories()
|
||||
)
|
||||
->setCanCreate($self->canCreateCategories())
|
||||
->setShouldLazyLoad(true),
|
||||
TagField::create(
|
||||
'Tags',
|
||||
_t('BlogPost.Tags', 'Tags'),
|
||||
$tags,
|
||||
$self->Tags()
|
||||
)
|
||||
->setCanCreate($self->canCreateTags())
|
||||
->setShouldLazyLoad(true),
|
||||
$authorField,
|
||||
$authorNames
|
||||
)->setTitle('Post Options');
|
||||
|
||||
$options->setName('blog-admin-sidebar');
|
||||
|
||||
$fields->insertBefore($options, 'Root');
|
||||
// @todo: Reimplement the sidebar
|
||||
// $options = BlogAdminSidebar::create(
|
||||
$fields->addFieldsToTab(
|
||||
'Root.PostOptions',
|
||||
[
|
||||
$publishDate,
|
||||
$urlSegment,
|
||||
TagField::create(
|
||||
'Categories',
|
||||
_t('BlogPost.Categories', 'Categories'),
|
||||
$categories,
|
||||
$this->Categories()
|
||||
)
|
||||
->setCanCreate($this->canCreateCategories())
|
||||
->setShouldLazyLoad(true),
|
||||
TagField::create(
|
||||
'Tags',
|
||||
_t('BlogPost.Tags', 'Tags'),
|
||||
$tags,
|
||||
$this->Tags()
|
||||
)
|
||||
->setCanCreate($this->canCreateTags())
|
||||
->setShouldLazyLoad(true),
|
||||
$authorField,
|
||||
$authorNames
|
||||
]
|
||||
);
|
||||
// )->setTitle('Post Options');
|
||||
// $options->setName('blog-admin-sidebar');
|
||||
// $fields->insertBefore($options, 'Root');
|
||||
});
|
||||
|
||||
$fields = parent::getCMSFields();
|
||||
@ -297,11 +333,11 @@ class BlogPost extends Page
|
||||
{
|
||||
if ($this->config()->restrict_authors_to_group) {
|
||||
return Group::get()->filter('Code', $this->config()->restrict_authors_to_group)->first()->Members();
|
||||
} else {
|
||||
$list = Member::get();
|
||||
$this->extend('updateCandidateAuthors', $list);
|
||||
return $list;
|
||||
}
|
||||
|
||||
$list = Member::get();
|
||||
$this->extend('updateCandidateAuthors', $list);
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -414,12 +450,12 @@ class BlogPost extends Page
|
||||
public function onBeforePublish()
|
||||
{
|
||||
/**
|
||||
* @var SS_Datetime $publishDate
|
||||
* @var DBDatetime $publishDate
|
||||
*/
|
||||
$publishDate = $this->dbObject('PublishDate');
|
||||
|
||||
if (!$publishDate->getValue()) {
|
||||
$this->PublishDate = SS_Datetime::now()->getValue();
|
||||
$this->PublishDate = DBDatetime::now()->getValue();
|
||||
$this->write();
|
||||
}
|
||||
}
|
||||
@ -461,15 +497,15 @@ class BlogPost extends Page
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->canEdit($member)) {
|
||||
if ($this->canEdit($member)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var SS_Datetime $publishDate
|
||||
* @var DBDatetime $publishDate
|
||||
*/
|
||||
$publishDate = $this->dbObject('PublishDate');
|
||||
if(!$publishDate->exists()) {
|
||||
if (!$publishDate->exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -565,7 +601,7 @@ class BlogPost extends Page
|
||||
public function getMonthlyArchiveLink($type = 'day')
|
||||
{
|
||||
/**
|
||||
* @var SS_Datetime $date
|
||||
* @var DBDatetime $date
|
||||
*/
|
||||
$date = $this->dbObject('PublishDate');
|
||||
|
||||
@ -593,7 +629,7 @@ class BlogPost extends Page
|
||||
public function getYearlyArchiveLink()
|
||||
{
|
||||
/**
|
||||
* @var SS_Datetime $date
|
||||
* @var DBDatetime $date
|
||||
*/
|
||||
$date = $this->dbObject('PublishDate');
|
||||
|
||||
@ -607,7 +643,7 @@ class BlogPost extends Page
|
||||
*/
|
||||
public function getCredits()
|
||||
{
|
||||
$list = new ArrayList();
|
||||
$list = ArrayList::create();
|
||||
|
||||
$list->merge($this->getDynamicCredits());
|
||||
$list->merge($this->getStaticCredits());
|
||||
@ -708,11 +744,3 @@ class BlogPost extends Page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class BlogPost_Controller extends Page_Controller
|
||||
{
|
||||
}
|
14
src/Model/BlogPostController.php
Normal file
14
src/Model/BlogPostController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use PageController;
|
||||
|
||||
/**
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class BlogPostController extends PageController
|
||||
{
|
||||
|
||||
}
|
66
src/Model/BlogPostFilter.php
Normal file
66
src/Model/BlogPostFilter.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\Admin\LeftAndMain;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DataQuery;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\ORM\Versioning\Versioned;
|
||||
use SilverStripe\Security\Permission;
|
||||
|
||||
/**
|
||||
* This is responsible for filtering only published posts to users who do not have permission to
|
||||
* view non-published posts.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*/
|
||||
class BlogPostFilter extends DataExtension
|
||||
{
|
||||
/**
|
||||
* Augment queries so that we don't fetch unpublished articles.
|
||||
*
|
||||
* @param SQLQuery $query
|
||||
*/
|
||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||
{
|
||||
$stage = Versioned::get_stage();
|
||||
|
||||
if (Controller::curr() instanceof LeftAndMain) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($stage == 'Live' || !Permission::check('VIEW_DRAFT_CONTENT')) {
|
||||
$query->addWhere(sprintf(
|
||||
'"PublishDate" < \'%s\'',
|
||||
Convert::raw2sql(DBDatetime::now())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This is a fix so that when we try to fetch subclasses of BlogPost, lazy loading includes the
|
||||
* BlogPost table in its query. Leaving this table out means the default sort order column
|
||||
* PublishDate causes an error.
|
||||
*
|
||||
* @see https://github.com/silverstripe/silverstripe-framework/issues/1682
|
||||
*
|
||||
* @param SQLSelect $query
|
||||
* @param DataQuery $dataQuery
|
||||
* @param DataObject $dataObject
|
||||
*/
|
||||
public function augmentLoadLazyFields(SQLSelect &$query, DataQuery &$dataQuery = null, $dataObject)
|
||||
{
|
||||
$dataQuery->innerJoin(
|
||||
DataObject::getSchema()->tableName(BlogPost::class),
|
||||
'"SiteTree"."ID" = "BlogPost"."ID"'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
/**
|
||||
* Customise blog post to support comment notifications.
|
||||
*
|
79
src/Model/BlogTag.php
Normal file
79
src/Model/BlogTag.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Model\BlogObject;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Blog\Model\CategorisationObject;
|
||||
|
||||
/**
|
||||
* A blog tag for keyword descriptions of a blog post.
|
||||
*
|
||||
* @package silverstripe
|
||||
* @subpackage blog
|
||||
*
|
||||
* @method Blog Blog()
|
||||
*
|
||||
* @property string $Title
|
||||
* @property string $URLSegment
|
||||
* @property int $BlogID
|
||||
*/
|
||||
class BlogTag extends DataObject implements CategorisationObject
|
||||
{
|
||||
use BlogObject;
|
||||
|
||||
/**
|
||||
* Use an exception code so that attempted writes can continue on
|
||||
* duplicate errors.
|
||||
*
|
||||
* @const string
|
||||
* This must be a string because ValidationException has decided we can't use int
|
||||
*/
|
||||
const DUPLICATE_EXCEPTION = 'DUPLICATE';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
private static $table_name = 'BlogTag';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)',
|
||||
'URLSegment' => 'Varchar(255)'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => Blog::class
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array(
|
||||
'BlogPosts' => BlogPost::class
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getListUrlSegment()
|
||||
{
|
||||
return 'tag';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDuplicateError()
|
||||
{
|
||||
return _t('BlogTag.Duplicate', 'A blog tag already exists with that name.');
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Model;
|
||||
|
||||
/**
|
||||
* @method ManyManyList BlogPosts
|
||||
*/
|
||||
interface CategorisationObject
|
||||
{
|
||||
|
||||
}
|
@ -1,9 +1,17 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists('Widget')) {
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
/**
|
||||
* @method Blog Blog()
|
||||
*
|
||||
@ -46,7 +54,7 @@ class BlogArchiveWidget extends Widget
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
'Blog' => Blog::class,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -54,13 +62,11 @@ class BlogArchiveWidget extends Widget
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$self =& $this;
|
||||
|
||||
$this->beforeUpdateCMSFields(function ($fields) use ($self) {
|
||||
$this->beforeUpdateCMSFields(function ($fields) {
|
||||
/**
|
||||
* @var Enum $archiveType
|
||||
*/
|
||||
$archiveType = $self->dbObject('ArchiveType');
|
||||
$archiveType = $this->dbObject('ArchiveType');
|
||||
|
||||
$type = $archiveType->enumValues();
|
||||
|
||||
@ -72,7 +78,11 @@ class BlogArchiveWidget extends Widget
|
||||
* @var FieldList $fields
|
||||
*/
|
||||
$fields->merge(array(
|
||||
DropdownField::create('BlogID', _t('BlogArchiveWidget.Blog', 'Blog'), Blog::get()->map()),
|
||||
DropdownField::create(
|
||||
'BlogID',
|
||||
_t('BlogArchiveWidget.Blog', 'Blog'),
|
||||
Blog::get()->map()
|
||||
),
|
||||
DropdownField::create('ArchiveType', _t('BlogArchiveWidget.ArchiveType', 'ArchiveType'), $type),
|
||||
NumericField::create('NumberToDisplay', _t('BlogArchiveWidget.NumberToDisplay', 'No. to Display'))
|
||||
));
|
||||
@ -132,7 +142,3 @@ class BlogArchiveWidget extends Widget
|
||||
return $archive;
|
||||
}
|
||||
}
|
||||
|
||||
class BlogArchiveWidget_Controller extends Widget_Controller
|
||||
{
|
||||
}
|
14
src/Widgets/BlogArchiveWidgetController.php
Normal file
14
src/Widgets/BlogArchiveWidgetController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Widgets\Controllers\WidgetController;
|
||||
|
||||
class BlogArchiveWidgetController extends WidgetController
|
||||
{
|
||||
|
||||
}
|
127
src/Widgets/BlogCategoriesWidget.php
Normal file
127
src/Widgets/BlogCategoriesWidget.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
/**
|
||||
* @method Blog Blog()
|
||||
*/
|
||||
class BlogCategoriesWidget extends Widget
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $title = 'Categories';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $cmsTitle = 'Blog Categories';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $description = 'Displays a list of blog categories.';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Limit' => 'Int',
|
||||
'Order' => 'Varchar',
|
||||
'Direction' => 'Varchar',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => Blog::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$this->beforeUpdateCMSFields(function (FieldList $fields) {
|
||||
$fields[] = DropdownField::create(
|
||||
'BlogID',
|
||||
_t('BlogCategoriesWidget.Blog', 'Blog'),
|
||||
Blog::get()->map()
|
||||
);
|
||||
|
||||
$fields[] = NumericField::create(
|
||||
'Limit',
|
||||
_t('BlogCategoriesWidget.Limit.Label', 'Limit'),
|
||||
0
|
||||
)
|
||||
->setDescription(
|
||||
_t(
|
||||
'BlogCategoriesWidget.Limit.Description',
|
||||
'Limit the number of categories shown by this widget (set to 0 to show all categories).'
|
||||
)
|
||||
)
|
||||
->setMaxLength(3);
|
||||
|
||||
$fields[] = DropdownField::create(
|
||||
'Order',
|
||||
_t('BlogCategoriesWidget.Sort.Label', 'Sort'),
|
||||
array('Title' => 'Title', 'Created' => 'Created', 'LastEdited' => 'Updated')
|
||||
)
|
||||
->setDescription(
|
||||
_t('BlogCategoriesWidget.Sort.Description', 'Change the order of categories shown by this widget.')
|
||||
);
|
||||
|
||||
$fields[] = DropdownField::create(
|
||||
'Direction',
|
||||
_t('BlogCategoriesWidget.Direction.Label', 'Direction'),
|
||||
array('ASC' => 'Ascending', 'DESC' => 'Descending')
|
||||
)
|
||||
->setDescription(
|
||||
_t(
|
||||
'BlogCategoriesWidget.Direction.Description',
|
||||
'Change the direction of ordering of categories shown by this widget.'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return parent::getCMSFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
$blog = $this->Blog();
|
||||
|
||||
if (!$blog) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$query = $blog->Categories();
|
||||
|
||||
if ($this->Limit) {
|
||||
$query = $query->limit(Convert::raw2sql($this->Limit));
|
||||
}
|
||||
|
||||
if ($this->Order && $this->Direction) {
|
||||
$query = $query->sort(Convert::raw2sql($this->Order), Convert::raw2sql($this->Direction));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
|
14
src/Widgets/BlogCategoriesWidgetController.php
Normal file
14
src/Widgets/BlogCategoriesWidgetController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Widgets\Controllers\WidgetController;
|
||||
|
||||
class BlogCategoriesWidgetController extends WidgetController
|
||||
{
|
||||
|
||||
}
|
@ -1,9 +1,16 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists("Widget")) {
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
/**
|
||||
* @method Blog Blog()
|
||||
*
|
||||
@ -37,7 +44,7 @@ class BlogRecentPostsWidget extends Widget
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
'Blog' => Blog::class,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -74,7 +81,3 @@ class BlogRecentPostsWidget extends Widget
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
class BlogRecentPostsWidget_Controller extends Widget_Controller
|
||||
{
|
||||
}
|
14
src/Widgets/BlogRecentPostsWidgetController.php
Normal file
14
src/Widgets/BlogRecentPostsWidgetController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Widgets\Controllers\WidgetController;
|
||||
|
||||
class BlogRecentPostsWidgetController extends WidgetController
|
||||
{
|
||||
|
||||
}
|
@ -1,9 +1,19 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists('Widget')) {
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
/**
|
||||
* @method Blog Blog()
|
||||
*/
|
||||
@ -33,7 +43,7 @@ class BlogTagsCloudWidget extends Widget
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
'Blog' => Blog::class,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -46,8 +56,11 @@ class BlogTagsCloudWidget extends Widget
|
||||
* @var FieldList $fields
|
||||
*/
|
||||
$fields->push(
|
||||
DropdownField::create('BlogID', _t('BlogTagsCloudWidget.Blog',
|
||||
'Blog'), Blog::get()->map())
|
||||
DropdownField::create(
|
||||
'BlogID',
|
||||
_t('BlogTagsCloudWidget.Blog', 'Blog'),
|
||||
Blog::get()->map()
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@ -105,7 +118,3 @@ class BlogTagsCloudWidget extends Widget
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
class BlogTagsCloudWidget_Controller extends Widget_Controller
|
||||
{
|
||||
}
|
13
src/Widgets/BlogTagsCloudWidgetController.php
Normal file
13
src/Widgets/BlogTagsCloudWidgetController.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Widgets\Controllers\WidgetController;
|
||||
|
||||
class BlogTagsCloudWidgetController extends WidgetController
|
||||
{
|
||||
}
|
@ -1,9 +1,18 @@
|
||||
<?php
|
||||
|
||||
if (!class_exists("Widget")) {
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
/**
|
||||
* @method Blog Blog()
|
||||
*/
|
||||
@ -37,7 +46,7 @@ class BlogTagsWidget extends Widget
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
'Blog' => 'Blog',
|
||||
'Blog' => Blog::class
|
||||
);
|
||||
|
||||
/**
|
||||
@ -47,24 +56,44 @@ class BlogTagsWidget extends Widget
|
||||
{
|
||||
$this->beforeUpdateCMSFields(function (Fieldlist $fields) {
|
||||
$fields[] = DropdownField::create(
|
||||
'BlogID', _t('BlogTagsWidget.Blog', 'Blog'), Blog::get()->map()
|
||||
'BlogID',
|
||||
_t('BlogTagsWidget.Blog', 'Blog'),
|
||||
Blog::get()->map()
|
||||
);
|
||||
|
||||
$fields[] = NumericField::create(
|
||||
'Limit', _t('BlogTagsWidget.Limit.Label', 'Limit'), 0
|
||||
'Limit',
|
||||
_t('BlogTagsWidget.Limit.Label', 'Limit'),
|
||||
0
|
||||
)
|
||||
->setDescription(_t('BlogTagsWidget.Limit.Description', 'Limit the number of tags shown by this widget (set to 0 to show all tags).'))
|
||||
->setDescription(
|
||||
_t(
|
||||
'BlogTagsWidget.Limit.Description',
|
||||
'Limit the number of tags shown by this widget (set to 0 to show all tags).'
|
||||
)
|
||||
)
|
||||
->setMaxLength(3);
|
||||
|
||||
$fields[] = DropdownField::create(
|
||||
'Order', _t('BlogTagsWidget.Sort.Label', 'Sort'), array('Title' => 'Title', 'Created' => 'Created', 'LastEdited' => 'Updated')
|
||||
'Order',
|
||||
_t('BlogTagsWidget.Sort.Label', 'Sort'),
|
||||
array('Title' => 'Title', 'Created' => 'Created', 'LastEdited' => 'Updated')
|
||||
)
|
||||
->setDescription(_t('BlogTagsWidget.Sort.Description', 'Change the order of tags shown by this widget.'));
|
||||
->setDescription(
|
||||
_t('BlogTagsWidget.Sort.Description', 'Change the order of tags shown by this widget.')
|
||||
);
|
||||
|
||||
$fields[] = DropdownField::create(
|
||||
'Direction', _t('BlogTagsWidget.Direction.Label', 'Direction'), array('ASC' => 'Ascending', 'DESC' => 'Descending')
|
||||
'Direction',
|
||||
_t('BlogTagsWidget.Direction.Label', 'Direction'),
|
||||
array('ASC' => 'Ascending', 'DESC' => 'Descending')
|
||||
)
|
||||
->setDescription(_t('BlogTagsWidget.Direction.Description', 'Change the direction of ordering of tags shown by this widget.'));
|
||||
->setDescription(
|
||||
_t(
|
||||
'BlogTagsWidget.Direction.Description',
|
||||
'Change the direction of ordering of tags shown by this widget.'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return parent::getCMSFields();
|
||||
@ -94,7 +123,3 @@ class BlogTagsWidget extends Widget
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
class BlogTagsWidget_Controller extends Widget_Controller
|
||||
{
|
||||
}
|
14
src/Widgets/BlogTagsWidgetController.php
Normal file
14
src/Widgets/BlogTagsWidgetController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Widgets;
|
||||
|
||||
if (!class_exists('\\SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\Widgets\Controllers\WidgetController;
|
||||
|
||||
class BlogTagsWidgetController extends WidgetController
|
||||
{
|
||||
|
||||
}
|
@ -1,31 +1,39 @@
|
||||
<p class="blog-post-meta">
|
||||
<% if $Categories.exists %>
|
||||
<%t Blog.PostedIn "Posted in" %>
|
||||
<% loop $Categories %>
|
||||
<a href="$Link" title="$Title">$Title</a><% if not Last %>, <% else %>;<% end_if %>
|
||||
<% end_loop %>
|
||||
<% end_if %>
|
||||
<% if $Categories.exists %>
|
||||
<%t Blog.PostedIn "Posted in" %>
|
||||
<% loop $Categories %>
|
||||
<a href="$Link" title="$Title">$Title</a><% if not Last %>, <% else %>;<% end_if %>
|
||||
<% end_loop %>
|
||||
<% end_if %>
|
||||
|
||||
<% if $Tags.exists %>
|
||||
<%t Blog.Tagged "Tagged" %>
|
||||
<% loop $Tags %>
|
||||
<a href="$Link" title="$Title">$Title</a><% if not Last %>, <% else %>;<% end_if %>
|
||||
<% end_loop %>
|
||||
<% end_if %>
|
||||
<% if $Tags.exists %>
|
||||
<%t Blog.Tagged "Tagged" %>
|
||||
<% loop $Tags %>
|
||||
<a href="$Link" title="$Title">$Title</a><% if not Last %>, <% else %>;<% end_if %>
|
||||
<% end_loop %>
|
||||
<% end_if %>
|
||||
|
||||
<% if $Comments.exists %>
|
||||
<a href="{$Link}#comments-holder">
|
||||
<%t Blog.Comments "Comments" %>
|
||||
$Comments.count
|
||||
</a>;
|
||||
<% end_if %>
|
||||
<% if $Comments.exists %>
|
||||
<a href="{$Link}#comments-holder">
|
||||
<%t Blog.Comments "Comments" %>
|
||||
$Comments.count
|
||||
</a>;
|
||||
<% end_if %>
|
||||
|
||||
<%t Blog.Posted "Posted" %>
|
||||
<a href="$MonthlyArchiveLink">$PublishDate.ago</a>
|
||||
<%t Blog.Posted "Posted" %>
|
||||
<a href="$MonthlyArchiveLink">$PublishDate.ago</a>
|
||||
|
||||
<% if $Credits %>
|
||||
<%t Blog.By "by" %>
|
||||
<% loop $Credits %><% if not $First && not $Last %>, <% end_if %><% if not $First && $Last %> <%t Blog.AND "and" %> <% end_if %><% if $URLSegment %><a href="$URL">$Name.XML</a><% else %>$Name.XML<% end_if %><% end_loop %>
|
||||
<% end_if %>
|
||||
<% if $Credits %>
|
||||
<%t Blog.By "by" %>
|
||||
|
||||
<% loop $Credits %>
|
||||
<% if not $First && not $Last %>, <% end_if %>
|
||||
<% if not $First && $Last %> <%t Blog.AND "and" %> <% end_if %>
|
||||
<% if $URLSegment %>
|
||||
<a href="$URL">$Name.XML</a>
|
||||
<% else %>
|
||||
$Name.XML
|
||||
<% end_if %>
|
||||
<% end_loop %>
|
||||
<% end_if %>
|
||||
</p>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div>
|
||||
<% if $CurrentProfile.BlogProfileImage %>
|
||||
<div class="profile-image">
|
||||
$CurrentProfile.BlogProfileImage.setWidth(180)
|
||||
$CurrentProfile.BlogProfileImage.ScaleWidth(180)
|
||||
</div>
|
||||
<% end_if %>
|
||||
<div class="profile-summary">
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<p class="post-image">
|
||||
<a href="$Link" title="<%t Blog.ReadMoreAbout "Read more about '{title}'..." title=$Title %>">
|
||||
$FeaturedImage.setWidth(795)
|
||||
$FeaturedImage.ScaleWidth(795)
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<h1>$Title</h1>
|
||||
|
||||
<% if $FeaturedImage %>
|
||||
<p class="post-image">$FeaturedImage.setWidth(795)</p>
|
||||
<p class="post-image">$FeaturedImage.ScaleWidth(795)</p>
|
||||
<% end_if %>
|
||||
|
||||
<div class="content">$Content</div>
|
@ -1,5 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Model\BlogCategory;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Blog\Model\BlogTag;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\ValidationException;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
@ -8,7 +20,7 @@ class BlogCategoryTest extends FunctionalTest
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -17,7 +29,7 @@ class BlogCategoryTest extends FunctionalTest
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
|
||||
DBDatetime::set_mock_now('2013-10-10 20:00:00');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,7 +37,7 @@ class BlogCategoryTest extends FunctionalTest
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
SS_Datetime::clear_mock_now();
|
||||
DBDatetime::clear_mock_now();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
@ -42,12 +54,12 @@ class BlogCategoryTest extends FunctionalTest
|
||||
$member->logout();
|
||||
}
|
||||
|
||||
$this->objFromFixture('BlogPost', 'FirstBlogPost');
|
||||
$this->objFromFixture(BlogPost::class, 'FirstBlogPost');
|
||||
|
||||
/**
|
||||
* @var BlogCategory $category
|
||||
*/
|
||||
$category = $this->objFromFixture('BlogCategory', 'FirstCategory');
|
||||
$category = $this->objFromFixture(BlogCategory::class, 'FirstCategory');
|
||||
|
||||
$this->assertEquals(5, $category->BlogPosts()->count(), 'Category blog post count');
|
||||
}
|
||||
@ -57,7 +69,7 @@ class BlogCategoryTest extends FunctionalTest
|
||||
*/
|
||||
public function testAllowMultibyteUrlSegment()
|
||||
{
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
$cat = new BlogCategory();
|
||||
$cat->BlogID = $blog->ID;
|
||||
$cat->Title = 'تست';
|
||||
@ -72,10 +84,10 @@ class BlogCategoryTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$this->objFromFixture('Member', 'Admin');
|
||||
$this->objFromFixture(Member::class, 'Admin');
|
||||
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$category = $this->objFromFixture('BlogCategory', 'SecondCategory');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
$category = $this->objFromFixture(BlogCategory::class, 'SecondCategory');
|
||||
|
||||
$this->assertFalse($category->canView($editor), 'Editor should not be able to view category.');
|
||||
}
|
||||
@ -87,20 +99,20 @@ class BlogCategoryTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$category = $this->objFromFixture('BlogCategory', 'FirstCategory');
|
||||
$category = $this->objFromFixture(BlogCategory::class, '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');
|
||||
$category = $this->objFromFixture(BlogCategory::class, 'SecondCategory');
|
||||
|
||||
$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');
|
||||
$category = $this->objFromFixture(BlogCategory::class, '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.');
|
||||
@ -110,10 +122,10 @@ class BlogCategoryTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$category = singleton('BlogCategory');
|
||||
$category = singleton(BlogCategory::class);
|
||||
|
||||
$this->assertTrue($category->canCreate($admin), 'Admin should be able to create category.');
|
||||
$this->assertTrue($category->canCreate($editor), 'Editor should be able to create category.');
|
||||
@ -123,24 +135,25 @@ class BlogCategoryTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$category = $this->objFromFixture('BlogCategory', 'FirstCategory');
|
||||
$category = $this->objFromFixture(BlogCategory::class, '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');
|
||||
$category = $this->objFromFixture(BlogCategory::class, 'SecondCategory');
|
||||
$this->assertTrue($category->canDelete($admin), 'Admin should be able to delete category.');
|
||||
$this->assertFalse($category->canDelete($editor), 'Editor should not be able to delete category.');
|
||||
|
||||
$category = $this->objFromFixture('BlogCategory', 'ThirdCategory');
|
||||
$category = $this->objFromFixture(BlogCategory::class, '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.');
|
||||
}
|
||||
|
||||
public function testDuplicateCategories() {
|
||||
public function testDuplicateCategories()
|
||||
{
|
||||
$blog = new Blog();
|
||||
$blog->Title = 'Testing for duplicate categories';
|
||||
$blog->write();
|
||||
@ -148,18 +161,20 @@ class BlogCategoryTest extends FunctionalTest
|
||||
$category = new BlogCategory();
|
||||
$category->Title = 'Test';
|
||||
$category->BlogID = $blog->ID;
|
||||
$category->URLSegment = 'test';
|
||||
$category->write();
|
||||
|
||||
$category = new BlogCategory();
|
||||
$category->Title = 'Test';
|
||||
$category->URLSegment = 'test';
|
||||
$category->BlogID = $blog->ID;
|
||||
try {
|
||||
$category->write();
|
||||
$this->fail('Duplicate BlogCategory written');
|
||||
} catch (ValidationException $e) {
|
||||
$codeList = $e->getResult()->codeList();
|
||||
$this->assertCount(1, $codeList);
|
||||
$this->assertEquals(BlogTag::DUPLICATE_EXCEPTION, $codeList[0]);
|
||||
$messages = $e->getResult()->getMessages();
|
||||
$this->assertCount(1, $messages);
|
||||
$this->assertEquals(BlogTag::DUPLICATE_EXCEPTION, $messages[0]['messageType']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
* @coversDefaultClass \SilverStripe\Blog\Model\BlogPostFilter
|
||||
*/
|
||||
class BlogPostFilterTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
|
||||
DBDatetime::set_mock_now('2013-10-10 20:00:00');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
SS_Datetime::clear_mock_now();
|
||||
DBDatetime::clear_mock_now();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that unpublished articles are not returned
|
||||
* @covers ::augmentSQL
|
||||
*/
|
||||
public function testFilter()
|
||||
{
|
||||
$member = Member::currentUser();
|
||||
@ -35,11 +47,11 @@ class BlogPostFilterTest extends SapphireTest
|
||||
/**
|
||||
* @var Blog $blog
|
||||
*/
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
|
||||
$this->assertEquals(3, $blog->AllChildren()->Count(), 'Filtered blog posts');
|
||||
|
||||
SS_Datetime::set_mock_now('2020-01-01 00:00:00');
|
||||
DBDatetime::set_mock_now('2020-01-01 00:00:00');
|
||||
|
||||
$this->assertEquals(5, $blog->AllChildren()->Count(), 'Unfiltered blog posts');
|
||||
}
|
||||
|
@ -1,11 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class BlogPostNotificationsTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
public function testUpdateNotificationRecipients()
|
||||
{
|
||||
@ -13,8 +19,8 @@ class BlogPostNotificationsTest extends SapphireTest
|
||||
$this->markTestSkipped('Comments Notification module is not installed');
|
||||
}
|
||||
|
||||
$blogPost = $this->objFromFixture('BlogPost', 'PostC');
|
||||
$comment = new Comment();
|
||||
$blogPost = $this->objFromFixture(BlogPost::class, 'PostC');
|
||||
$comment = new \SilverStripe\Comments\Model\Comment();
|
||||
$comment->Comment = 'This is a comment';
|
||||
$comment->write();
|
||||
$recipients = $blogPost->notificationRecipients(
|
||||
@ -27,8 +33,10 @@ class BlogPostNotificationsTest extends SapphireTest
|
||||
}
|
||||
|
||||
sort($segments);
|
||||
$this->assertEquals(array('blog-contributor', 'blog-editor',
|
||||
'blog-writer', ), $segments);
|
||||
$this->assertEquals(
|
||||
array('blog-contributor', 'blog-editor', 'blog-writer'),
|
||||
$segments
|
||||
);
|
||||
}
|
||||
|
||||
public function testUpdateNotificationSubject()
|
||||
@ -36,8 +44,8 @@ class BlogPostNotificationsTest extends SapphireTest
|
||||
if (!class_exists('CommentNotifier')) {
|
||||
$this->markTestSkipped('Comments Notification module is not installed');
|
||||
}
|
||||
$blogPost = $this->objFromFixture('BlogPost', 'PostC');
|
||||
$comment = new Comment();
|
||||
$blogPost = $this->objFromFixture(BlogPost::class, 'PostC');
|
||||
$comment = new SilverStripe\Comments\Model\Comment();
|
||||
$comment->Comment = 'This is a comment';
|
||||
$comment->write();
|
||||
$recipients = $blogPost->notificationRecipients(
|
||||
|
@ -1,26 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class BlogPostTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
SS_Datetime::clear_mock_now();
|
||||
DBDatetime::clear_mock_now();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
@ -29,12 +30,15 @@ class BlogPostTest extends SapphireTest
|
||||
*/
|
||||
public function testCanView($date, $user, $page, $canView)
|
||||
{
|
||||
$userRecord = $this->objFromFixture('Member', $user);
|
||||
$pageRecord = $this->objFromFixture('BlogPost', $page);
|
||||
SS_Datetime::set_mock_now($date);
|
||||
$userRecord = $this->objFromFixture(Member::class, $user);
|
||||
$pageRecord = $this->objFromFixture(BlogPost::class, $page);
|
||||
DBDatetime::set_mock_now($date);
|
||||
$this->assertEquals($canView, $pageRecord->canView($userRecord));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function canViewProvider()
|
||||
{
|
||||
$someFutureDate = '2013-10-10 20:00:00';
|
||||
@ -70,12 +74,12 @@ class BlogPostTest extends SapphireTest
|
||||
|
||||
public function testCandidateAuthors()
|
||||
{
|
||||
$blogpost = $this->objFromFixture('BlogPost', 'PostC');
|
||||
$blogpost = $this->objFromFixture(BlogPost::class, 'PostC');
|
||||
|
||||
$this->assertEquals(7, $blogpost->getCandidateAuthors()->count());
|
||||
|
||||
//Set the group to draw Members from
|
||||
Config::inst()->update('BlogPost', 'restrict_authors_to_group', 'blogusers');
|
||||
Config::inst()->update(BlogPost::class, 'restrict_authors_to_group', 'blogusers');
|
||||
|
||||
$this->assertEquals(3, $blogpost->getCandidateAuthors()->count());
|
||||
|
||||
@ -86,12 +90,12 @@ class BlogPostTest extends SapphireTest
|
||||
|
||||
public function testCanViewFuturePost()
|
||||
{
|
||||
$blogPost = $this->objFromFixture('BlogPost', 'NullPublishDate');
|
||||
$blogPost = $this->objFromFixture(BlogPost::class, 'NullPublishDate');
|
||||
|
||||
$editor = $this->objFromFixture('Member', 'BlogEditor');
|
||||
$editor = $this->objFromFixture(Member::class, 'BlogEditor');
|
||||
$this->assertTrue($blogPost->canView($editor));
|
||||
|
||||
$visitor = $this->objFromFixture('Member', 'Visitor');
|
||||
$visitor = $this->objFromFixture(Member::class, 'Visitor');
|
||||
$this->assertFalse($blogPost->canView($visitor));
|
||||
}
|
||||
|
||||
@ -101,10 +105,10 @@ class BlogPostTest extends SapphireTest
|
||||
*/
|
||||
public function testGetDate()
|
||||
{
|
||||
$blogPost = $this->objFromFixture('BlogPost', 'NullPublishDate');
|
||||
$blogPost = $this->objFromFixture(BlogPost::class, 'NullPublishDate');
|
||||
$this->assertNull($blogPost->getDate());
|
||||
|
||||
$blogPost = $this->objFromFixture('BlogPost', 'PostA');
|
||||
$blogPost = $this->objFromFixture(BlogPost::class, 'PostA');
|
||||
$this->assertEquals('2012-01-09 15:00:00', $blogPost->getDate());
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\Blog\Model\BlogTag;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\ValidationException;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class BlogTagTest extends FunctionalTest
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -17,7 +29,7 @@ class BlogTagTest extends FunctionalTest
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
|
||||
DBDatetime::set_mock_now('2013-10-10 20:00:00');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,7 +37,7 @@ class BlogTagTest extends FunctionalTest
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
SS_Datetime::clear_mock_now();
|
||||
DBDatetime::clear_mock_now();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
@ -42,12 +54,12 @@ class BlogTagTest extends FunctionalTest
|
||||
$member->logout();
|
||||
}
|
||||
|
||||
$this->objFromFixture('BlogPost', 'FirstBlogPost');
|
||||
$this->objFromFixture(BlogPost::class, 'FirstBlogPost');
|
||||
|
||||
/**
|
||||
* @var BlogTag $tag
|
||||
*/
|
||||
$tag = $this->objFromFixture('BlogTag', 'FirstTag');
|
||||
$tag = $this->objFromFixture(BlogTag::class, 'FirstTag');
|
||||
|
||||
$this->assertEquals(1, $tag->BlogPosts()->count(), 'Tag blog post count');
|
||||
}
|
||||
@ -57,7 +69,7 @@ class BlogTagTest extends FunctionalTest
|
||||
*/
|
||||
public function testAllowMultibyteUrlSegment()
|
||||
{
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
$tag = new BlogTag();
|
||||
$tag->BlogID = $blog->ID;
|
||||
$tag->Title = 'تست';
|
||||
@ -75,15 +87,15 @@ class BlogTagTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$tag = $this->objFromFixture('BlogTag', 'FirstTag');
|
||||
$tag = $this->objFromFixture(BlogTag::class, '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');
|
||||
$tag = $this->objFromFixture(BlogTag::class, '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.');
|
||||
@ -93,20 +105,20 @@ class BlogTagTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$tag = $this->objFromFixture('BlogTag', 'FirstTag');
|
||||
$tag = $this->objFromFixture(BlogTag::class, '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');
|
||||
$tag = $this->objFromFixture(BlogTag::class, 'SecondTag');
|
||||
|
||||
$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');
|
||||
$tag = $this->objFromFixture(BlogTag::class, '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.');
|
||||
@ -116,10 +128,10 @@ class BlogTagTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$tag = singleton('BlogTag');
|
||||
$tag = singleton(BlogTag::class);
|
||||
|
||||
$this->assertTrue($tag->canCreate($admin), 'Admin should be able to create tag.');
|
||||
$this->assertTrue($tag->canCreate($editor), 'Editor should be able to create tag.');
|
||||
@ -129,26 +141,27 @@ class BlogTagTest extends FunctionalTest
|
||||
{
|
||||
$this->useDraftSite();
|
||||
|
||||
$admin = $this->objFromFixture('Member', 'Admin');
|
||||
$editor = $this->objFromFixture('Member', 'Editor');
|
||||
$admin = $this->objFromFixture(Member::class, 'Admin');
|
||||
$editor = $this->objFromFixture(Member::class, 'Editor');
|
||||
|
||||
$tag = $this->objFromFixture('BlogTag', 'FirstTag');
|
||||
$tag = $this->objFromFixture(BlogTag::class, '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');
|
||||
$tag = $this->objFromFixture(BlogTag::class, 'SecondTag');
|
||||
|
||||
$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');
|
||||
$tag = $this->objFromFixture(BlogTag::class, '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.');
|
||||
}
|
||||
|
||||
public function testDuplicateTagsForURLSegment() {
|
||||
public function testDuplicateTagsForURLSegment()
|
||||
{
|
||||
$blog = new Blog();
|
||||
$blog->Title = 'Testing for duplicates blog';
|
||||
$blog->write();
|
||||
@ -162,11 +175,11 @@ class BlogTagTest extends FunctionalTest
|
||||
$tag2->Title = 'cat test';
|
||||
$tag2->BlogID = $blog->ID;
|
||||
$tag2->write();
|
||||
$this->assertEquals('cat-test-0', $tag2->URLSegment);
|
||||
|
||||
$this->assertEquals('cat-test-1', $tag2->URLSegment);
|
||||
}
|
||||
|
||||
public function testDuplicateTags() {
|
||||
public function testDuplicateTags()
|
||||
{
|
||||
$blog = new Blog();
|
||||
$blog->Title = 'Testing for duplicate tags';
|
||||
$blog->write();
|
||||
@ -174,19 +187,20 @@ class BlogTagTest extends FunctionalTest
|
||||
$tag = new BlogTag();
|
||||
$tag->Title = 'Test';
|
||||
$tag->BlogID = $blog->ID;
|
||||
$tag->URLSegment = 'test';
|
||||
$tag->write();
|
||||
|
||||
$tag = new BlogTag();
|
||||
$tag->Title = 'Test';
|
||||
$tag->URLSegment = 'test';
|
||||
$tag->BlogID = $blog->ID;
|
||||
try {
|
||||
$tag->write();
|
||||
$this->fail('Duplicate BlogTag written');
|
||||
} catch (ValidationException $e) {
|
||||
$codeList = $e->getResult()->codeList();
|
||||
$this->assertCount(1, $codeList);
|
||||
$this->assertEquals(BlogTag::DUPLICATE_EXCEPTION, $codeList[0]);
|
||||
$messages = $e->getResult()->getMessages();
|
||||
$this->assertCount(1, $messages);
|
||||
$this->assertEquals(BlogTag::DUPLICATE_EXCEPTION, $messages[0]['messageType']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
<?php
|
||||
|
||||
class BlogTagsCloudWidgetTest extends SapphireTest {
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Widgets\BlogTagsCloudWidget;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
|
||||
class BlogTagsCloudWidgetTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
public function testGetCMSFields() {
|
||||
if (!class_exists('Widget')) {
|
||||
public function testGetCMSFields()
|
||||
{
|
||||
if (!class_exists('SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
$this->markTestSkipped('Widgets module not installed');
|
||||
}
|
||||
|
||||
@ -23,12 +32,13 @@ class BlogTagsCloudWidgetTest extends SapphireTest {
|
||||
$this->assertEquals($expected, $names);
|
||||
}
|
||||
|
||||
public function testGetTags() {
|
||||
if (!class_exists('Widget')) {
|
||||
public function testGetTags()
|
||||
{
|
||||
if (!class_exists('SilverStripe\\Widgets\\Model\\Widget')) {
|
||||
$this->markTestSkipped('Widgets module not installed');
|
||||
}
|
||||
$widget = new BlogTagsCloudWidget();
|
||||
$blog = $this->objFromFixture('Blog', 'FourthBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FourthBlog');
|
||||
$widget->BlogID = $blog->ID;
|
||||
$widget->write();
|
||||
$tags = $widget->getTags()->toArray();
|
||||
|
@ -1,5 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Blog\Tests;
|
||||
|
||||
use SilverStripe\Blog\Model\Blog;
|
||||
use SilverStripe\Blog\Model\BlogController;
|
||||
use SilverStripe\Blog\Model\BlogPost;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\DataModel;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
@ -8,7 +24,7 @@ class BlogTest extends SapphireTest
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fixture_file = 'blog.yml';
|
||||
protected static $fixture_file = 'blog.yml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -18,12 +34,12 @@ class BlogTest extends SapphireTest
|
||||
parent::setUp();
|
||||
|
||||
Config::nest();
|
||||
SS_Datetime::set_mock_now('2013-10-10 20:00:00');
|
||||
DBDatetime::set_mock_now('2013-10-10 20:00:00');
|
||||
|
||||
/**
|
||||
* @var Blog $blog
|
||||
*/
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
|
||||
$blog->publish('Stage', 'Live');
|
||||
}
|
||||
@ -33,7 +49,7 @@ class BlogTest extends SapphireTest
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
SS_Datetime::clear_mock_now();
|
||||
DBDatetime::clear_mock_now();
|
||||
Config::unnest();
|
||||
|
||||
parent::tearDown();
|
||||
@ -50,17 +66,17 @@ class BlogTest extends SapphireTest
|
||||
/**
|
||||
* @var Blog $blog
|
||||
*/
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
|
||||
Config::inst()->update('BlogPost', 'show_in_sitetree', true);
|
||||
Config::inst()->update(BlogPost::class, 'show_in_sitetree', true);
|
||||
$classes = $blog->getExcludedSiteTreeClassNames();
|
||||
|
||||
$this->assertNotContains('BlogPost', $classes, 'BlogPost class should be hidden.');
|
||||
$this->assertNotContains(BlogPost::class, $classes, 'BlogPost class should be hidden.');
|
||||
|
||||
Config::inst()->update('BlogPost', 'show_in_sitetree', false);
|
||||
Config::inst()->update(BlogPost::class, 'show_in_sitetree', false);
|
||||
$classes = $blog->getExcludedSiteTreeClassNames();
|
||||
|
||||
$this->assertContains('BlogPost', $classes, 'BlogPost class should be hidden.');
|
||||
$this->assertContains(BlogPost::class, $classes, 'BlogPost class should be hidden.');
|
||||
}
|
||||
|
||||
public function testGetArchivedBlogPosts()
|
||||
@ -74,7 +90,7 @@ class BlogTest extends SapphireTest
|
||||
/**
|
||||
* @var Blog $blog
|
||||
*/
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
|
||||
$archive = $blog->getArchivedBlogPosts(2013);
|
||||
|
||||
@ -96,7 +112,7 @@ class BlogTest extends SapphireTest
|
||||
/**
|
||||
* @var Blog $blog
|
||||
*/
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
|
||||
$link = Controller::join_links($blog->Link('archive'), '2013', '10', '01');
|
||||
|
||||
@ -135,8 +151,8 @@ class BlogTest extends SapphireTest
|
||||
*/
|
||||
public function testArchiveYear()
|
||||
{
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$controller = new Blog_Controller($blog);
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
$controller = new BlogController($blog);
|
||||
$this->requestURL($controller, 'first-post/archive/');
|
||||
$this->assertEquals(2013, $controller->getArchiveYear(), 'getArchiveYear should return 2013');
|
||||
}
|
||||
@ -156,47 +172,47 @@ class BlogTest extends SapphireTest
|
||||
/**
|
||||
* @var Blog $firstBlog
|
||||
*/
|
||||
$firstBlog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$firstBlog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
|
||||
/**
|
||||
* @var Blog $fourthBlog
|
||||
*/
|
||||
$fourthBlog = $this->objFromFixture('Blog', 'FourthBlog');
|
||||
$fourthBlog = $this->objFromFixture(Blog::class, 'FourthBlog');
|
||||
|
||||
/**
|
||||
* @var BlogPost $postA
|
||||
*/
|
||||
$postA = $this->objFromFixture('BlogPost', 'PostA');
|
||||
$postA = $this->objFromFixture(BlogPost::class, 'PostA');
|
||||
|
||||
/**
|
||||
* @var BlogPost $postB
|
||||
*/
|
||||
$postB = $this->objFromFixture('BlogPost', 'PostB');
|
||||
$postB = $this->objFromFixture(BlogPost::class, 'PostB');
|
||||
|
||||
/**
|
||||
* @var BlogPost $postC
|
||||
*/
|
||||
$postC = $this->objFromFixture('BlogPost', 'PostC');
|
||||
$postC = $this->objFromFixture(BlogPost::class, 'PostC');
|
||||
|
||||
/**
|
||||
* @var Member $editor
|
||||
*/
|
||||
$editor = $this->objFromFixture('Member', 'BlogEditor');
|
||||
$editor = $this->objFromFixture(Member::class, 'BlogEditor');
|
||||
|
||||
/**
|
||||
* @var Member $writer
|
||||
*/
|
||||
$writer = $this->objFromFixture('Member', 'Writer');
|
||||
$writer = $this->objFromFixture(Member::class, 'Writer');
|
||||
|
||||
/**
|
||||
* @var Member $contributor
|
||||
*/
|
||||
$contributor = $this->objFromFixture('Member', 'Contributor');
|
||||
$contributor = $this->objFromFixture(Member::class, 'Contributor');
|
||||
|
||||
/**
|
||||
* @var Member $visitor
|
||||
*/
|
||||
$visitor = $this->objFromFixture('Member', 'Visitor');
|
||||
$visitor = $this->objFromFixture(Member::class, 'Visitor');
|
||||
|
||||
$this->assertEquals('Editor', $fourthBlog->RoleOf($editor));
|
||||
$this->assertEquals('Contributor', $fourthBlog->RoleOf($contributor));
|
||||
@ -274,8 +290,8 @@ class BlogTest extends SapphireTest
|
||||
|
||||
public function testFilteredCategories()
|
||||
{
|
||||
$blog = $this->objFromFixture('Blog', 'FirstBlog');
|
||||
$controller = new Blog_Controller($blog);
|
||||
$blog = $this->objFromFixture(Blog::class, 'FirstBlog');
|
||||
$controller = new BlogController($blog);
|
||||
|
||||
// Root url
|
||||
$this->requestURL($controller, 'first-post');
|
||||
@ -293,10 +309,10 @@ class BlogTest extends SapphireTest
|
||||
);
|
||||
|
||||
// Posts
|
||||
$firstPostID = $this->idFromFixture('BlogPost', 'FirstBlogPost');
|
||||
$secondPostID = $this->idFromFixture('BlogPost', 'SecondBlogPost');
|
||||
$firstFuturePostID = $this->idFromFixture('BlogPost', 'FirstFutureBlogPost');
|
||||
$secondFuturePostID = $this->idFromFixture('BlogPost', 'SecondFutureBlogPost');
|
||||
$firstPostID = $this->idFromFixture(BlogPost::class, 'FirstBlogPost');
|
||||
$secondPostID = $this->idFromFixture(BlogPost::class, 'SecondBlogPost');
|
||||
$firstFuturePostID = $this->idFromFixture(BlogPost::class, 'FirstFutureBlogPost');
|
||||
$secondFuturePostID = $this->idFromFixture(BlogPost::class, 'SecondFutureBlogPost');
|
||||
|
||||
// Request first tag
|
||||
$this->requestURL($controller, 'first-post/tag/first-tag');
|
||||
@ -321,10 +337,10 @@ class BlogTest extends SapphireTest
|
||||
*/
|
||||
protected function requestURL(ContentController $controller, $url)
|
||||
{
|
||||
$request = new SS_HTTPRequest('get', $url);
|
||||
$request = new HTTPRequest('get', $url);
|
||||
$request->match('$URLSegment//$Action/$ID/$OtherID');
|
||||
$request->shift();
|
||||
$controller->init();
|
||||
$controller->doInit();
|
||||
$controller->handleRequest($request, new DataModel());
|
||||
}
|
||||
|
||||
|
132
tests/blog.yml
132
tests/blog.yml
@ -1,6 +1,6 @@
|
||||
# Mock date is set to 2013-10-01 20:00:00
|
||||
|
||||
Group:
|
||||
SilverStripe\Security\Group:
|
||||
Administrators:
|
||||
Title: Administrators
|
||||
Editors:
|
||||
@ -9,193 +9,193 @@ Group:
|
||||
Title: Blog Users
|
||||
Code: blogusers
|
||||
|
||||
Permission:
|
||||
SilverStripe\Security\Permission:
|
||||
Administrators:
|
||||
Code: ADMIN
|
||||
Group: =>Group.Administrators
|
||||
Group: =>SilverStripe\Security\Group.Administrators
|
||||
Editors:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
Group: =>Group.Editors
|
||||
Group: =>SilverStripe\Security\Group.Editors
|
||||
BlogUsers:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
Group: =>Group.BlogUsers
|
||||
Group: =>SilverStripe\Security\Group.BlogUsers
|
||||
|
||||
SiteConfig:
|
||||
SilverStripe\SiteConfig\SiteConfig:
|
||||
Default:
|
||||
CanEditType: 'OnlyTheseUsers'
|
||||
CanCreateTopLevelType: 'OnlyTheseUsers'
|
||||
EditorGroups: =>Group.Administrators,=>Group.Editors
|
||||
CreateTopLevelGroups: =>Group.Administrators,=>Group.Editors
|
||||
EditorGroups: =>SilverStripe\Security\Group.Administrators,=>SilverStripe\Security\Group.Editors
|
||||
CreateTopLevelGroups: =>SilverStripe\Security\Group.Administrators,=>SilverStripe\Security\Group.Editors
|
||||
|
||||
Member:
|
||||
SilverStripe\Security\Member:
|
||||
Admin:
|
||||
FirstName: Test
|
||||
Surname: Administrator
|
||||
Groups: =>Group.Administrators
|
||||
Groups: =>SilverStripe\Security\Group.Administrators
|
||||
Editor:
|
||||
FirstName: Test
|
||||
Surname: Editor
|
||||
Groups: =>Group.Editors
|
||||
Groups: =>SilverStripe\Security\Group.Editors
|
||||
BlogEditor:
|
||||
FirstName: Blog
|
||||
Surname: Editor
|
||||
Groups: =>Group.BlogUsers
|
||||
Groups: =>SilverStripe\Security\Group.BlogUsers
|
||||
Writer:
|
||||
FirstName: Blog
|
||||
Surname: Writer
|
||||
Groups: =>Group.BlogUsers
|
||||
Groups: =>SilverStripe\Security\Group.BlogUsers
|
||||
Contributor:
|
||||
FirstName: Blog
|
||||
Surname: Contributor
|
||||
Groups: =>Group.BlogUsers
|
||||
Groups: =>SilverStripe\Security\Group.BlogUsers
|
||||
Visitor:
|
||||
FirstName: Blog
|
||||
Surname: Visitor
|
||||
|
||||
Blog:
|
||||
SilverStripe\Blog\Model\Blog:
|
||||
FirstBlog:
|
||||
Title: 'First Blog'
|
||||
SecondBlog:
|
||||
Title: 'Second Blog'
|
||||
CanViewType: 'OnlyTheseUsers'
|
||||
CanEditType: 'OnlyTheseUsers'
|
||||
ViewerGroups: =>Group.Administrators
|
||||
EditorGroups: =>Group.Administrators
|
||||
ViewerGroups: =>SilverStripe\Security\Group.Administrators
|
||||
EditorGroups: =>SilverStripe\Security\Group.Administrators
|
||||
ThirdBlog:
|
||||
Title: 'Third Blog'
|
||||
CanEditType: 'OnlyTheseUsers'
|
||||
EditorGroups: =>Group.Editors
|
||||
EditorGroups: =>SilverStripe\Security\Group.Editors
|
||||
FourthBlog:
|
||||
Title: 'Fourth Blog'
|
||||
Editors: =>Member.BlogEditor
|
||||
Writers: =>Member.Writer
|
||||
Contributors: =>Member.Contributor
|
||||
Editors: =>SilverStripe\Security\Member.BlogEditor
|
||||
Writers: =>SilverStripe\Security\Member.Writer
|
||||
Contributors: =>SilverStripe\Security\Member.Contributor
|
||||
|
||||
BlogTag:
|
||||
SilverStripe\Blog\Model\BlogTag:
|
||||
FirstTag:
|
||||
Title: 'First Tag'
|
||||
URLSegment: 'first-tag'
|
||||
Blog: =>Blog.FirstBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
SecondTag:
|
||||
Title: 'Second Tag'
|
||||
URLSegment: 'second-tag'
|
||||
Blog: =>Blog.SecondBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.SecondBlog
|
||||
ThirdTag:
|
||||
Title: 'Third Tag'
|
||||
URLSegment: 'third-tag'
|
||||
Blog: =>Blog.ThirdBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.ThirdBlog
|
||||
|
||||
#Tags for Tag Cloud widget
|
||||
PopularTag:
|
||||
Title: 'Popular'
|
||||
URLSegment: 'popular-tag'
|
||||
Blog: =>Blog.FourthBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
CoolTag:
|
||||
Title: 'Cool'
|
||||
URLSegment: 'cool-tag'
|
||||
Blog: =>Blog.FourthBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
CatTag:
|
||||
Title: 'Cat'
|
||||
URLSegment: 'cat-tag'
|
||||
Blog: =>Blog.FourthBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
KiwiTag:
|
||||
Title: 'Kiwi'
|
||||
URLSegment: 'kiwi-tag'
|
||||
Blog: =>Blog.FourthBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
|
||||
BlogCategory:
|
||||
SilverStripe\Blog\Model\BlogCategory:
|
||||
FirstCategory:
|
||||
Title: 'First Category'
|
||||
URLSegment: 'first-category'
|
||||
Blog: =>Blog.FirstBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
SecondCategory:
|
||||
Title: 'Second Category'
|
||||
URLSegment: 'second-category'
|
||||
Blog: =>Blog.SecondBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.SecondBlog
|
||||
ThirdCategory:
|
||||
Title: 'Third Category'
|
||||
URLSegment: 'third-category'
|
||||
Blog: =>Blog.ThirdBlog
|
||||
Blog: =>SilverStripe\Blog\Model\Blog.ThirdBlog
|
||||
|
||||
BlogPost:
|
||||
SilverStripe\Blog\Model\BlogPost:
|
||||
FirstBlogPost:
|
||||
Title: 'First Post'
|
||||
URLSegment: first-post
|
||||
PublishDate: '2013-10-01 15:00:00'
|
||||
Parent: =>Blog.FirstBlog
|
||||
Tags: =>BlogTag.FirstTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.FirstTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
SecondBlogPost:
|
||||
Title: 'Second Post'
|
||||
URLSegment: second-post
|
||||
PublishDate: '2013-09-01 15:00:00'
|
||||
Parent: =>Blog.FirstBlog
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
ThirdBlogPost:
|
||||
Title: 'Old Post'
|
||||
URLSegment: old-post
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.FirstBlog
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
FirstFutureBlogPost:
|
||||
Title: 'Future Post'
|
||||
URLSegment: future-post
|
||||
PublishDate: '2015-01-01 00:00:00'
|
||||
Tags: =>BlogTag.FirstTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>Blog.FirstBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.FirstTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
SecondFutureBlogPost:
|
||||
Title: 'Future Post 2'
|
||||
URLSegment: future-post-2
|
||||
PublishDate: '2013-11-01 00:00:00'
|
||||
Tags: =>BlogTag.FirstTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>Blog.FirstBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.FirstTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FirstBlog
|
||||
PostA:
|
||||
Title: 'One Post'
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.FourthBlog
|
||||
Authors: =>Member.Writer,=>Member.Contributor
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
Authors: =>SilverStripe\Security\Member.Writer,=>SilverStripe\Security\Member.Contributor
|
||||
PostB:
|
||||
Title: 'Second Post'
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.FourthBlog
|
||||
Authors: =>Member.BlogEditor
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
Authors: =>SilverStripe\Security\Member.BlogEditor
|
||||
PostC:
|
||||
Title: 'Third Post'
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Parent: =>Blog.FourthBlog
|
||||
Authors: =>Member.BlogEditor,=>Member.Writer,=>Member.Contributor
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
Authors: =>SilverStripe\Security\Member.BlogEditor,=>SilverStripe\Security\Member.Writer,=>SilverStripe\Security\Member.Contributor
|
||||
NullPublishDate:
|
||||
Title: 'No publish date'
|
||||
PublishDate: 'NULL'
|
||||
Parent: =>Blog.FourthBlog
|
||||
Authors: =>Member.BlogEditor,=>Member.Writer,=>Member.Contributor
|
||||
PublishDate: ''
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
Authors: =>SilverStripe\Security\Member.BlogEditor,=>SilverStripe\Security\Member.Writer,=>SilverStripe\Security\Member.Contributor
|
||||
|
||||
#Posts for the tag cloud widget test
|
||||
TaggedPost1:
|
||||
Title: 'Tagged Post 1'
|
||||
URLSegment: tagged-post-1
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Tags: =>BlogTag.PopularTag,=>BlogTag.CoolTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>Blog.FourthBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag,=>SilverStripe\Blog\Model\BlogTag.CoolTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
TaggedPost2:
|
||||
Title: 'Tagged Post 2'
|
||||
URLSegment: tagged-post-2
|
||||
PublishDate: '2012-01-09 15:00:00'
|
||||
Tags: =>BlogTag.PopularTag,=>BlogTag.CoolTag,=>BlogTag.CatTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>Blog.FourthBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag,=>SilverStripe\Blog\Model\BlogTag.CoolTag,=>SilverStripe\Blog\Model\BlogTag.CatTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
TaggedPost3:
|
||||
Title: 'Tagged Post 3'
|
||||
URLSegment: tagged-post-3
|
||||
PublishDate: '2012-01-09 17:20:00'
|
||||
Tags: =>BlogTag.PopularTag,=>BlogTag.CoolTag,=>BlogTag.CatTag,=>BlogTag.KiwiTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>Blog.FourthBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag,=>SilverStripe\Blog\Model\BlogTag.CoolTag,=>SilverStripe\Blog\Model\BlogTag.CatTag,=>SilverStripe\Blog\Model\BlogTag.KiwiTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
TaggedPost4:
|
||||
Title: 'Tagged Post 4'
|
||||
URLSegment: tagged-post-4
|
||||
PublishDate: '2012-04-09 15:00:00'
|
||||
Tags: =>BlogTag.PopularTag
|
||||
Categories: =>BlogCategory.FirstCategory
|
||||
Parent: =>Blog.FourthBlog
|
||||
Tags: =>SilverStripe\Blog\Model\BlogTag.PopularTag
|
||||
Categories: =>SilverStripe\Blog\Model\BlogCategory.FirstCategory
|
||||
Parent: =>SilverStripe\Blog\Model\Blog.FourthBlog
|
||||
|
Loading…
Reference in New Issue
Block a user