diff --git a/code/CMSMain.php b/code/CMSMain.php index 83014360..c845d49c 100755 --- a/code/CMSMain.php +++ b/code/CMSMain.php @@ -133,11 +133,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $this->getSiteTreeFor($this->stat('tree_class')); } - - protected function getMarkingFilter($params) { - return new CMSMainMarkingFilter($params); - } - + public function generateDataTreeHints() { $classes = ClassInfo::subclassesFor( $this->stat('tree_class') ); @@ -1055,20 +1051,24 @@ JS; array_shift($filters); // add filters to map foreach($filters as $filter) { - if(!call_user_func(array($filter, 'showInList'))) continue; - $filterMap[$filter] = call_user_func(array($filter, 'title')); } - + // ensure that 'all pages' filter is on top position + uasort($filterMap, + create_function('$a,$b', 'return ($a == "CMSSiteTreeFilter_Search") ? 1 : -1;') + ); + + $showDefaultFields = array(); $form = new Form( $this, 'SearchTreeForm', new FieldSet( - new TextField( + // TODO i18n + $showDefaultFields[] = new DropdownField('FilterClass', 'Pages', $filterMap), + $showDefaultFields[] = new TextField( 'Title', _t('CMSMain.TITLEOPT', 'Title') ), - new DropdownField('filter', 'Type', $filterMap, null, null, 'Any'), new TextField('Content', 'Text'), new CalendarDateField('EditedSince', _t('CMSMain_left.ss.EDITEDSINCE','Edited Since')), new DropdownField('ClassName', 'Page Type', $pageTypes, null, null, 'Any'), @@ -1100,15 +1100,19 @@ JS; ) ) ); + $form->setFormMethod('GET'); + $form->disableSecurityToken(); $form->unsetValidator(); + foreach($showDefaultFields as $f) $f->addExtraClass('show-default'); + return $form; } function doSearchTree($data, $form) { return $this->getsubtree($this->request); } - + function publishall() { ini_set("memory_limit", -1); ini_set('max_execution_time', 0); @@ -1342,46 +1346,4 @@ JS; } } -class CMSMainMarkingFilter extends LeftAndMainMarkingFilter{ - - protected function getQuery($params) { - $where = array(); - - $SQL_params = Convert::raw2sql($params); - foreach($SQL_params as $name => $val) { - switch($name) { - // Match against URLSegment, Title, MenuTitle & Content - case 'SiteTreeSearchTerm': - $where[] = "\"URLSegment\" LIKE '%$val%' OR \"Title\" LIKE '%$val%' OR \"MenuTitle\" LIKE '%$val%' OR \"Content\" LIKE '%$val%'"; - break; - // Match against date - case 'SiteTreeFilterDate': - $val = ((int)substr($val,6,4)) - . '-' . ((int)substr($val,3,2)) - . '-' . ((int)substr($val,0,2)); - $where[] = "\"LastEdited\" > '$val'"; - break; - // Match against exact ClassName - case 'ClassName': - if($val && $val != 'All') { - $where[] = "\"ClassName\" = '$val'"; - } - break; - default: - // Partial string match against a variety of fields - if(!empty($val) && singleton("SiteTree")->hasDatabaseField($name)) { - $where[] = "\"$name\" LIKE '%$val%'"; - } - } - } - - return new SQLQuery( - array("ParentID", "ID"), - 'SiteTree', - $where - ); - } - -} - -?> +?> \ No newline at end of file diff --git a/code/CMSSiteTreeFilter.php b/code/CMSSiteTreeFilter.php index 353ab89a..6eff66b1 100644 --- a/code/CMSSiteTreeFilter.php +++ b/code/CMSSiteTreeFilter.php @@ -15,150 +15,208 @@ */ abstract class CMSSiteTreeFilter extends Object { - protected $ids = null; - protected $expanded = array(); + /** + * @var Array Search parameters, mostly properties on {@link SiteTree}. + * Caution: Unescaped data. + */ + protected $params = array(); - static function showInList() { - return true; - } - - function getTree() { - if(method_exists($this, 'pagesIncluded')) { - $this->populateIDs(); - } - - $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; + /** + * @var Array + */ + protected $_cache_ids = null; + + /** + * @var Array + */ + protected $_cache_expanded = array(); + + /** + * @var String + */ + protected $childrenMethod = null; + + function __construct($params = null) { + if($params) $this->params = $params; + + parent::__construct(); } /** - * Populate $this->ids with the IDs of the pages returned by pagesIncluded(), also including + * @return String Method on {@link Hierarchy} objects + * which is used to traverse into children relationships. + */ + function getChildrenMethod() { + return $this->childrenMethod; + } + + /** + * @return Array Map of Page IDs to their respective ParentID values. + */ + function pagesIncluded() {} + + /** + * Populate the IDs of the pages returned by pagesIncluded(), also including * the necessary parent helper pages. */ protected function populateIDs() { - if($res = $this->pagesIncluded()) { + $parents = array(); + $this->_cache_ids = array(); + + if($pages = $this->pagesIncluded()) { - /* 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; + // And keep a record of parents we don't need to get + // parents of themselves, as well as IDs to mark + foreach($pages as $pageArr) { + $parents[$pageArr['ParentID']] = true; + $this->_cache_ids[$pageArr['ID']] = true; } - - while (!empty($parents)) { - $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "ID" in ('.implode(',',array_keys($parents)).')'); - $parents = array(); + if(!empty($parents)) { + $q = new SQLQuery(); + $q->select(array('ID','ParentID')) + ->from('SiteTree') + ->where('"ID" in ('.implode(',',array_keys($parents)).')'); - foreach($res as $row) { + foreach($q->execute() as $row) { if ($row['ParentID']) $parents[$row['ParentID']] = true; - $this->ids[$row['ID']] = true; - $this->expanded[$row['ID']] = true; + $this->_cache_ids[$row['ID']] = true; + $this->_cache_expanded[$row['ID']] = true; } } } } /** - * Returns true if the given page should be included in the tree. + * Returns TRUE if the given page should be included in the tree. + * Caution: Does NOT check view permissions on the page. + * + * @param SiteTree $page + * @return Boolean */ - public function includeInTree($page) { - return isset($this->ids[$page->ID]) && $this->ids[$page->ID] ? true : false; + public function isPageIncluded($page) { + if($this->_cache_ids === NULL) $this->populateIDs(); + + return (isset($this->_cache_ids[$page->ID]) && $this->_cache_ids[$page->ID]); } } +/** + * Works a bit different than the other filters: + * Shows all pages *including* those deleted from stage and live. + * It does not filter out pages still existing in the different stages. + * + * @package cms + * @subpackage content + */ class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter { + + protected $childrenMethod = "AllHistoricalChildren"; + static function title() { + // TODO i18n 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]*]*>','', $tree); - $tree = ereg_replace(']*>[ \t\r\n]*$','', $tree); - - return $tree; + function pagesIncluded() { + $ids = array(); + // TODO Not very memory efficient, but usually not very many deleted pages exist + $pages = Versioned::get_including_deleted('SiteTree'); + if($pages) foreach($pages as $page) { + $ids[] = array('ID' => $page->ID, 'ParentID' => $page->ParentID); + } + return $ids; } } +/** + * Gets all pages which have changed on stage. + * + * @package cms + * @subpackage content + */ class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter { + static function title() { + // TODO i18n return "Changed pages"; } function pagesIncluded() { - return DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "Status" LIKE \'Saved%\''); + $ids = array(); + $q = new SQLQuery(); + $q->select(array('"SiteTree"."ID"','"SiteTree"."ParentID"')) + ->from('SiteTree') + ->leftJoin("SiteTree_Live", '"SiteTree_Live"."ID" = "SiteTree"."ID"') + ->where('"SiteTree"."Version" > "SiteTree_Live"."Version"'); + + foreach($q->execute() as $row) { + $ids[] = array('ID'=>$row['ID'],'ParentID'=>$row['ParentID']); + } + + return $ids; } } +/** + * @package cms + * @subpackage content + */ class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter { - public $data; - - - function __construct() { - $this->data = $_REQUEST; - } - - static function showInList() { return false; } - + static function title() { - return "Search"; + // TODO i18n + return "All pages"; } /** * Retun an array of maps containing the keys, 'ID' and 'ParentID' for each page to be displayed * in the search. + * + * @return Array */ function pagesIncluded() { - $data = $this->data; - - $this->ids = array(); - $this->expanded = array(); - + $ids = array(); + $q = new SQLQuery(); + $q->select(array('ID','ParentID')) + ->from('SiteTree'); $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%'"; + $SQL_params = Convert::raw2sql($this->params); + foreach($SQL_params as $name => $val) { + switch($name) { + // Match against URLSegment, Title, MenuTitle & Content + case 'SiteTreeSearchTerm': + $where[] = "\"URLSegment\" LIKE '%$val%' OR \"Title\" LIKE '%$val%' OR \"MenuTitle\" LIKE '%$val%' OR \"Content\" LIKE '%$val%'"; + break; + // Match against date + case 'SiteTreeFilterDate': + // TODO Date Parsing + $val = ((int)substr($val,6,4)) + . '-' . ((int)substr($val,3,2)) + . '-' . ((int)substr($val,0,2)); + $where[] = "\"LastEdited\" > '$val'"; + break; + // Match against exact ClassName + case 'ClassName': + if($val && $val != 'All') { + $where[] = "\"ClassName\" = '$val'"; + } + break; + default: + // Partial string match against a variety of fields + if(!empty($val) && singleton("SiteTree")->hasDatabaseField($name)) { + $where[] = "\"$name\" LIKE '%$val%'"; + } } } + $q->where(empty($where) ? '' : '(' . implode(') AND (',$where) . ')'); - $where = empty($where) ? '' : 'WHERE (' . implode(') AND (',$where) . ')'; + foreach($q->execute() as $row) { + $ids[] = array('ID'=>$row['ID'],'ParentID'=>$row['ParentID']); + } - $parents = array(); - - /* Do the actual search */ - $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" '.$where); - return $res; + return $ids; } -} +} \ No newline at end of file diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index 1065a75f..6ac7cbe2 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -448,18 +448,22 @@ class LeftAndMain extends Controller { } } + /** + * @return String HTML + */ public function SiteTreeAsUL() { return $this->getSiteTreeFor($this->stat('tree_class')); } /** - * Get a site tree displaying the nodes under the given objects. + * Get a site tree HTML listing which displays the nodes under the given criteria. * * @param $className The class of the root object * @param $rootID The ID of the root object. If this is null then a complete tree will be * shown * @param $childrenMethod The method to call to get the children of the tree. For example, * Children, AllChildrenIncludingDeleted, or AllHistoricalChildren + * @return String Nested