elofgren: NEW FEATURE: When 'Search' button is clicked show a simple search text input and button which allow for filtering the Site Tree by searching the URL, Title, Menu Title, and Content.

Also show an 'Add criteria...' drop-down with 'Page Type', 'Status', 'Description', and 'Keywords' options which allow for more fine grained filtering based on columns in the SiteTree? table. 
In addition add an 'Edited Since' option which uses a CalendarDatePicker? which allows for filtering pages based on when they were last edited. 
NOTE: Pages that have children will always be shown whether they match the filter or not, in case one of their children matches the filter. It would probably be better to switch to a flat 
display of the results. 
More info: http://www.silverstripe.com/google-summer-of-code-forum/flat/2526 
(merged from branches/gsoc)


git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@42086 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2007-09-16 15:19:17 +00:00
parent c09e518635
commit a56657fb83
6 changed files with 209 additions and 24 deletions

View File

@ -9,6 +9,11 @@
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider {
static $tree_class = "SiteTree";
static $subitem_class = "Member";
/**
* SiteTree Columns that can be filtered using the the Site Tree Search button
*/
static $site_tree_filter_options = array('ClassName' => 'Page Type', 'Status' => 'Status',
'MetaDescription' => 'Description', 'MetaKeywords' => 'Keywords');
public function init() {
parent::init();
@ -103,6 +108,59 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getSiteTreeFor("SiteTree");
}
/**
* Returns the SiteTree columns that can be filtered using the the Site Tree Search button as a DataObjectSet
*/
public function SiteTreeFilterOptions() {
$filter_options = new DataObjectSet();
foreach(self::$site_tree_filter_options as $key => $value) {
$record = array(
'Column' => $key,
'Title' => $value,
);
$filter_options->push(new ArrayData($record));
}
return $filter_options;
}
public function SiteTreeFilterDateField() {
$dateField = new CalendarDateField('SiteTreeFilterDate');
return $dateField->Field();
}
/**
* Returns a filtered Site Tree
*/
public function filterSiteTree() {
$className = 'SiteTree';
$rootID = null;
$obj = $rootID ? $this->getRecord($rootID) : singleton($className);
$obj->setMarkingFilterFunction('CMSMainMarkingFilterFunction');
$obj->markPartialTree();
if($p = $this->currentPage()) $obj->markToExpose($p);
// getChildrenAsUL is a flexible and complex way of traversing the tree
$siteTree = $obj->getChildrenAsUL("", '
"<li id=\"record-$child->ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" .
"<a href=\"" . Director::link(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" " . (($child->canEdit() || $child->canAddChildren()) ? "" : "class=\"disabled\"") . " title=\"' . _t('LeftAndMain.PAGETYPE','Page type: ') . '".$child->class."\" >" .
($child->TreeTitle()) .
"</a>"
'
,$this, true);
// Wrap the root if needs be.
if(!$rootID) {
$rootLink = $this->Link() . '0';
$siteTree = "<ul id=\"sitetree\" class=\"tree unformatted\"><li id=\"record-0\" class=\"Root nodelete\"><a href=\"$rootLink\">" .
_t('LeftAndMain.SITECONTENT',"Site Content",PR_HIGH,'Root node on left') . "</a>"
. $siteTree . "</li></ul>";
}
return $siteTree;
}
public function generateDataTreeHints() {
$classes = ClassInfo::subclassesFor( $this->stat('tree_class') );
@ -1162,5 +1220,55 @@ HTML;
return $perms;
}
}
// TODO: Find way to put this in a class
function CMSMainMarkingFilterFunction($node) {
// Expand all nodes
// $node->markingFinished();
// Don't ever hide nodes with children, because otherwise if one of their children matches the search, it wouldn't be shown.
if($node->AllChildrenIncludingDeleted()->count() > 0) {
// Open all nodes with children so it is easy to see any children that match the search.
$node->markOpened();
return true;
} else {
$failed_filter = false;
// First check for the generic search term in the URLSegment, Title, MenuTitle, & Content
if (!empty($_REQUEST['SiteTreeSearchTerm'])) {
// For childless nodes, show only those matching the filter
$filter = strtolower($_REQUEST['SiteTreeSearchTerm']);
if ( strpos( strtolower($node->URLSegment) , $filter) === false
&& strpos( strtolower($node->Title) , $filter) === false
&& strpos( strtolower($node->MenuTitle) , $filter) === false
&& strpos( strtolower($node->Content) , $filter) === false) {
$failed_filter = true;
}
}
// Check the 'Edited Since' date
if (!empty($_REQUEST['SiteTreeFilterDate'])) {
$edited_since = mktime(0, 0, 0, substr($_REQUEST['SiteTreeFilterDate'], 3, 2),
substr($_REQUEST['SiteTreeFilterDate'], 0, 2), substr($_REQUEST['SiteTreeFilterDate'], 6, 4));
if ( strtotime($node->LastEdited) < $edited_since ) {
$failed_filter = true;
}
}
// Now check if a specified Criteria attribute matches
foreach (CMSMain::$site_tree_filter_options as $key => $value)
{
if (!empty($_REQUEST[$key])) {
$parameterName = $key;
$filter = strtolower($_REQUEST[$key]);
// Show node only if the filter string exists anywere in the filter paramater (ignoring case)
if (strpos( strtolower($node->$parameterName) , $filter) === false) {
$failed_filter = true;
}
}
}
// Each filter must match or it fails
if (true == $failed_filter) {
return false;
} else {
return true;
}
}
}
?>

View File

@ -375,3 +375,7 @@ ul.tree span.a.MailType span.c, ul.tree span.a.last.MailType span.c, ul.tree spa
background-color: #B6BDD2;
text-decoration: none;
}
#left form.actionparams .SearchCriteria {
width: 45%;
float: left;
}

View File

@ -97,6 +97,53 @@ addpage.prototype = {
}
}
/**
* Search button click action
*/
search = Class.create();
search.applyTo('#search');
search.prototype = {
initialize : function() {
Observable.applyTo($(_HANDLER_FORMS.search));
},
onclick : function() {
if(treeactions.toggleSelection(this)) {
this.o2 = $(_HANDLER_FORMS[this.id]).observeMethod('Close', this.popupClosed.bind(this));
}
return false;
},
popupClosed : function() {
$(_HANDLER_FORMS.search).stopObserving(this.o2);
// Reload the site tree if it has been filtered
if ($('SiteTreeIsFiltered').value == 1) {
// Show all items in Site Tree again
new Ajax.Request( 'admin/SiteTreeAsUL' + '&ajax=1', {
onSuccess: function( response ) {
$('sitetree_ul').innerHTML = response.responseText;
Behaviour.apply();
$('SiteTreeIsFiltered').value = 0;
statusMessage('Unfiltered tree','good');
},
onFailure : function(response) {
errorMessage('Could not unfilter site tree<br />' + response.responseText);
}
});
}
}
}
/**
* Add Criteria Drop-down onchange action which allows more criteria to be shown
*/
SiteTreeFilterAddCriteria = Class.create();
SiteTreeFilterAddCriteria.applyTo('#SiteTreeFilterAddCriteria');
SiteTreeFilterAddCriteria.prototype = {
onchange : function() {
Element.show('Text' + this.value);
Element.show('Input' + this.value);
}
}
/**
* Batch Actions button click action
*/

View File

@ -12,7 +12,8 @@ SiteTreeHandlers.controller_url = 'admin';
var _HANDLER_FORMS = {
addpage : 'Form_AddPageOptionsForm',
batchactions : 'batchactionsforms'
batchactions : 'batchactionsforms',
search : 'search_options'
};
@ -460,28 +461,22 @@ ReorganiseAction.prototype = {
* Control the site tree filter
*/
SiteTreeFilterForm = Class.create();
SiteTreeFilterForm.applyTo('form#SiteTreeFilter');
SiteTreeFilterForm.applyTo('form#search_options');
SiteTreeFilterForm.prototype = {
initialize: function() {
this.valueField = $('SiteTreeFilterValue');
this.submitButton = $('SiteTreeFilterButton');
this.submitButton.onclick = this.submitclick.bind(this);
// this.submitButton.onclick = function() { alert('Click'); return false; }
},
submitclick: function() {
new Ajax.Request( this.action + '?SiteTreeFilterValue=' + encodeURIComponent( this.valueField.value ) + '&ajax=1', {
onSuccess: function( response ) {
alert(response.responseText);
$('sitetree').innerHTML = response.responseText;
Behaviour.apply( $('sitetree') );
statusMessage('Filtered tree','good');
},
onFailure: function( response ) {
errorMessage('Could not filter site tree<br />' + response.responseText);
}
});
onsubmit: function() {
$('SiteTreeSearchButton').className = 'loading';
Ajax.SubmitForm(this, null, {
onSuccess : function(response) {
$('SiteTreeIsFiltered').value = 1;
$('SiteTreeSearchButton').className = '';
$('sitetree_ul').innerHTML = response.responseText;
Behaviour.apply();
statusMessage('Filtered tree','good');
},
onFailure : function(response) {
errorMessage('Could not filter site tree<br />' + response.responseText);
}
});
return false;
}

View File

@ -2,6 +2,12 @@
global $lang;
$lang['en_US']['LeftAndMain']['PAGETYPE'] = 'Page type: ';
$lang['en_US']['LeftAndMain']['SITECONTENT'] = array(
'Site Content',
PR_HIGH,
'Root node on left'
);
$lang['en_US']['CMSMain']['CREATE'] = array(
'Create a ',
PR_MEDIUM,
@ -143,6 +149,10 @@ $lang['en_US']['CMSMain_left.ss']['ENABLEDRAGGING'] = array(
'Allow drag &amp; drop reordering',
PR_HIGH
);
$lang['en_US']['CMSMain_left.ss']['SEARCH'] = 'Search';
$lang['en_US']['CMSMain_left.ss']['SEARCHTITLE'] = 'Search through URL, Title, Menu Title, &amp; Content';
$lang['en_US']['CMSMain_left.ss']['EDITEDSINCE'] = 'Edited Since';
$lang['en_US']['CMSMain_left.ss']['ADDSEARCHCRITERIA'] = 'Add Criteria...';
$lang['en_US']['CMSMain_left.ss']['SELECTPAGESACTIONS'] = 'Select the pages that you want to change &amp; then click an action:';
$lang['en_US']['CMSMain_left.ss']['DELETECONFIRM'] = 'Delete the selected pages';
$lang['en_US']['CMSMain_left.ss']['PUBLISHCONFIRM'] = 'Publish the selected pages';

View File

@ -42,6 +42,26 @@
</form>
<% end_control %>
<form class="actionparams" style="display: none" id="search_options" action="admin/filterSiteTree">
<div>
<input type="hidden" id="SiteTreeIsFiltered" value="0" />
<input type="text" id="SiteTreeSearchTerm" name="SiteTreeSearchTerm" />
<input type="submit" id="SiteTreeSearchButton" value="<% _t('SEARCH','Search'); %>" title="<% _t('SEARCHTITLE','Search through URL, Title, Menu Title, &amp; Content'); %>" />
<div style="display:none" id="TextSiteTreeFilterDate" class="SearchCriteria"><b><% _t('EDITEDSINCE','Edited Since'); %>:</b></div>
<div style="display:none" id="InputSiteTreeFilterDate">$SiteTreeFilterDateField</div>
<% control SiteTreeFilterOptions %>
<div style="display:none" id="Text$Column" class="SearchCriteria"><b>$Title:</b></div>
<input style="display:none" id="Input$Column" name="$Column" class="SearchCriteria" />
<% end_control %>
<select id="SiteTreeFilterAddCriteria">
<option><% _t('ADDSEARCHCRITERIA','Add Criteria...'); %></option>
<option value="SiteTreeFilterDate"><% _t('EDITEDSINCE','Edited Since'); %></option>
<% control SiteTreeFilterOptions %>
<option value="$Column">$Title</option>
<% end_control %>
</select>
</div>
</form>
<div id="batchactionsforms" style="display: none">
<form class="actionparams" style="border:0" id="deletepage_options" action="admin/deleteitems">
<p><% _t('SELECTPAGESACTIONS','Select the pages that you want to change &amp; then click an action:'); %></p>
@ -74,8 +94,9 @@
<span style="cursor: help" title="<% _t('EDITEDNOTPUB','Edited on the draft site and not published yet'); %>" class="modified"><% _t('CHANGED','changed'); %></span>
</div>
$SiteTreeAsUL
<div id="sitetree_ul">
$SiteTreeAsUL
</div>
</div>
<!--<div id="search_holder" style="display:none">
<h2>Search</h2>