From 6a0f21ca052747fe2a41a038e5c2f0de1b46657a Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 4 Aug 2009 03:09:26 +0000 Subject: [PATCH] ENHANCEMENT sitetree filters now show up in a dropdown, and you can add your own by extending CMSSiteTreeFilter git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@83674 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- code/CMSMain.php | 55 +++++++----- code/CMSSiteTreeFilter.php | 136 +++++++++++++++++++++++++++++ code/LeftAndMain.php | 1 + javascript/CMSMain_left.js | 56 +++--------- templates/Includes/CMSMain_left.ss | 15 ++-- 5 files changed, 191 insertions(+), 72 deletions(-) create mode 100644 code/CMSSiteTreeFilter.php diff --git a/code/CMSMain.php b/code/CMSMain.php index 4a0ee603..abea0e60 100644 --- a/code/CMSMain.php +++ b/code/CMSMain.php @@ -57,6 +57,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr 'SiteTreeAsUL', 'getshowdeletedsubtree', 'getfilteredsubtree', + 'getawesomesubtree', 'batchactions' ); @@ -152,31 +153,43 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $this->getSiteTreeFor("SiteTree"); } - + /** - * Get a subtree underneath the request param 'ID', of the tree that includes deleted pages. - * If ID = 0, then get the whole tree. + * Use a CMSSiteTreeFilter to only get certain nodes + * + * @return string */ - public function getshowdeletedsubtree() { - // Get the tree - $tree = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID'], "AllHistoricalChildren"); - - // Trim off the outer tag - $tree = ereg_replace('^[ \t\r\n]*]*>','', $tree); - $tree = ereg_replace(']*>[ \t\r\n]*$','', $tree); - - return $tree; + public function getfilteredsubtree() { + // Sanity and security checks + if (!isset($_REQUEST['filter'])) die('No filter passed'); + if (!ClassInfo::exists($_REQUEST['filter'])) die ('That filter class does not exist'); + if (!is_subclass_of($_REQUEST['filter'], 'CMSSiteTreeFilter')) die ('That is not a valid filter'); + + // Do eeet! + $filter = new $_REQUEST['filter'](); + return $filter->getTree(); } - public function getfilteredsubtree() { - // Get the tree - $tree = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID'], null, array(new CMSMainMarkingFilter(), 'mark')); - - // Trim off the outer tag - $tree = ereg_replace('^[ \t\r\n]*]*>','', $tree); - $tree = ereg_replace(']*>[ \t\r\n]*$','', $tree); - - return $tree; + /** + * Returns a list of batch actions + */ + function SiteTreeFilters() { + $filters = ClassInfo::subclassesFor('CMSSiteTreeFilter'); + array_shift($filters); + $doSet = new DataObjectSet(); + $doSet->push(new ArrayData(array( + 'ClassName' => 'all', + 'Title' => 'All items' + ))); + foreach($filters as $filter) { + if (call_user_func(array($filter, 'showInList'))) { + $doSet->push(new ArrayData(array( + 'ClassName' => $filter, + 'Title' => call_user_func(array($filter, 'title')) + ))); + } + } + return $doSet; } /** diff --git a/code/CMSSiteTreeFilter.php b/code/CMSSiteTreeFilter.php new file mode 100644 index 00000000..e0a858b2 --- /dev/null +++ b/code/CMSSiteTreeFilter.php @@ -0,0 +1,136 @@ +getSiteTreeFor('SiteTree', isset($_REQUEST['ID']) ? $_REQUEST['ID'] : 0, "AllHistoricalChildren"); + + // Trim off the outer tag + $tree = ereg_replace('^[ \t\r\n]*]*>','', $tree); + $tree = ereg_replace(']*>[ \t\r\n]*$','', $tree); + + return $tree; + } +} + +class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter { + static function title() { + return "Changed pages"; + } + + function getTree() { + $search = new CMSSitetreeFilter_Search(); + $search->data = array('Status' => 'Saved'); + return $search->getTree(); + } +} + +class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter { + protected $ids = null; + protected $expanded = array(); + public $data; + + + function __construct() { + $this->data = $_REQUEST; + } + + static function showInList() { return false; } + + static function title() { + return "Search"; + } + + function populateIds($data) { + $this->ids = array(); + $this->expanded = array(); + + $where = array(); + + // Match against URLSegment, Title, MenuTitle & Content + if (isset($data['SiteTreeSearchTerm'])) { + $term = Convert::raw2sql($data['SiteTreeSearchTerm']); + $where[] = "\"URLSegment\" LIKE '%$term%' OR \"Title\" LIKE '%$term%' OR \"MenuTitle\" LIKE '%$term%' OR \"Content\" LIKE '%$term%'"; + } + + // Match against date + if (isset($data['SiteTreeFilterDate'])) { + $date = $data['SiteTreeFilterDate']; + $date = ((int)substr($date,6,4)) . '-' . ((int)substr($date,3,2)) . '-' . ((int)substr($date,0,2)); + $where[] = "\"LastEdited\" > '$date'"; + } + + // Match against exact ClassName + if (isset($data['ClassName']) && $data['ClassName'] != 'All') { + $klass = Convert::raw2sql($data['ClassName']); + $where[] = "\"ClassName\" = '$klass'"; + } + + // Partial string match against a variety of fields + foreach (CMSMain::T_SiteTreeFilterOptions() as $key => $value) { + if (!empty($data[$key])) { + $match = Convert::raw2sql($data[$key]); + $where[] = "\"$key\" LIKE '%$match%'"; + } + } + + $where = empty($where) ? '' : 'WHERE (' . implode(') AND (',$where) . ')'; + + $parents = array(); + + /* Do the actual search */ + $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" '.$where); + if (!$res) return; + + /* And keep a record of parents we don't need to get parents of themselves, as well as IDs to mark */ + foreach($res as $row) { + if ($row['ParentID']) $parents[$row['ParentID']] = true; + $this->ids[$row['ID']] = true; + } + + /* We need to recurse up the tree, finding ParentIDs for each ID until we run out of parents */ + while (!empty($parents)) { + $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "ID" in ('.implode(',',array_keys($parents)).')'); + $parents = array(); + + foreach($res as $row) { + if ($row['ParentID']) $parents[$row['ParentID']] = true; + $this->ids[$row['ID']] = true; + $this->expanded[$row['ID']] = true; + } + } + } + + public function includeInTree($page) { + if ($this->ids === null) $this->populateIds($this->data); + return isset($this->ids[$page->ID]) && $this->ids[$page->ID] ? true : false; + } + + function getTree() { + $leftAndMain = new LeftAndMain(); + $tree = $leftAndMain->getSiteTreeFor('SiteTree', isset($_REQUEST['ID']) ? $_REQUEST['ID'] : 0, null, array($this, 'includeInTree')); + + // Trim off the outer tag + $tree = ereg_replace('^[ \t\r\n]*]*>','', $tree); + $tree = ereg_replace(']*>[ \t\r\n]*$','', $tree); + + return $tree; + } +} diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index 33b30ecd..ca2e5ef4 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -286,6 +286,7 @@ class LeftAndMain extends Controller { 'cms/javascript/Upload.js', 'sapphire/javascript/TreeSelectorField.js', 'cms/javascript/ThumbnailStripField.js', + 'cms/javascript/FilterSiteTree.js', ) ); diff --git a/javascript/CMSMain_left.js b/javascript/CMSMain_left.js index 4751888c..f6cafe68 100755 --- a/javascript/CMSMain_left.js +++ b/javascript/CMSMain_left.js @@ -118,18 +118,16 @@ searchclass.prototype = { } } -/** - * Show deleted pages checkbox - */ -ShowDeletedPagesAction = Class.create(); -ShowDeletedPagesAction.applyTo('#showdeletedpages'); -ShowDeletedPagesAction.prototype = { +SiteTreeFilter = Class.create(); +SiteTreeFilter.applyTo('#siteTreeFilterList'); +SiteTreeFilter.prototype = { initialize: function () { }, - - onclick : function() { - if(this.checked) { - $('sitetree').setCustomURL(SiteTreeHandlers.controller_url+'/getshowdeletedsubtree'); + onchange : function() { + var value = this.options[this.selectedIndex].value; + + if(value != 'all') { + $('sitetree').setCustomURL(SiteTreeHandlers.controller_url+'/getfilteredsubtree?filter='+escape(value)); } else { $('sitetree').clearCustomURL(); } @@ -140,14 +138,14 @@ ShowDeletedPagesAction.prototype = { $('sitetree').stopBeingDraggable(); __makeDraggableAfterUpdate = true; } - - var indicator = $('checkboxActionIndicator'); - indicator.style.display = 'block'; - + + var indicator = $('siteTreeFilterActionIndicator'); + indicator.style.display = 'inline'; + $('sitetree').reload({ onSuccess: function() { - if(__makeDraggableAfterUpdate) $('sitetree').makeDraggable(); indicator.style.display = 'none'; + if(__makeDraggableAfterUpdate) $('sitetree').makeDraggable(); }, onFailure: function(response) { errorMessage('Could not update tree', response); @@ -155,34 +153,6 @@ ShowDeletedPagesAction.prototype = { }); } } - -/** - * Show only drafts checkbox click action - */ -showonlydrafts = Class.create(); -showonlydrafts.applyTo('#publishpage_show_drafts'); -showonlydrafts.prototype = { - onclick : function() { - if(this.checked) { - $('sitetree').setCustomURL(SiteTreeHandlers.controller_url+'/getfilteredsubtree', {Status:'Saved'}); - } else { - $('sitetree').clearCustomURL(); - } - - $('sitetree').reload({ - onSuccess: function() { - statusMessage(ss.i18n._t('CMSMAIN.FILTEREDTREE'),'good'); - }, - onFailure: function(response) { - errorMessage(ss.i18n.sprintf( - ss.i18n._t('CMSMAIN.ERRORFILTERPAGES'), - response.responseText - )); - } - }); - } -} - /** * Control the site tree filter */ diff --git a/templates/Includes/CMSMain_left.ss b/templates/Includes/CMSMain_left.ss index 7a4d7c0a..3cf55fba 100755 --- a/templates/Includes/CMSMain_left.ss +++ b/templates/Includes/CMSMain_left.ss @@ -81,20 +81,19 @@ +
+ Show: +
-
- - -
-
- - -
<% if IsTranslatableEnabled %>