diff --git a/code/Controllers/CMSMain.php b/code/Controllers/CMSMain.php index 093865da01..a3473f1222 100644 --- a/code/Controllers/CMSMain.php +++ b/code/Controllers/CMSMain.php @@ -17,7 +17,6 @@ use SilverStripe\CMS\Forms\CMSMainAddForm; use SilverStripe\CMS\Model\CurrentRecordIdentifier; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\CMS\Search\SearchForm; use SilverStripe\Control\Controller; use SilverStripe\Control\Director; use SilverStripe\Control\HTTPRequest; @@ -61,6 +60,7 @@ use SilverStripe\ORM\Hierarchy\MarkedSet; use SilverStripe\Model\List\SS_List; use SilverStripe\Core\Validation\ValidationResult; +use SilverStripe\Forms\CompositeField; use SilverStripe\Security\InheritedPermissions; use SilverStripe\Security\PermissionProvider; use SilverStripe\Security\Security; @@ -903,18 +903,30 @@ public function getSearchFieldSchema(): string $singleton = DataObject::singleton($this->getModelClass()); $context = $singleton->getDefaultSearchContext(); + + // This logic poached directly from GridFieldFilterHeader + $searchField = $singleton::config()->get('general_search_field'); + if (!$searchField) { + $searchField = $context->getSearchFields()->first(); + $searchField = $searchField && property_exists($searchField, 'name') ? $searchField->name : null; + } + $params = $this->getRequest()->requestVar('q') ?: []; $context->setSearchParams($params); - $placeholder = _t(SearchForm::class . '.FILTERLABELTEXT2', 'Search "{model}"', ['model' => $singleton->i18n_plural_name()]); + $placeholder = _t(__CLASS__ . '.FILTERLABELTEXT', 'Search "{model}"', ['model' => $singleton->i18n_plural_name()]); $searchParams = $context->getSearchParams(); - $searchParams = array_combine(array_map(function ($key) { - return 'Search__' . $key; - }, array_keys($searchParams ?? [])), $searchParams ?? []); + + // Prefix "Search__" onto the search params to match the field names in the actual form + if (!empty($searchParams)) { + $searchParams = array_combine(array_map(function ($key) { + return 'Search__' . $key; + }, array_keys($searchParams ?? [])), $searchParams ?? []); + } $schema = [ 'formSchemaUrl' => $schemaUrl, - 'name' => 'Term', + 'name' => $searchField, 'placeholder' => $placeholder, 'filters' => $searchParams ?: new \stdClass // stdClass maps to empty json object '{}' ]; @@ -929,62 +941,13 @@ public function getSearchFieldSchema(): string */ public function getSearchForm(): Form { - $modelClass = $this->getModelClass(); - $singleton = DataObject::singleton($modelClass); - // Create the fields - $dateFrom = DateField::create( - 'Search__LastEditedFrom', - _t(SearchForm::class . '.FILTERDATEFROM', 'From') - )->setLocale(Security::getCurrentUser()->Locale); - $dateTo = DateField::create( - 'Search__LastEditedTo', - _t(SearchForm::class . '.FILTERDATETO', 'To') - )->setLocale(Security::getCurrentUser()->Locale); - $filters = CMSSiteTreeFilter::get_all_filters(); - // Remove 'All records' as we set that to empty/default value - unset($filters[CMSSiteTreeFilter_Search::class]); - $recordFilter = DropdownField::create( - 'Search__FilterClass', - _t(SearchForm::class . '.RECORD_STATUS', '{model} status', ['model' => $singleton->i18n_singular_name()]), - $filters - ); - $recordFilter->setEmptyString(_t( - SearchForm::class . '.RECORDS_ALLOPT', - 'All {model}', - ['model' => mb_strtolower($singleton->i18n_plural_name())] - )); - $classes = DropdownField::create( - 'Search__ClassName', - _t( - SearchForm::class . '.RECORD_TYPEOPT', - '{model} type', - 'Dropdown for limiting search to a record type', - ['model' => $singleton->i18n_singular_name()] - ), - $this->getRecordTypes() - ); - $classes->setEmptyString(_t(SearchForm::class . '.RECORD_TYPEANYOPT', 'Any')); - - // Group the Datefields - $dateGroup = FieldGroup::create( - _t(SearchForm::class . '.RECORD_FILTERDATEHEADING', 'Last edited'), - [$dateFrom, $dateTo] - )->setName('Search__LastEdited') - ->addExtraClass('fieldgroup--fill-width'); - - // Create the Field list - $fields = new FieldList( - $recordFilter, - $classes, - $dateGroup - ); - // Create the form + $fields = DataObject::singleton($this->getModelClass())->scaffoldSearchFields(); + $this->addSearchPrefixToFields($fields); $form = Form::create( $this, 'SearchForm', - $fields, - new FieldList() + $fields ); $form->addExtraClass('cms-search-form'); $form->setFormMethod('GET'); @@ -1001,6 +964,19 @@ public function getSearchForm(): Form return $form; } + /** + * Append a prefix to search field names to prevent conflicts with other fields in the search form + */ + private function addSearchPrefixToFields(FieldList $fields): void + { + foreach ($fields as $field) { + $field->setName('Search__' . $field->getName()); + if ($field instanceof CompositeField) { + $this->addSearchPrefixToFields($field->getChildren()); + } + } + } + /** * Returns a sorted array suitable for a dropdown with classes and their localised name */ @@ -1658,58 +1634,15 @@ public function childfilter(HTTPRequest $request): HTTPResponse ->setBody(json_encode($disallowedChildren)); } - /** - * Safely reconstruct a selected filter from a given set of query parameters - * - * @param array $params Query parameters to use, or null if none present - * @return CMSSiteTreeFilter The filter class - * @throws InvalidArgumentException if invalid filter class is passed. - */ - protected function getQueryFilter($params) - { - if (empty($params['FilterClass'])) { - return null; - } - $filterClass = $params['FilterClass']; - if (!is_subclass_of($filterClass, CMSSiteTreeFilter::class)) { - throw new InvalidArgumentException("Invalid filter class passed: {$filterClass}"); - } - return $filterClass::create($params); - } - - /** - * Returns the records meet a certain criteria as {@see CMSSiteTreeFilter} or the subrecords of a parent record - * defaulting to no filter and show all records in first level. - * Doubles as search results, if any search parameters are set through {@link SearchForm()}. - * - * @param array $params Search filter criteria - * @param int $parentID Optional parent node to filter on (can't be combined with other search criteria) - * @return SS_List - * @throws InvalidArgumentException if invalid filter class is passed. - */ - public function getList($params = [], $parentID = 0) - { - if ($filter = $this->getQueryFilter($params)) { - return $filter->getFilteredPages(); - } else { - $list = DataObject::get($this->getModelClass()); - $parentID = is_numeric($parentID) ? $parentID : 0; - return $list->filter("ParentID", $parentID); - } - } - /** * @return Form */ public function ListViewForm() { - $params = $this->getRequest()->requestVar('q'); + $params = $this->getRequest()->requestVar('q') ?? []; $parentID = $this->getRequest()->requestVar('ParentID'); - // Set default filter if other params are set - if ($params && empty($params['FilterClass'])) { - $params['FilterClass'] = CMSSiteTreeFilter_Search::class; - } - $list = $this->getList($params, $parentID); + $modelClass = $this->getModelClass(); + $list = DataObject::singleton($modelClass)->getDefaultSearchContext()->getQuery($params); $gridFieldConfig = GridFieldConfig::create()->addComponents( Injector::inst()->create(GridFieldSortableHeader::class), Injector::inst()->create(GridFieldDataColumns::class), @@ -1729,7 +1662,6 @@ public function ListViewForm() $columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class); // Set up columns and sorting for list view GridField - $modelClass = $this->getModelClass(); $fields = [ 'getTreeTitle' => _t($modelClass . '.TREETITLE', 'Title'), 'i18n_singular_name' => _t($modelClass . '.TREETYPE', 'Record Type'), diff --git a/code/Controllers/CMSSiteTreeFilter.php b/code/Controllers/CMSSiteTreeFilter.php index ca77dbcba4..221b40aa46 100644 --- a/code/Controllers/CMSSiteTreeFilter.php +++ b/code/Controllers/CMSSiteTreeFilter.php @@ -5,9 +5,7 @@ use SilverStripe\Admin\LeftAndMain_SearchFilter; use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Core\ClassInfo; -use SilverStripe\Core\Convert; use SilverStripe\Core\Injector\Injectable; -use SilverStripe\Forms\DateField; use SilverStripe\ORM\DataList; use SilverStripe\Model\List\SS_List; use SilverStripe\Versioned\Versioned; @@ -27,14 +25,6 @@ abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter { use Injectable; - /** - * Search parameters, mostly properties on {@link SiteTree}. - * Caution: Unescaped data. - * - * @var array - */ - protected $params = []; - /** * List of filtered items and all their parents * @@ -96,13 +86,6 @@ public static function get_all_filters() return $filterMap; } - public function __construct($params = null) - { - if ($params) { - $this->params = $params; - } - } - public function getChildrenMethod() { return $this->childrenMethod; @@ -131,9 +114,8 @@ public function getRecordClasses($page) * Gets the list of filtered pages * * @see {@link ModelData::getStatusFlags()} - * @return SS_List */ - abstract public function getFilteredPages(); + abstract public function getFilteredPages(DataList $existingQuery = null): DataList; /** * @return array Map of Page IDs to their respective ParentID values. @@ -187,64 +169,6 @@ public function isRecordIncluded($page) return !empty($this->_cache_ids[$page->ID]); } - /** - * Applies the default filters to a specified DataList of pages - * - * @param DataList $query Unfiltered query - * @return DataList Filtered query - */ - protected function applyDefaultFilters($query) - { - $sng = SiteTree::singleton(); - foreach ($this->params as $name => $val) { - if (empty($val)) { - continue; - } - - switch ($name) { - case 'Term': - $query = $query->filterAny([ - 'URLSegment:PartialMatch' => Convert::raw2url($val), - 'Title:PartialMatch' => $val, - 'MenuTitle:PartialMatch' => $val, - 'Content:PartialMatch' => $val - ]); - break; - - case 'URLSegment': - $query = $query->filter([ - 'URLSegment:PartialMatch' => Convert::raw2url($val), - ]); - break; - - case 'LastEditedFrom': - $fromDate = new DateField(null, null, $val); - $query = $query->filter("LastEdited:GreaterThanOrEqual", $fromDate->dataValue().' 00:00:00'); - break; - - case 'LastEditedTo': - $toDate = new DateField(null, null, $val); - $query = $query->filter("LastEdited:LessThanOrEqual", $toDate->dataValue().' 23:59:59'); - break; - - case 'ClassName': - if ($val != 'All') { - $query = $query->filter('ClassName', $val); - } - break; - - default: - $field = $sng->dbObject($name); - if ($field) { - $filter = $field->defaultSearchFilter(); - $filter->setValue($val); - $query = $query->alterDataQuery([$filter, 'apply']); - } - } - } - return $query; - } - /** * Maps a list of pages to an array of associative arrays with ID and ParentID keys * diff --git a/code/Controllers/CMSSiteTreeFilter_ChangedPages.php b/code/Controllers/CMSSiteTreeFilter_ChangedPages.php index 5d180eccc9..3b4d559715 100644 --- a/code/Controllers/CMSSiteTreeFilter_ChangedPages.php +++ b/code/Controllers/CMSSiteTreeFilter_ChangedPages.php @@ -3,7 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\ORM\DataObject; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; /** @@ -17,14 +17,17 @@ public static function title() return _t(__CLASS__ . '.Title', "Modified pages"); } - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - $table = DataObject::singleton(SiteTree::class)->baseTable(); - $liveTable = DataObject::singleton(SiteTree::class)->stageTable($table, Versioned::LIVE); - $pages = Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT); - $pages = $this->applyDefaultFilters($pages) - ->leftJoin($liveTable, "\"$liveTable\".\"ID\" = \"$table\".\"ID\"") + $table = SiteTree::singleton()->baseTable(); + $liveTable = SiteTree::singleton()->stageTable($table, Versioned::LIVE); + if ($list) { + $list = Versioned::updateListForStage($list, Versioned::DRAFT); + } else { + $list = Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT); + } + $list = $list->leftJoin($liveTable, "\"$liveTable\".\"ID\" = \"$table\".\"ID\"") ->where("\"$table\".\"Version\" <> \"$liveTable\".\"Version\""); - return $pages; + return $list; } } diff --git a/code/Controllers/CMSSiteTreeFilter_DeletedPages.php b/code/Controllers/CMSSiteTreeFilter_DeletedPages.php index 926f2b749f..ecc1ccfc55 100644 --- a/code/Controllers/CMSSiteTreeFilter_DeletedPages.php +++ b/code/Controllers/CMSSiteTreeFilter_DeletedPages.php @@ -3,6 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; /** @@ -28,10 +29,11 @@ public static function title() return _t(__CLASS__ . '.Title', "All pages, including archived"); } - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - $pages = Versioned::get_including_deleted(SiteTree::class); - $pages = $this->applyDefaultFilters($pages); - return $pages; + if ($list) { + return Versioned::updateListToIncludeDeleted($list); + } + return Versioned::get_including_deleted(SiteTree::class); } } diff --git a/code/Controllers/CMSSiteTreeFilter_PublishedPages.php b/code/Controllers/CMSSiteTreeFilter_PublishedPages.php index 5c120aeb01..e5e8944b01 100644 --- a/code/Controllers/CMSSiteTreeFilter_PublishedPages.php +++ b/code/Controllers/CMSSiteTreeFilter_PublishedPages.php @@ -3,7 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\Model\List\SS_List; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; /** @@ -37,18 +37,19 @@ public static function title() * Filters out all pages who's status who's status that doesn't exist on live * * @see {@link ModelData::getStatusFlags()} - * @return SS_List */ - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - $pages = Versioned::get_including_deleted(SiteTree::class) - ->innerJoin( - 'SiteTree_Live', - '"SiteTree_Versions"."RecordID" = "SiteTree_Live"."ID"' - ); - - $pages = $this->applyDefaultFilters($pages); - - return $pages; + if ($list) { + $list = Versioned::updateListToIncludeDeleted($list); + } else { + $list = Versioned::get_including_deleted(SiteTree::class); + } + $baseTable = SiteTree::singleton()->baseTable(); + $liveTable = SiteTree::singleton()->stageTable($baseTable, Versioned::LIVE); + return $list->innerJoin( + $liveTable, + "\"{$baseTable}_Versions\".\"RecordID\" = \"$liveTable\".\"ID\"" + ); } } diff --git a/code/Controllers/CMSSiteTreeFilter_Search.php b/code/Controllers/CMSSiteTreeFilter_Search.php index 64ba79587f..877f591446 100644 --- a/code/Controllers/CMSSiteTreeFilter_Search.php +++ b/code/Controllers/CMSSiteTreeFilter_Search.php @@ -3,7 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\Model\List\SS_List; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter @@ -17,14 +17,12 @@ public static function title() /** * Retun an array of maps containing the keys, 'ID' and 'ParentID' for each page to be displayed * in the search. - * - * @return SS_List */ - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - // Filter default records - $pages = Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT); - $pages = $this->applyDefaultFilters($pages); - return $pages; + if ($list) { + return Versioned::updateListForStage($list, Versioned::DRAFT); + } + return Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT); } } diff --git a/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php b/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php index bf9b58a676..d29e3ee2d9 100644 --- a/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php +++ b/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php @@ -3,7 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\Model\List\SS_List; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; /** @@ -31,17 +31,12 @@ public static function title() * Filters out all pages who's status is set to "Deleted". * * @see {@link ModelData::getStatusFlags()} - * @return SS_List */ - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - $pages = Versioned::get_including_deleted(SiteTree::class); - $pages = $this->applyDefaultFilters($pages); - - $pages = $pages->filterByCallback(function (SiteTree $page) { - // Doesn't exist on either stage or live - return $page->isArchived(); - }); - return $pages; + if ($list) { + return Versioned::updateListToIncludeArchivedOnly($list); + } + return Versioned::getArchivedOnly(SiteTree::class); } } diff --git a/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php b/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php index dbed7234f9..9436d29b27 100644 --- a/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php +++ b/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php @@ -3,7 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\Model\List\SS_List; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; /** @@ -21,16 +21,18 @@ public static function title() * Filters out all pages who's status is set to "Draft". * * @see {@link ModelData::getStatusFlags()} - * @return SS_List */ - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - $pages = Versioned::get_by_stage(SiteTree::class, 'Stage'); - $pages = $this->applyDefaultFilters($pages); - $pages = $pages->filterByCallback(function (SiteTree $page) { - // If page exists on stage but not on live - return $page->isOnDraftOnly(); - }); - return $pages; + if (!$list) { + $list = SiteTree::get(); + } + // Get all pages existing in draft but not live + // Don't just use withVersionedMode - that would just get the latest draft versions + // including records which have since been published. + return $list->setDataQueryParam([ + 'Versioned.mode' => 'stage_unique', + 'Versioned.stage' => Versioned::DRAFT, + ]); } } diff --git a/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php b/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php index e2e53db175..c6864789a1 100644 --- a/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php +++ b/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php @@ -3,7 +3,7 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\Model\List\SS_List; +use SilverStripe\ORM\DataList; use SilverStripe\Versioned\Versioned; /** @@ -19,17 +19,18 @@ public static function title() /** * Filters out all pages who's status is set to "Removed from draft". - * - * @return SS_List */ - public function getFilteredPages() + public function getFilteredPages(DataList $list = null): DataList { - $pages = Versioned::get_including_deleted(SiteTree::class); - $pages = $this->applyDefaultFilters($pages); - $pages = $pages->filterByCallback(function (SiteTree $page) { - // If page is removed from stage but not live - return $page->isOnLiveOnly(); - }); - return $pages; + if (!$list) { + $list = SiteTree::get(); + } + // Get all pages removed from stage but not live + // Don't just use withVersionedMode - that would just get the latest live versions + // including records which were not removed from draft. + return $list->setDataQueryParam([ + 'Versioned.mode' => 'stage_unique', + 'Versioned.stage' => Versioned::LIVE, + ]); } } diff --git a/code/Model/SiteTree.php b/code/Model/SiteTree.php index 7a4678dee8..eb0c022513 100755 --- a/code/Model/SiteTree.php +++ b/code/Model/SiteTree.php @@ -7,10 +7,13 @@ use SilverStripe\Admin\CMSEditLinkExtension; use SilverStripe\Assets\Shortcodes\FileLinkTracking; use SilverStripe\CMS\Controllers\CMSMain; +use SilverStripe\CMS\Controllers\CMSSiteTreeFilter; +use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search; use SilverStripe\CMS\Controllers\ContentController; use SilverStripe\CMS\Controllers\ModelAsController; use SilverStripe\CMS\Controllers\RootURLController; use SilverStripe\CMS\Forms\SiteTreeURLSegmentField; +use SilverStripe\CMS\Search\SiteTreeSearchContext; use SilverStripe\Control\ContentNegotiator; use SilverStripe\Control\Controller; use SilverStripe\Control\Director; @@ -50,6 +53,7 @@ use SilverStripe\ORM\HiddenClass; use SilverStripe\ORM\Hierarchy\Hierarchy; use SilverStripe\Core\Validation\ValidationResult; +use SilverStripe\Forms\DateField; use SilverStripe\Forms\HiddenField; use SilverStripe\Security\Group; use SilverStripe\Security\InheritedPermissions; @@ -64,6 +68,7 @@ use SilverStripe\Versioned\RecursivePublishable; use SilverStripe\Versioned\Versioned; use SilverStripe\Model\ArrayData; +use SilverStripe\ORM\Filters\WithinRangeFilter; use SilverStripe\Security\PermissionCheckable; use SilverStripe\View\HTML; use SilverStripe\View\Parsers\HTMLValue; @@ -289,8 +294,23 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi ]; private static $searchable_fields = [ - 'Title', - 'Content', + // See also searchableFields() which adds `FilterClass` + 'ClassName' => [ + 'general' => false, + ], + 'LastEdited' => [ + 'general' => false, + 'filter' => WithinRangeFilter::class, + 'field' => DateField::class, + ], + // The below fields are excluded from the filter form but + // are included in the general search + 'Title' => [ + 'field' => HiddenField::class, + ], + 'Content' => [ + 'field' => HiddenField::class, + ], ]; private static $field_labels = [ @@ -2844,6 +2864,52 @@ public function isHomePage(): bool return $this->URLSegment === RootURLController::get_homepage_link(); } + public function getDefaultSearchContext() + { + return SiteTreeSearchContext::create( + static::class, + $this->scaffoldSearchFields(), + $this->defaultSearchFilters() + ); + } + + public function searchableFields() + { + $fields = parent::searchableFields(); + // Push `FilterClass` to the top of the fields list + $fields = array_merge([ + 'FilterClass' => [ + 'filter' => '', + 'general' => false, + 'title' => _t(__CLASS__ . '.Filter_Status', 'Page status'), + 'field' => DropdownField::class, + ] + ], $fields); + return $fields; + } + + public function scaffoldSearchFields($_params = null) + { + $fields = parent::scaffoldSearchFields($_params); + + // Update "Status" sources + $filters = CMSSiteTreeFilter::get_all_filters(); + // Remove 'All records' as we set that to empty/default value + unset($filters[CMSSiteTreeFilter_Search::class]); + $fields->dataFieldByName('FilterClass')?->setSource($filters)->setEmptyString(CMSSiteTreeFilter_Search::singleton()->title()); + + // Update "Page type" sources + $classNames = []; + $validClasses = ClassInfo::getValidSubClasses(SiteTree::class); + $this->invokeWithExtensions('updateAllowedSubClasses', $validClasses); + foreach ($validClasses as $class) { + $classNames[$class] = SiteTree::singleton($class)->i18n_singular_name(); + } + $fields->dataFieldByName('ClassName')?->setSource($classNames)->setEmptyString(_t(__CLASS__ . '.Filter_ClassName_Any', 'Any')); + + return $fields; + } + /** * Updates the list of status updates sent in response to the CMSMain::savetreenode action */ diff --git a/code/Search/SiteTreeSearchContext.php b/code/Search/SiteTreeSearchContext.php new file mode 100644 index 0000000000..485502c2c8 --- /dev/null +++ b/code/Search/SiteTreeSearchContext.php @@ -0,0 +1,44 @@ +getQueryFilter($searchPhrase); + return $filter->getFilteredPages($query); + } + + return parent::individualFieldSearch($query, $searchableFields, $searchField, $searchPhrase); + } + + private function getQueryFilter($filterClass): CMSSiteTreeFilter + { + if (!is_subclass_of($filterClass, CMSSiteTreeFilter::class)) { + throw new InvalidArgumentException("Invalid filter class passed: {$filterClass}"); + } + return $filterClass::create(); + } +} diff --git a/tests/php/Controllers/CMSMainTest.php b/tests/php/Controllers/CMSMainTest.php index 0312f74e37..e4bbf3f98f 100644 --- a/tests/php/Controllers/CMSMainTest.php +++ b/tests/php/Controllers/CMSMainTest.php @@ -8,6 +8,7 @@ use ReflectionMethod; use SilverStripe\Admin\CMSBatchActionHandler; use SilverStripe\CMS\Controllers\CMSMain; +use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search; use SilverStripe\CMS\Model\RedirectorPage; use SilverStripe\CMS\Model\SiteTree; use SilverStripe\CMS\Tests\Controllers\CMSMainTest\TestHierarchicalDataObject; @@ -738,7 +739,7 @@ public function testSearchField() $this->assertJsonStringEqualsJsonString( json_encode([ 'formSchemaUrl' => 'admin/pages/schema/SearchForm', - 'name' => 'Term', + 'name' => 'q', 'placeholder' => 'Search "Pages"', 'filters' => new \stdClass ]), @@ -749,8 +750,8 @@ public function testSearchField() 'GET', 'admin/pages/schema/SearchForm', ['q' => [ - 'Term' => 'test', - 'FilterClass' => 'SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search' + 'q' => 'test', + 'FilterClass' => CMSSiteTreeFilter_Search::class, ]] ); $cms->setRequest($request); @@ -759,11 +760,11 @@ public function testSearchField() $this->assertJsonStringEqualsJsonString( json_encode([ 'formSchemaUrl' => 'admin/pages/schema/SearchForm', - 'name' => 'Term', + 'name' => 'q', 'placeholder' => 'Search "Pages"', 'filters' => [ - 'Search__Term' => 'test', - 'Search__FilterClass' => 'SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search' + 'q' => 'test', + 'FilterClass' => CMSSiteTreeFilter_Search::class, ] ]), $searchSchema