diff --git a/code/model/BlogCategory.php b/code/model/BlogCategory.php index b9186ac..c4ea804 100644 --- a/code/model/BlogCategory.php +++ b/code/model/BlogCategory.php @@ -8,11 +8,13 @@ * * @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 @@ -21,13 +23,14 @@ class BlogCategory extends DataObject implements CategorisationObject * @const string * This must be a string because ValidationException has decided we can't use int */ - const DUPLICATE_EXCEPTION = "DUPLICATE"; + const DUPLICATE_EXCEPTION = 'DUPLICATE'; /** * @var array */ private static $db = array( 'Title' => 'Varchar(255)', + 'URLSegment' => 'Varchar(255)', ); /** @@ -45,140 +48,19 @@ class BlogCategory extends DataObject implements CategorisationObject ); /** - * @var array + * {@inheritdoc} */ - private static $extensions = array( - 'URLSegmentExtension', - ); - - /** - * @return DataList - */ - public function BlogPosts() + protected function getListUrlSegment() { - $blogPosts = parent::BlogPosts(); - - $this->extend("updateGetBlogPosts", $blogPosts); - - return $blogPosts; + return 'categories'; } /** * {@inheritdoc} */ - public function getCMSFields() + protected function getDuplicateError() { - $fields = new FieldList( - TextField::create('Title', _t('BlogCategory.Title', 'Title')) - ); - - $this->extend('updateCMSFields', $fields); - - return $fields; + return _t('BlogCategory.Duplicate', 'A blog category already exists with that name.'); } - /** - * {@inheritdoc} - */ - public 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); - } - - /** - * {@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()->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); - } } diff --git a/code/model/BlogTag.php b/code/model/BlogTag.php index caf744e..7a6c181 100644 --- a/code/model/BlogTag.php +++ b/code/model/BlogTag.php @@ -14,6 +14,7 @@ */ class BlogTag extends DataObject implements CategorisationObject { + use BlogObject; /** * Use an exception code so that attempted writes can continue on @@ -29,6 +30,7 @@ class BlogTag extends DataObject implements CategorisationObject */ private static $db = array( 'Title' => 'Varchar(255)', + 'URLSegment' => 'Varchar(255)', ); /** @@ -46,140 +48,18 @@ class BlogTag extends DataObject implements CategorisationObject ); /** - * @var array + * {@inheritdoc} */ - private static $extensions = array( - 'URLSegmentExtension', - ); - - /** - * @return DataList - */ - public function BlogPosts() + protected function getListUrlSegment() { - $blogPosts = parent::BlogPosts(); - - $this->extend("updateGetBlogPosts", $blogPosts); - - return $blogPosts; + return 'tags'; } /** * {@inheritdoc} */ - public function getCMSFields() + protected function getDuplicateError() { - $fields = new FieldList( - TextField::create('Title', _t('BlogTag.Title', 'Title')) - ); - - $this->extend('updateCMSFields', $fields); - - return $fields; - } - - /** - * {@inheritdoc} - */ - public 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); - } - - /** - * {@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()->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); + return _t('BlogTag.Duplicate', 'A blog tags already exists with that name.'); } } diff --git a/code/traits/BlogObject.php b/code/traits/BlogObject.php new file mode 100644 index 0000000..1d0b091 --- /dev/null +++ b/code/traits/BlogObject.php @@ -0,0 +1,221 @@ +extend('updateGetBlogPosts', $blogPosts); + + return $blogPosts; + } + + /** + * {@inheritdoc} + */ + public function getCMSFields() + { + $fields = new TabSet('Root', + new Tab('Main', + TextField::create('Title', _t(self::class . '.Title', 'Title')) + ) + ); + + $fields = FieldList::create($fields); + $this->extend('updateCMSFields', $fields); + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function validate() + { + $validation = parent::validate(); + if(!$validation->valid()) { + return $validation; + } + + $blog = $this->Blog(); + if(!$blog || !$blog->exists()) { + return $validation; + } + + if($this->getDuplicatesByUrlSegment()->count() > 0) { + $validation->error($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()->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); + } + + /** + * {@inheritdoc} + */ + protected function onBeforeWrite() + { + parent::onBeforeWrite(); + if(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 = new URLSegmentFilter(); + + $this->URLSegment = $filter->filter($this->owner->Title); + + if ($increment > 0) { + $this->URLSegment .= '-' . $increment; + } + + if ($this->getDuplicatesByUrlSegment()->count() > 0) { + $this->owner->generateURLSegment($increment+1); + } + + return $this->owner->URLSegment; + } + + /** + * Looks for objects of the same type by url segment. + * + * @return DataList + */ + protected function getDuplicatesByUrlSegment() + { + $duplicates = DataList::create(self::class)->filter(array( + 'URLSegment' => $this->URLSegment, + '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(); + +} diff --git a/tests/BlogCategoryTest.php b/tests/BlogCategoryTest.php index c29aa95..c40e976 100755 --- a/tests/BlogCategoryTest.php +++ b/tests/BlogCategoryTest.php @@ -148,10 +148,12 @@ 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(); diff --git a/tests/BlogTagTest.php b/tests/BlogTagTest.php index dcf886c..fd61cf5 100755 --- a/tests/BlogTagTest.php +++ b/tests/BlogTagTest.php @@ -162,7 +162,7 @@ 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); } @@ -174,10 +174,12 @@ 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();