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
This commit is contained in:
Tom Rix 2009-08-04 03:09:26 +00:00
parent 07a61a19a5
commit 6a0f21ca05
5 changed files with 191 additions and 72 deletions

View File

@ -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]*<ul[^>]*>','', $tree);
$tree = ereg_replace('</ul[^>]*>[ \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]*<ul[^>]*>','', $tree);
$tree = ereg_replace('</ul[^>]*>[ \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;
}
/**

136
code/CMSSiteTreeFilter.php Normal file
View File

@ -0,0 +1,136 @@
<?php
/**
* Base class for filtering the subtree for certain node statuses
* @package cms
* @subpackage content
*/
abstract class CMSSiteTreeFilter extends Object {
abstract function getTree();
static abstract function title();
static function showInList() {
return true;
}
}
class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter {
static function title() {
return "Deleted pages";
}
function getTree() {
$leftAndMain = new LeftAndMain();
$tree = $leftAndMain->getSiteTreeFor('SiteTree', isset($_REQUEST['ID']) ? $_REQUEST['ID'] : 0, "AllHistoricalChildren");
// Trim off the outer tag
$tree = ereg_replace('^[ \t\r\n]*<ul[^>]*>','', $tree);
$tree = ereg_replace('</ul[^>]*>[ \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]*<ul[^>]*>','', $tree);
$tree = ereg_replace('</ul[^>]*>[ \t\r\n]*$','', $tree);
return $tree;
}
}

View File

@ -286,6 +286,7 @@ class LeftAndMain extends Controller {
'cms/javascript/Upload.js',
'sapphire/javascript/TreeSelectorField.js',
'cms/javascript/ThumbnailStripField.js',
'cms/javascript/FilterSiteTree.js',
)
);

View File

@ -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
*/

View File

@ -81,20 +81,19 @@
</div>
</form>
</div>
<div class="checkboxAboveTree" style="border-bottom:none">
Show: <select id="siteTreeFilterList">
<% control SiteTreeFilters %>
<option value="$ClassName">$Title</option>
<% end_control %>
</select> <img id="siteTreeFilterActionIndicator" style="display:none" src="cms/images/network-save.gif">
</div>
<div class="checkboxAboveTree">
<img id="checkboxActionIndicator" src="cms/images/network-save.gif">
<div>
<input type="checkbox" id="sortitems" />
<label for="sortitems"><% _t('ENABLEDRAGGING','Allow drag &amp; drop reordering', PR_HIGH) %></label>
</div>
<div>
<input type="checkbox" id="publishpage_show_drafts" />
<label for="publishpage_show_drafts"><% _t('SHOWONLYCHANGED','Show only changed pages') %></label>
</div>
<div>
<input type="checkbox" id="showdeletedpages" />
<label for="showdeletedpages"><% _t('SHOW_DELETED_PAGES','Show deleted pages', PR_HIGH) %></label>
</div>
</div>
<% if IsTranslatableEnabled %>