From 5d9ed5b9e051fe4cdfc5896e53bb8833e080e206 Mon Sep 17 00:00:00 2001 From: Christopher Pitt Date: Tue, 21 Apr 2015 16:47:46 +1200 Subject: [PATCH] Added merging of tags and categories --- code/admin/GridFieldCategorisationConfig.php | 51 +++++++ code/admin/GridFieldFormAction.php | 28 ++++ code/admin/GridFieldMergeAction.php | 140 +++++++++++++++++++ code/model/Blog.php | 113 +++++++-------- css/cms.css | 32 ++--- js/merge-action.js | 44 ++++++ scss/cms.scss | 16 ++- 7 files changed, 333 insertions(+), 91 deletions(-) create mode 100644 code/admin/GridFieldCategorisationConfig.php create mode 100644 code/admin/GridFieldFormAction.php create mode 100644 code/admin/GridFieldMergeAction.php create mode 100644 js/merge-action.js diff --git a/code/admin/GridFieldCategorisationConfig.php b/code/admin/GridFieldCategorisationConfig.php new file mode 100644 index 0000000..d629228 --- /dev/null +++ b/code/admin/GridFieldCategorisationConfig.php @@ -0,0 +1,51 @@ +removeComponentsByType("GridFieldAddNewButton"); + + $this->addComponent( + new GridFieldAddByDBField("buttons-before-left") + ); + + $this->addComponent( + new GridFieldMergeAction($mergeRecords, $parentType, $parentMethod, $childMethod) + ); + + $columns = $this->getComponentByType('GridFieldDataColumns'); + + $columns->setFieldFormatting(array( + 'BlogPostsCount' => function($value, &$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', + )); + } +} \ No newline at end of file diff --git a/code/admin/GridFieldFormAction.php b/code/admin/GridFieldFormAction.php new file mode 100644 index 0000000..1020fb3 --- /dev/null +++ b/code/admin/GridFieldFormAction.php @@ -0,0 +1,28 @@ +extraAttributes = $attributes; + } + + /** + * @return array + */ + public function getAttributes() + { + $attributes = parent::getAttributes(); + + return array_merge( + $attributes, + $this->extraAttributes + ); + } +} \ No newline at end of file diff --git a/code/admin/GridFieldMergeAction.php b/code/admin/GridFieldMergeAction.php new file mode 100644 index 0000000..2c25153 --- /dev/null +++ b/code/admin/GridFieldMergeAction.php @@ -0,0 +1,140 @@ +records = $records; + $this->parentType = $parentType; + $this->parentMethod = $parentMethod; + $this->childMethod = $childMethod; + } + + /** + * {@inheritdoc} + */ + public function augmentColumns($gridField, &$columns) { + if(!in_array('MergeAction', $columns)) { + $columns[] = 'MergeAction'; + } + + return $columns; + } + + /** + * {@inheritdoc} + */ + public function getColumnsHandled($gridField) { + return array('MergeAction'); + } + + /** + * {@inheritdoc} + */ + public function getColumnContent($gridField, $record, $columnName) { + if($columnName === 'MergeAction') { + $dropdown = new DropdownField('Target', 'Target', $this->records->map()); + + $prefix = strtolower($this->parentMethod . '-' . $this->childMethod); + + $action = GridFieldFormAction::create( + $gridField, + 'MergeAction' . $record->ID, + 'Move', + 'merge', + array( + 'record' => $record->ID, + 'target' => $prefix . '-target-record-' . $record->ID, + ) + ); + + $action->setExtraAttributes(array( + 'data-target' => $prefix . '-target-record-' . $record->ID + )); + + return $dropdown->Field() . $action->Field() . 'move posts to'; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getColumnAttributes($gridField, $record, $columnName) { + return array('class' => 'MergeAction'); + } + + /** + * {@inheritdoc} + */ + public function getColumnMetadata($gridField, $columnName) { + return array('title' => 'Move Posts To'); + } + + /** + * {@inheritdoc} + */ + public function getActions($gridField) { + return array('merge'); + } + + /** + * {@inheritdoc} + */ + public function handleAction(GridField $gridField, $actionName, $arguments, $data) { + if($actionName === 'merge') { + $controller = Controller::curr(); + + $request = $controller->getRequest(); + + $target = $request->requestVar($arguments["target"]); + + $parentType = $this->parentType; + + $fromParent = $parentType::get()->byId($arguments['record']); + $toParent = $parentType::get()->byId($target); + + $posts = $fromParent->{$this->childMethod}(); + + foreach ($posts as $post) { + $relationship = $post->{$this->parentMethod}(); + + $relationship->remove($fromParent); + $relationship->add($toParent); + } + } + } +} \ No newline at end of file diff --git a/code/model/Blog.php b/code/model/Blog.php index b3bb9dc..42023f8 100644 --- a/code/model/Blog.php +++ b/code/model/Blog.php @@ -5,7 +5,7 @@ * * @package silverstripe * @subpackage blog - * + * * @method HasManyList Tags() List of tags in this blog * @method HasManyList Categories() List of categories in this blog * @method ManyManyList Editors() List of editors @@ -13,7 +13,7 @@ * @method ManyManyList Contributors() List of contributors * * @author Michael Strong -**/ + **/ class Blog extends Page implements PermissionProvider { /** @@ -63,7 +63,7 @@ class Blog extends Page implements PermissionProvider { 'Writers' => 'Member', 'Contributors' => 'Member', ); - + private static $allowed_children = array( "BlogPost", ); @@ -80,41 +80,38 @@ class Blog extends Page implements PermissionProvider { public function getCMSFields() { Requirements::css(BLOGGER_DIR . '/css/cms.css'); - Requirements::javascript(BLOGGER_DIR . '/js/expandable-help-text.js'); + Requirements::javascript(BLOGGER_DIR . '/js/merge-action.js'); $self =& $this; - $this->beforeUpdateCMSFields(function($fields) use ($self) { - // Don't show this tab if edit is not allowed - if(!$self->canEdit()) return; - - // Create categories and tag config - $config = GridFieldConfig_RecordEditor::create(); - $config->removeComponentsByType("GridFieldAddNewButton"); - $config->addComponent(new GridFieldAddByDBField("buttons-before-left")); + $this->beforeUpdateCMSFields(function ($fields) use ($self) { + if(!$self->canEdit()) { + return; + } $categories = GridField::create( "Categories", _t("Blog.Categories", "Categories"), $self->Categories(), - $config + GridFieldCategorisationConfig::create(15, $self->Categories(), 'BlogCategory', 'Categories', 'BlogPosts') ); $tags = GridField::create( "Tags", _t("Blog.Tags", "Tags"), $self->Tags(), - $config + GridFieldCategorisationConfig::create(15, $self->Categories(), 'BlogTag', 'Tags', 'BlogPosts') ); - $fields->addFieldsToTab("Root.BlogOptions", array( + $fields->addFieldsToTab("Root.Categorisation", array( $categories, $tags )); }); - + $fields = parent::getCMSFields(); + return $fields; } @@ -122,6 +119,7 @@ class Blog extends Page implements PermissionProvider { * Check if this member is an editor of the blog * * @param Member $member + * * @return boolean */ public function isEditor($member) { @@ -134,6 +132,7 @@ class Blog extends Page implements PermissionProvider { * Check if this member is a writer of the blog * * @param Member $member + * * @return boolean */ public function isWriter($member) { @@ -146,6 +145,7 @@ class Blog extends Page implements PermissionProvider { * Check if this member is a contributor of the blog * * @param Member $member + * * @return boolean */ public function isContributor($member) { @@ -161,6 +161,7 @@ class Blog extends Page implements PermissionProvider { * E.g. `Hello $RoleOf($CurrentMember.ID)` * * @param Member|integer $member + * * @return string|null Author, Editor, Writer, Contributor, or null if no role */ public function RoleOf($member) { @@ -178,6 +179,7 @@ class Blog extends Page implements PermissionProvider { * * @param Member $member * @param DataList $list Relation to check + * * @return boolean */ protected function isMemberOf($member, $list) { @@ -191,11 +193,10 @@ class Blog extends Page implements PermissionProvider { } - public function canEdit($member = null) { $member = $member ?: Member::currentUser(); if(is_numeric($member)) $member = Member::get()->byID($member); - + // Allow editors to edit this page if($this->isEditor($member)) return true; @@ -206,6 +207,7 @@ class Blog extends Page implements PermissionProvider { * Determine if this user can edit the editors list * * @param Member $member + * * @return boolean */ public function canEditEditors($member = null) { @@ -222,6 +224,7 @@ class Blog extends Page implements PermissionProvider { * Determine if this user can edit writers list * * @param Member $member + * * @return boolean */ public function canEditWriters($member = null) { @@ -242,12 +245,13 @@ class Blog extends Page implements PermissionProvider { * Determines if this user can edit the contributors list * * @param type $member + * * @return boolean */ public function canEditContributors($member = null) { $member = $member ?: Member::currentUser(); if(is_numeric($member)) $member = Member::get()->byID($member); - + $extended = $this->extendedCan('canEditContributors', $member); if($extended !== null) return $extended; @@ -273,7 +277,7 @@ class Blog extends Page implements PermissionProvider { public function getSettingsFields() { $fields = parent::getSettingsFields(); - $fields->addFieldToTab("Root.Settings", + $fields->addFieldToTab("Root.Settings", NumericField::create("PostsPerPage", _t("Blog.PostsPerPage", "Posts Per Page")) ); @@ -328,11 +332,11 @@ class Blog extends Page implements PermissionProvider {
Update any BlogPost they have authored or have been assigned to '); - + if(!$this->canEditContributors()) { $contributorField = $contributorField->performDisabledTransformation(); } - + $fields->addFieldsToTab('Root.Users', array( $editorField, $writerField, @@ -347,7 +351,7 @@ class Blog extends Page implements PermissionProvider { * Return blog posts * * @return DataList of BlogPost objects - **/ + **/ public function getBlogPosts() { $blogPosts = BlogPost::get()->filter("ParentID", $this->ID); //Allow decorators to manipulate list @@ -356,7 +360,6 @@ class Blog extends Page implements PermissionProvider { } - /** * Returns blogs posts for a given date period. * @@ -365,7 +368,7 @@ class Blog extends Page implements PermissionProvider { * @param $day int * * @return DataList - **/ + **/ public function getArchivedBlogPosts($year, $month = null, $day = null) { $query = $this->getBlogPosts()->dataQuery(); @@ -387,8 +390,9 @@ class Blog extends Page implements PermissionProvider { /** * Get a link to a Member profile. - * + * * @param urlSegment + * * @return String */ public function ProfileLink($urlSegment) { @@ -406,7 +410,6 @@ class Blog extends Page implements PermissionProvider { } - /** * This overwrites lumberjacks default gridfield config. * @@ -499,7 +502,6 @@ class Blog extends Page implements PermissionProvider { } - /** * Blog Controller * @@ -507,7 +509,7 @@ class Blog extends Page implements PermissionProvider { * @subpackage blog * * @author Michael Strong -**/ + **/ class Blog_Controller extends Page_Controller { private static $allowed_actions = array( @@ -531,15 +533,14 @@ class Blog_Controller extends Page_Controller { ); - /** + /** * The current Blog Post DataList query. * * @var DataList - **/ + **/ protected $blogPosts; - public function index() { $this->blogPosts = $this->getBlogPosts(); return $this->render(); @@ -549,7 +550,7 @@ class Blog_Controller extends Page_Controller { * Renders a Blog Member's profile. * * @return SS_HTTPResponse - **/ + **/ public function profile() { $profile = $this->getCurrentProfile(); @@ -564,9 +565,9 @@ class Blog_Controller extends Page_Controller { /** * Get the Member associated with the current URL segment. - * + * * @return Member|null - **/ + **/ public function getCurrentProfile() { $urlSegment = $this->request->param('URLSegment'); @@ -581,9 +582,9 @@ class Blog_Controller extends Page_Controller { /** * Get posts related to the current Member profile - * + * * @return DataList|null - **/ + **/ public function getCurrentProfilePosts() { $profile = $this->getCurrentProfile(); @@ -598,7 +599,7 @@ class Blog_Controller extends Page_Controller { * Renders an archive for a specificed date. This can be by year or year/month * * @return SS_HTTPResponse - **/ + **/ public function archive() { $year = $this->getArchiveYear(); $month = $this->getArchiveMonth(); @@ -622,12 +623,11 @@ class Blog_Controller extends Page_Controller { } - /** * Renders the blog posts for a given tag. * * @return SS_HTTPResponse - **/ + **/ public function tag() { $tag = $this->getCurrentTag(); if($tag) { @@ -638,12 +638,11 @@ class Blog_Controller extends Page_Controller { } - /** * Renders the blog posts for a given category * * @return SS_HTTPResponse - **/ + **/ public function category() { $category = $this->getCurrentCategory(); if($category) { @@ -664,7 +663,7 @@ class Blog_Controller extends Page_Controller { if($filter) { $title = "{$title} - {$filter}"; } - + $this->extend('updateMetaTitle', $title); return $title; } @@ -725,7 +724,7 @@ class Blog_Controller extends Page_Controller { if($this->owner->getArchiveYear()) { if($this->owner->getArchiveDay()) { $date = $this->owner->getArchiveDate()->Nice(); - } elseif($this->owner->getArchiveMonth ()) { + } elseif($this->owner->getArchiveMonth()) { $date = $this->owner->getArchiveDate()->format("F, Y"); } else { $date = $this->owner->getArchiveDate()->format("Y"); @@ -751,7 +750,7 @@ class Blog_Controller extends Page_Controller { * Displays an RSS feed of blog posts * * @return string HTML - **/ + **/ public function rss() { $rss = new RSSFeed($this->getBlogPosts(), $this->Link(), $this->MetaTitle, $this->MetaDescription); $this->extend('updateRss', $rss); @@ -759,12 +758,11 @@ class Blog_Controller extends Page_Controller { } - /** * Returns a list of paginated blog posts based on the blogPost dataList * * @return PaginatedList - **/ + **/ public function PaginatedList() { $posts = new PaginatedList($this->blogPosts); @@ -783,12 +781,11 @@ class Blog_Controller extends Page_Controller { } - /** * Tag Getter for use in templates. * * @return BlogTag|null - **/ + **/ public function getCurrentTag() { $tag = $this->request->param("Tag"); if($tag) { @@ -800,12 +797,11 @@ class Blog_Controller extends Page_Controller { } - /** * Category Getter for use in templates. * * @return BlogCategory|null - **/ + **/ public function getCurrentCategory() { $category = $this->request->param("Category"); if($category) { @@ -817,12 +813,11 @@ class Blog_Controller extends Page_Controller { } - /** * Fetches the archive year from the url * * @return int|null - **/ + **/ public function getArchiveYear() { $year = $this->request->param("Year"); if(preg_match("/^[0-9]{4}$/", $year)) { @@ -832,12 +827,11 @@ class Blog_Controller extends Page_Controller { } - /** * Fetches the archive money from the url. * * @return int|null - **/ + **/ public function getArchiveMonth() { $month = $this->request->param("Month"); if(preg_match("/^[0-9]{1,2}$/", $month)) { @@ -852,12 +846,11 @@ class Blog_Controller extends Page_Controller { } - /** * Fetches the archive day from the url * * @return int|null - **/ + **/ public function getArchiveDay() { $day = $this->request->param("Day"); if(preg_match("/^[0-9]{1,2}$/", $day)) { @@ -871,12 +864,11 @@ class Blog_Controller extends Page_Controller { } - /** * Returns the current archive date. * * @return Date - **/ + **/ public function getArchiveDate() { $year = $this->getArchiveYear(); $month = $this->getArchiveMonth(); @@ -896,12 +888,11 @@ class Blog_Controller extends Page_Controller { } - /** * Returns a link to the RSS feed. * * @return string URL - **/ + **/ public function getRSSLink() { return $this->Link("rss"); } diff --git a/css/cms.css b/css/cms.css index 4bbf658..bd6d6b7 100755 --- a/css/cms.css +++ b/css/cms.css @@ -7,24 +7,20 @@ /* * Sprite maps & Icons */ -/* line 48, blog-icon/*.png */ .blog-icon-sprite, .gridfield-icon .blog-icon-timer { background-image: url('../images/blog-icon-s0a5ab5f851.png'); background-repeat: no-repeat; } -/* line 84, ../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/utilities/sprites/_base.scss */ .gridfield-icon .blog-icon-timer { background-position: 0 0; } -/* line 20, ../scss/cms.scss */ #FeaturedImage .middleColumn { clear: none; float: left; } -/* line 25, ../scss/cms.scss */ .blog-admin-sidebar { width: 280px; border-right: none; @@ -34,11 +30,9 @@ bottom: 0px; height: 100%; } -/* line 34, ../scss/cms.scss */ .blog-admin-sidebar .cms-panel-toggle a { text-align: left; } -/* line 38, ../scss/cms.scss */ .blog-admin-sidebar ~ .blog-admin-outer { width: 100%; padding-right: 280px; @@ -48,59 +42,47 @@ overflow-x: hidden; box-sizing: border-box; } -/* line 47, ../scss/cms.scss */ .blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset { position: relative; overflow: auto; height: 100%; width: 100%; } -/* line 54, ../scss/cms.scss */ .blog-admin-sidebar ~ .blog-admin-outer > .ss-tabset #Title label { float: none; } -/* line 57, ../scss/cms.scss */ .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; } -/* line 68, ../scss/cms.scss */ .blog-admin-sidebar .cms-content-view > .field + .field { margin-top: 10px; } -/* line 73, ../scss/cms.scss */ .blog-admin-sidebar .cms-content-view > .field.urlsegment .preview { padding-top: 0; line-height: 25px; } -/* line 78, ../scss/cms.scss */ .blog-admin-sidebar .cms-content-view > .field.urlsegment .edit { float: right; } -/* line 85, ../scss/cms.scss */ .blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .date { width: 60%; } -/* line 89, ../scss/cms.scss */ .blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn > .time { width: 36%; float: right; } -/* line 94, ../scss/cms.scss */ .blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn .middleColumn, .blog-admin-sidebar .cms-content-view > .field.datetime > .middleColumn input { width: 100%; } -/* line 103, ../scss/cms.scss */ .blog-admin-sidebar.collapsed ~ .blog-admin-outer { padding-right: 41px; } -/* line 109, ../scss/cms.scss */ .blog-admin-sidebar.cms-content-tools .cms-panel-content { width: auto; } -/* line 115, ../scss/cms.scss */ .toggle-description { text-indent: -1000000px; display: inline-block; @@ -110,14 +92,12 @@ margin-left: 4px; } -/* line 124, ../scss/cms.scss */ .middleColumn.toggle-description-correct-middle { margin-left: 0; float: left; width: 416px; } -/* line 130, ../scss/cms.scss */ label.right.toggle-description-correct-right { display: inline-block; margin-left: 0; @@ -125,22 +105,26 @@ label.right.toggle-description-correct-right { float: left; } -/* line 137, ../scss/cms.scss */ .description.toggle-description-correct-description { width: 416px; padding: 12px 0; } -/* line 144, ../scss/cms.scss */ .custom-summary .ui-accordion-content .field { margin: 0; } -/* line 148, ../scss/cms.scss */ .custom-summary .ui-accordion-content, .custom-summary .ui-accordion-content .field { padding: 0; } -/* line 153, ../scss/cms.scss */ .custom-summary .ui-icon-triangle-1-e { background-position: -16px -128px; } + +.cms table.ss-gridfield-table tr td.MergeAction { + width: 150px; +} + +.cms table.ss-gridfield-table tr td.MergeAction select { + width: 75px; +} diff --git a/js/merge-action.js b/js/merge-action.js new file mode 100644 index 0000000..74530b8 --- /dev/null +++ b/js/merge-action.js @@ -0,0 +1,44 @@ +/** + * Register expandable help text functions with fields. + */ +(function ($) { + + $.entwine('ss', function ($) { + + $('.MergeAction').entwine({ + 'onadd': function() { + var $this = $(this); + + $this.on('click', 'select', function() { + return false; + }); + + $this.children('button').each(function(i, button) { + var $button = $(button); + var $select = $button.prev('select'); + + $button.before(''); + }); + + $this.on('change', 'select', function(e) { + var $target = $(e.target); + + $target.next('input').val($target.val()); + }); + + $this.children('button, select').hide(); + + $this.on('click', '.MergeActionReveal', function(e) { + var $target = $(e.target); + + $target.parent().children('button, select').show(); + $target.hide(); + + return false; + }); + } + }) + + }); + +}(jQuery)); diff --git a/scss/cms.scss b/scss/cms.scss index a6840a1..d9159ec 100755 --- a/scss/cms.scss +++ b/scss/cms.scss @@ -1,12 +1,10 @@ /** * CMS Styles */ - /** * Include Compass framework */ @import "compass"; - /* * Sprite maps & Icons */ @@ -56,7 +54,7 @@ } .middleColumn, input { width: 100%; - max-width:100%; + max-width: 100%; margin-left: 0; } } @@ -88,7 +86,7 @@ > .time { width: 36%; - float:right; + float: right; } .middleColumn, input { @@ -140,7 +138,6 @@ label.right.toggle-description-correct-right { } .custom-summary { - .ui-accordion-content .field { margin: 0; } @@ -153,5 +150,12 @@ label.right.toggle-description-correct-right { .ui-icon-triangle-1-e { background-position: -16px -128px; } +} -} \ No newline at end of file +.cms table.ss-gridfield-table tr td.MergeAction { + width: 150px; +} + +.cms table.ss-gridfield-table tr td.MergeAction select { + width: 75px; +}