Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Refactor SiteTree filtering to work like GridField filters #3052

Draft
wants to merge 1 commit into
base: 6.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 13 additions & 180 deletions code/Controllers/CMSMain.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace SilverStripe\CMS\Controllers;

use InvalidArgumentException;
use LogicException;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Admin\AdminRootController;
Expand All @@ -13,11 +12,9 @@
use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
use SilverStripe\CMS\BatchActions\CMSBatchAction_Publish;
use SilverStripe\CMS\BatchActions\CMSBatchAction_Unpublish;
use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search;
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;
Expand All @@ -33,9 +30,6 @@
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ModuleResource;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
use SilverStripe\Forms\DateField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormAction;
Expand Down Expand Up @@ -66,11 +60,10 @@
use SilverStripe\Security\Security;
use SilverStripe\Security\SecurityToken;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Versioned\ChangeSet;
use SilverStripe\Versioned\ChangeSetItem;
use SilverStripe\Versioned\Versioned;
use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
use SilverStripe\Model\ArrayData;
use SilverStripe\ORM\Search\SearchContextForm;
use SilverStripe\Security\Permission;
use SilverStripe\Security\PermissionCheckable;
use SilverStripe\Versioned\RecursivePublishable;
Expand Down Expand Up @@ -134,8 +127,8 @@ class CMSMain extends LeftAndMain implements CurrentRecordIdentifier, Permission
'PublishItemsForm',
'submit',
'EditForm',
'schema',
'SearchForm',
'schema', // @TODO do I need this??
'getSearchForm',
'TreeAsUL',
'getshowdeletedsubtree',
'savetreenode',
Expand All @@ -150,6 +143,7 @@ class CMSMain extends LeftAndMain implements CurrentRecordIdentifier, Permission

private static array $url_handlers = [
'EditForm/$ID' => 'EditForm',
'GET SearchForm' => 'getSearchForm',
];

private static array $casting = [
Expand Down Expand Up @@ -496,11 +490,10 @@ public function AddForm(): CMSMainAddForm
public function TreeAsUL()
{
$modelClass = $this->getModelClass();
$filter = $this->getSearchFilter();

DataObject::singleton($modelClass)->prepopulateTreeDataCache(null, [
'childrenMethod' => $filter ? $filter->getChildrenMethod() : 'AllChildrenIncludingDeleted',
'numChildrenMethod' => $filter ? $filter->getNumChildrenMethod() : 'numChildren',
'childrenMethod' => 'AllChildrenIncludingDeleted',
'numChildrenMethod' => 'numChildren',
]);

$html = $this->getTreeFor($modelClass);
Expand Down Expand Up @@ -530,21 +523,6 @@ public function getTreeFor(
$nodeCountThreshold = null
) {
$nodeCountThreshold = is_null($nodeCountThreshold) ? Config::inst()->get($className, 'node_threshold_total') : $nodeCountThreshold;
// Provide better defaults from filter
$filter = $this->getSearchFilter();
if ($filter) {
if (!$childrenMethod) {
$childrenMethod = $filter->getChildrenMethod();
}
if (!$numChildrenMethod) {
$numChildrenMethod = $filter->getNumChildrenMethod();
}
if (!$filterFunction) {
$filterFunction = function ($node) use ($filter) {
return $filter->isRecordIncluded($node);
};
}
}

// Build set from node and begin marking
$record = ($rootID) ? $this->getRecord($rootID) : null;
Expand Down Expand Up @@ -627,15 +605,6 @@ public function getTreeNodeClasses(DataObject $node): string
}
}

// Get additional filter classes
$filter = $this->getSearchFilter();
if ($filter && ($filterClasses = $filter->getRecordClasses($node))) {
if (is_array($filterClasses)) {
$filterClasses = implode(' ', $filterClasses);
}
$classes .= ' ' . $filterClasses;
}

return trim($classes ?? '');
}

Expand Down Expand Up @@ -894,110 +863,18 @@ public function ExtraTreeTools(): string
return $html;
}

/**
* Returns the search form schema for the current model
*/
public function getSearchFieldSchema(): string
{
$schemaUrl = $this->Link('schema/SearchForm');

$singleton = DataObject::singleton($this->getModelClass());
$context = $singleton->getDefaultSearchContext();
$params = $this->getRequest()->requestVar('q') ?: [];
$context->setSearchParams($params);

$placeholder = _t(SearchForm::class . '.FILTERLABELTEXT2', 'Search "{model}"', ['model' => $singleton->i18n_plural_name()]);
$searchParams = $context->getSearchParams();
$searchParams = array_combine(array_map(function ($key) {
return 'Search__' . $key;
}, array_keys($searchParams ?? [])), $searchParams ?? []);

$schema = [
'formSchemaUrl' => $schemaUrl,
'name' => 'Term',
'placeholder' => $placeholder,
'filters' => $searchParams ?: new \stdClass // stdClass maps to empty json object '{}'
];

return json_encode($schema);
}

/**
* Returns a Form for record searching for use in templates.
*
* Can be modified from a decorator by a 'updateSearchForm' method
*/
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
$form = Form::create(
$this,
'SearchForm',
$fields,
new FieldList()
);
$context = DataObject::singleton($this->getModelClass())->getDefaultSearchContext();
$params = $this->getRequest()->requestVar('q') ?: []; // @TODO check if this is valid here
$context->setSearchParams($params);
$form = SearchContextForm::create($this, $context);
$form->addExtraClass('cms-search-form');
$form->setFormMethod('GET');
$form->setFormAction(CMSMain::singleton()->Link());
$form->disableSecurityToken();
$form->unsetValidator();

// Load the form with previously sent search data
$form->loadDataFrom($this->getRequest()->getVars());

// Allow decorators to modify the form
$this->extend('updateSearchForm', $form);

return $form;
}

Expand Down Expand Up @@ -1658,58 +1535,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),
Expand All @@ -1729,7 +1563,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'),
Expand Down
Loading
Loading