diff --git a/_config.php b/_config.php index 0fb43491..7be5e0c7 100644 --- a/_config.php +++ b/_config.php @@ -2,7 +2,6 @@ use SilverStripe\Admin\CMSMenu; use SilverStripe\CMS\Controllers\CMSMain; -use SilverStripe\CMS\Controllers\CMSPageAddController; use SilverStripe\CMS\Controllers\CMSPageEditController; use SilverStripe\CMS\Controllers\CMSPageSettingsController; use SilverStripe\CMS\Model\SiteTree; @@ -35,4 +34,3 @@ ShortcodeParser::get('default')->register( CMSMenu::remove_menu_class(CMSMain::class); CMSMenu::remove_menu_class(CMSPageEditController::class); CMSMenu::remove_menu_class(CMSPageSettingsController::class); -CMSMenu::remove_menu_class(CMSPageAddController::class); diff --git a/_config/cache.yml b/_config/cache.yml index 65c7b080..0237deb2 100644 --- a/_config/cache.yml +++ b/_config/cache.yml @@ -4,10 +4,10 @@ After: - '#corecache' --- SilverStripe\Core\Injector\Injector: - Psr\SimpleCache\CacheInterface.CMSMain_SiteTreeHints: + Psr\SimpleCache\CacheInterface.CMSMain_TreeHints: factory: SilverStripe\Core\Cache\CacheFactory constructor: - namespace: "CMSMain_SiteTreeHints" + namespace: "CMSMain_TreeHints" Psr\SimpleCache\CacheInterface.SiteTree_CreatableChildren: factory: SilverStripe\Core\Cache\CacheFactory constructor: @@ -15,4 +15,4 @@ SilverStripe\Core\Injector\Injector: Psr\SimpleCache\CacheInterface.SiteTree_PageIcons: factory: SilverStripe\Core\Cache\CacheFactory constructor: - namespace: "SiteTree_PageIcons" \ No newline at end of file + namespace: "SiteTree_PageIcons" diff --git a/code/Controllers/CMSMain.php b/code/Controllers/CMSMain.php index 61f3635a..919d21a0 100644 --- a/code/Controllers/CMSMain.php +++ b/code/Controllers/CMSMain.php @@ -13,10 +13,12 @@ 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\Model\CurrentPageIdentifier; +use SilverStripe\CMS\Forms\CMSMainAddForm; +use SilverStripe\CMS\Model\CurrentRecordIdentifier; use SilverStripe\CMS\Model\RedirectorPage; use SilverStripe\CMS\Model\SiteTree; use SilverStripe\CMS\Model\VirtualPage; +use SilverStripe\CMS\Search\SearchForm; use SilverStripe\Control\Controller; use SilverStripe\Control\Director; use SilverStripe\Control\HTTPRequest; @@ -25,7 +27,6 @@ use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\Control\PjaxResponseNegotiator; use SilverStripe\Core\Cache\MemberCacheFlusher; use SilverStripe\Core\Config\Config; -use SilverStripe\Core\Convert; use SilverStripe\Core\Environment; use SilverStripe\Core\Flushable; use SilverStripe\Core\Injector\Injector; @@ -47,10 +48,8 @@ use SilverStripe\Forms\LabelField; use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\Tab; use SilverStripe\Forms\TabSet; -use SilverStripe\Forms\TextField; use SilverStripe\Model\List\ArrayList; use SilverStripe\ORM\CMSPreviewable; -use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; use SilverStripe\ORM\FieldType\DBHTMLText; @@ -60,7 +59,6 @@ use SilverStripe\ORM\Hierarchy\MarkedSet; use SilverStripe\Model\List\SS_List; use SilverStripe\Core\Validation\ValidationResult; use SilverStripe\Security\InheritedPermissions; -use SilverStripe\Security\Member; use SilverStripe\Security\Permission; use SilverStripe\Security\PermissionProvider; use SilverStripe\Security\Security; @@ -71,6 +69,7 @@ use SilverStripe\Versioned\ChangeSetItem; use SilverStripe\Versioned\Versioned; use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController; use SilverStripe\Model\ArrayData; +use SilverStripe\Versioned\RecursivePublishable; use SilverStripe\View\Requirements; /** @@ -78,53 +77,47 @@ use SilverStripe\View\Requirements; * * This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main * admin menu. - * - * @mixin LeftAndMainPageIconsExtension */ -class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider, Flushable, MemberCacheFlusher +class CMSMain extends LeftAndMain implements CurrentRecordIdentifier, PermissionProvider, Flushable, MemberCacheFlusher { /** * Unique ID for page icons CSS block */ - const PAGE_ICONS_ID = 'PageIcons'; + public const PAGE_ICONS_ID = 'PageIcons'; // @TODO AHHHH!!! - private static $url_segment = 'pages'; + private static string $url_segment = 'pages'; - private static $url_rule = '/$Action/$ID/$OtherID'; + private static string $url_rule = '/$Action/$ID/$OtherID'; // Maintain a lower priority than other administration sections // so that Director does not think they are actions of CMSMain - private static $url_priority = 39; + private static int $url_priority = 39; - private static $menu_title = 'Edit Page'; + private static $menu_title = 'Pages'; - private static $menu_icon_class = 'font-icon-sitemap'; + private static string $menu_icon_class = 'font-icon-sitemap'; - private static $menu_priority = 10; + private static int $menu_priority = 10; - private static $tree_class = SiteTree::class; + private static string $tree_class = SiteTree::class; - private static $session_namespace = CMSMain::class; + private static string $session_namespace = CMSMain::class; - private static $required_permission_codes = 'CMS_ACCESS_CMSMain'; + private static string|array $required_permission_codes = 'CMS_ACCESS_CMSMain'; /** * Should the archive warning message be dynamic based on the specific content? This is slow on larger sites and can be disabled. - * - * @config - * @var bool */ - private static $enable_dynamic_archive_warning_message = true; + private static bool $enable_dynamic_archive_warning_message = true; /** * Amount of results showing on a single page. - * - * @config - * @var int */ - private static $page_length = 15; + private static int $page_length = 15; - private static $allowed_actions = [ + private static array $allowed_actions = [ + 'add', + 'AddForm', 'archive', 'deleteitems', 'DeleteItemsForm', @@ -138,7 +131,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr 'EditForm', 'schema', 'SearchForm', - 'SiteTreeAsUL', + 'TreeAsUL', 'getshowdeletedsubtree', 'savetreenode', 'getsubtree', @@ -150,26 +143,26 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr 'childfilter', ]; - private static $url_handlers = [ + private static array $url_handlers = [ 'EditForm/$ID' => 'EditForm', ]; - private static $casting = [ + private static array $casting = [ 'TreeIsFiltered' => 'Boolean', 'AddForm' => 'HTMLFragment', - 'LinkPages' => 'Text', + 'LinkRecords' => 'Text', 'Link' => 'Text', 'ListViewForm' => 'HTMLFragment', 'ExtraTreeTools' => 'HTMLFragment', - 'PageList' => 'HTMLFragment', - 'PageListSidebar' => 'HTMLFragment', - 'SiteTreeHints' => 'HTMLFragment', + 'RecordList' => 'HTMLFragment', + 'RecordListSidebar' => 'HTMLFragment', + 'TreeHints' => 'HTMLFragment', 'SecurityID' => 'Text', - 'SiteTreeAsUL' => 'HTMLFragment', + 'TreeAsUL' => 'HTMLFragment', ]; - private static $dependencies = [ - 'HintsCache' => '%$' . CacheInterface::class . '.CMSMain_SiteTreeHints', + private static array $dependencies = [ + 'HintsCache' => '%$' . CacheInterface::class . '.CMSMain_TreeHints', ]; /** @@ -197,7 +190,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // In case we're not showing a specific record, explicitly remove any session state, // to avoid it being highlighted in the tree, and causing an edit form to show. if (!$request->param('Action')) { - $this->setCurrentPageID(null); + $this->setCurrentRecordID(null); } return parent::index($request); @@ -216,51 +209,42 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Get pages listing area - * - * @return DBHTMLText + * Get record listing area */ - public function PageList() + public function RecordList(): DBHTMLText { - return $this->renderWith($this->getTemplatesWithSuffix('_PageList')); + return $this->renderWith($this->getTemplatesWithSuffix('_RecordList')); } /** - * Page list view for edit-form - * - * @return DBHTMLText + * Record list view for edit-form */ - public function PageListSidebar() + public function RecordListSidebar(): DBHTMLText { - return $this->renderWith($this->getTemplatesWithSuffix('_PageList_Sidebar')); + return $this->renderWith($this->getTemplatesWithSuffix('_RecordList_Sidebar')); } /** * If this is set to true, the "switchView" context in the * template is shown, with links to the staging and publish site. - * - * @return boolean */ - public function ShowSwitchView() + public function ShowSwitchView(): bool { return true; } /** - * Overloads the LeftAndMain::ShowView. Allows to pass a page as a parameter, so we are able + * Overloads the LeftAndMain::ShowView. Allows to pass a record as a parameter, so we are able * to switch view also for archived versions. - * - * @param SiteTree $page - * @return array */ - public function SwitchView($page = null) + public function SwitchView(?DataObject $record = null): array { - if (!$page) { - $page = $this->currentPage(); + if (!$record) { + $record = $this->currentRecord(); } - if ($page) { - $nav = SilverStripeNavigator::get_for_record($page); + if ($record) { + $nav = SilverStripeNavigator::get_for_record($record); return $nav['items']; } } @@ -289,14 +273,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $link; } - public function LinkPages() + public function LinkRecords() { return CMSPagesController::singleton()->Link(); } - public function LinkPagesWithSearch() + public function LinkRecordsWithSearch() { - return $this->LinkWithSearch($this->LinkPages()); + return $this->LinkWithSearch($this->LinkRecords()); } /** @@ -306,7 +290,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr */ public function LinkTreeView() { - // Tree view is just default link to main pages section (no /treeview suffix) + // Tree view is just default link to main section (no /treeview suffix) return CMSMain::singleton()->Link(); } @@ -317,12 +301,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr */ public function LinkListView() { - // Note : Force redirect to top level page controller (no parentid) + // Note : Force redirect to top level record controller (no parentid) return $this->LinkWithSearch(CMSMain::singleton()->Link('listview')); } /** - * Link to list view for children of a parent page + * Link to list view for children of a parent record * * @param int|string $parentID Literal parentID, or placeholder (e.g. '%d') for * client side substitution @@ -366,28 +350,31 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Get the link for editing a page. + * Get the link for editing a record. * * @see CMSEditLinkExtension::getCMSEditLinkForManagedDataObject() */ - public function getCMSEditLinkForManagedDataObject(SiteTree $obj): string + public function getCMSEditLinkForManagedDataObject(DataObject $obj): string { return Controller::join_links(CMSPageEditController::singleton()->Link('show'), $obj->ID); } - public function LinkPageEdit($id = null) + public function LinkRecordEdit($id = null) { if (!$id) { - $id = $this->currentPageID(); + $id = $this->currentRecordID(); } return $this->LinkWithSearch( Controller::join_links(CMSPageEditController::singleton()->Link('show'), $id) ); } - public function LinkPageSettings() + public function LinkRecordSettings() { - if ($id = $this->currentPageID()) { + if (!DataObject::singleton($this->getModelClass())->hasMethod('getSettingsFields')) { // @TODO This is awful, I'd much rather it just be part of the main form. + return null; + } + if ($id = $this->currentRecordID()) { return $this->LinkWithSearch( Controller::join_links(CMSPageSettingsController::singleton()->Link('show'), $id) ); @@ -396,10 +383,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } } - public function LinkPageHistory() + public function LinkRecordHistory() { $controller = Injector::inst()->get(CMSPageHistoryViewerController::class); - if (($id = $this->currentPageID()) && $controller) { + if (($id = $this->currentRecordID()) && $controller) { if ($controller) { return $this->LinkWithSearch( Controller::join_links($controller->Link('show'), $id) @@ -463,10 +450,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $link; } - public function LinkPageAdd($extra = null, $placeholders = null) + public function LinkRecordAdd($extra = null, $placeholders = null) { - $link = CMSPageAddController::singleton()->Link(); - $this->extend('updateLinkPageAdd', $link); + $link = $this->Link('add'); + $this->extend('updateLinkRecordAdd', $link); if ($extra) { $link = Controller::join_links($link, $extra); @@ -484,10 +471,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr */ public function LinkPreview() { - $record = $this->getRecord($this->currentPageID()); + $record = $this->getRecord($this->currentRecordID()); $baseLink = Director::absoluteBaseURL(); - if ($record && $record instanceof SiteTree) { - // if we are an external redirector don't show a link + if ($record && $record->hasMethod('Link')) { + // if we are an external redirector don't show a link //@TODO generalise this!!!!! if ($record instanceof RedirectorPage && $record->RedirectionType == 'External') { $baseLink = false; } else { @@ -497,12 +484,27 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $baseLink; } + public function add() + { + if ($this->getRequest()->isAjax()) { + return $this->AddForm()->forTemplate(); + } + return $this->render([ + 'Content' => DBHTMLText::create()->setValue($this->AddForm()->forTemplate()), + ]); + } + + public function AddForm(): Form + { + return CMSMainAddForm::create($this); + } + /** * Return the entire site tree as a nested set of ULs */ - public function SiteTreeAsUL() + public function TreeAsUL() { - $treeClass = $this->config()->get('tree_class'); + $treeClass = $this->getModelClass(); $filter = $this->getSearchFilter(); DataObject::singleton($treeClass)->prepopulateTreeDataCache(null, [ @@ -510,9 +512,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr 'numChildrenMethod' => $filter ? $filter->getNumChildrenMethod() : 'numChildren', ]); - $html = $this->getSiteTreeFor($treeClass); + $html = $this->getTreeFor($treeClass); - $this->extend('updateSiteTreeAsUL', $html); + $this->extend('updateTreeAsUL', $html); return $html; } @@ -528,9 +530,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr * @param string $numChildrenMethod * @param callable $filterFunction * @param int $nodeCountThreshold - * @return string Nested unordered list with links to each page + * @return string Nested unordered list with links to each record */ - public function getSiteTreeFor( + public function getTreeFor( $className, $rootID = null, $childrenMethod = null, @@ -550,7 +552,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } if (!$filterFunction) { $filterFunction = function ($node) use ($filter) { - return $filter->isPageIncluded($node); + return $filter->isRecordIncluded($node); }; } } @@ -568,20 +570,23 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Mark tree from this node $markingSet->markPartialTree(); - // Ensure current page is exposed - $currentPage = $this->currentPage(); - if ($currentPage) { - $markingSet->markToExpose($currentPage); + // Ensure current record is exposed + $currentRecord = $this->currentRecord(); + if ($currentRecord) { + $markingSet->markToExpose($currentRecord); } // Pre-cache permissions - $checker = SiteTree::getPermissionChecker(); + $modelClass = $this->getModelClass(); + $singleton = DataObject::singleton($modelClass); + $checker = $singleton->hasMethod('getPermissionChecker') ? $modelClass::getPermissionChecker() : null; // @TODO eww why is it static? if ($checker instanceof InheritedPermissions) { $checker->prePopulatePermissionCache( InheritedPermissions::EDIT, $markingSet->markedNodeIDs() ); } + // @TODO if we don't have inherited permissions, make sure we still DO do permission checks where needed!! // Render using full-subtree template return $markingSet->renderChildren( @@ -590,7 +595,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr ); } - /** * Get callback to determine template customisations for nodes * @@ -599,14 +603,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr protected function getTreeNodeCustomisations() { $rootTitle = $this->getCMSTreeTitle(); - return function (SiteTree $node) use ($rootTitle) { + return function (DataObject $node) use ($rootTitle) { return [ 'listViewLink' => $this->LinkListViewChildren($node->ID), 'rootTitle' => $rootTitle, 'extraClass' => $this->getTreeNodeClasses($node), 'Title' => _t( - CMSMain::class . '.PAGETYPE_TITLE', - '(Page type: {type}) {title}', + CMSMain::class . '.RECORD_TYPE_TITLE', + '(Record type: {type}) {title}', [ 'type' => $node->i18n_singular_name(), 'title' => $node->Title, @@ -617,15 +621,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Get extra CSS classes for a page's tree node - * - * @param SiteTree $node - * @return string + * Get extra CSS classes for a record's tree node */ - public function getTreeNodeClasses(SiteTree $node) + public function getTreeNodeClasses(DataObject $node): string { // Get classes from object - $classes = $node->CMSTreeClasses(); + $classes = $node->CMSTreeClasses(); // @TODO that's obviously not a thing. // Get status flag classes $flags = $node->getStatusFlags(); @@ -638,7 +639,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Get additional filter classes $filter = $this->getSearchFilter(); - if ($filter && ($filterClasses = $filter->getPageClasses($node))) { + if ($filter && ($filterClasses = $filter->getRecordClasses($node))) { // @TODO rename getRecordClasses or similar (though this is probably part of https://github.com/silverstripe/silverstripe-cms/issues/2949) if (is_array($filterClasses)) { $filterClasses = implode(' ', $filterClasses); } @@ -654,8 +655,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr */ public function getsubtree(HTTPRequest $request): HTTPResponse { - $html = $this->getSiteTreeFor( - $this->config()->get('tree_class'), + $html = $this->getTreeFor( + $this->getModelClass(), $request->getVar('ID'), null, null, @@ -687,7 +688,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $record = $this->getRecord($id); if (!$record) { - continue; // In case a page is no longer available + continue; // In case a record is no longer available } // Create marking set with sole marked root @@ -700,7 +701,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset) $prev = null; - $className = $this->config()->get('tree_class'); + $className = $this->getModelClass(); $next = DataObject::get($className) ->filter('ParentID', $record->ParentID) ->filter('Sort:GreaterThan', $record->Sort) @@ -750,17 +751,17 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr if (!SecurityToken::inst()->checkRequest($request)) { $this->httpError(400); } - if (!$this->CanOrganiseSitetree()) { + if (!$this->CanOrganiseTree()) { $this->httpError( 403, _t( - __CLASS__.'.CANT_REORGANISE', - "You do not have permission to rearange the site tree. Your change was not saved." + __CLASS__.'.CANT_REORGANISE2', + "You do not have permission to rearange the tree. Your change was not saved.", ) ); } - $className = $this->config()->get('tree_class'); + $className = $this->getModelClass(); $id = $request->requestVar('ID'); $parentID = $request->requestVar('ParentID'); if (!is_numeric($id) || !is_numeric($parentID)) { @@ -768,26 +769,25 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } // Check record exists in the DB - /** @var SiteTree $node */ $node = DataObject::get_by_id($className, $id); if (!$node) { $this->httpError( 500, _t( - __CLASS__.'.PLEASESAVE', - "Please Save Page: This page could not be updated because it hasn't been saved yet." + __CLASS__.'.PLEASESAVE2', + "Please Save Record: This record could not be updated because it hasn't been saved yet." ) ); } // Check top level permissions - $root = $node->getParentType(); + $root = $node->getParentType(); // @TODO generalise. POC has `$node->ParentID == 0 ? 'root' : 'subpage';` if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) { $this->httpError( 403, _t( - __CLASS__.'.CANT_REORGANISE', - "You do not have permission to alter Top level pages. Your change was not saved." + __CLASS__.'.CANT_REORGANISE_TOPLEVEL', + 'You do not have permission to alter Top level records. Your change was not saved.' ) ); } @@ -805,20 +805,20 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $node->write(); $statusUpdates['modified'][$node->ID] = [ - 'TreeTitle' => $node->TreeTitle + 'TreeTitle' => $node->TreeTitle // @TODO generalise. ]; - // Update all dependent pages + // Update all dependent pages // @TODO generalise!! We shouldn't reference explicitly page types here. $virtualPages = VirtualPage::get()->filter("CopyContentFromID", $node->ID); foreach ($virtualPages as $virtualPage) { $statusUpdates['modified'][$virtualPage->ID] = [ - 'TreeTitle' => $virtualPage->TreeTitle + 'TreeTitle' => $virtualPage->TreeTitle // @TODO generalise. ]; } $this->getResponse()->addHeader( 'X-Status', - rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.') ?? '') + rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL2', 'Reorganised the tree successfully.') ?? '') ); } @@ -830,7 +830,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $node->Sort = ++$counter; $node->write(); $statusUpdates['modified'][$node->ID] = [ - 'TreeTitle' => $node->TreeTitle + 'TreeTitle' => $node->TreeTitle // @TODO generalise. ]; } elseif (is_numeric($id)) { // Nodes that weren't "actually moved" shouldn't be registered as @@ -846,7 +846,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $this->getResponse()->addHeader( 'X-Status', - rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.') ?? '') + rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL2', 'Reorganised the tree successfully.') ?? '') ); } @@ -857,64 +857,43 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Whether the current member has the permission to reorganise SiteTree objects. - * @return bool + * Whether the current member has the permission to reorganise records. */ - public function CanOrganiseSitetree() + public function CanOrganiseTree(): bool { - return Permission::check('SITETREE_REORGANISE'); + return (bool) Permission::check('SITETREE_REORGANISE'); // @TODO model needs a method or config to say "this is the permission for that!!" } - /** - * @return boolean + * Whether the tree has been filtered in this request or not. */ - public function TreeIsFiltered() + public function TreeIsFiltered(): bool { $query = $this->getRequest()->getVar('q'); return !empty($query); } - public function ExtraTreeTools() + public function ExtraTreeTools(): string { $html = ''; $this->extend('updateExtraTreeTools', $html); return $html; } - /** - * This provides information required to generate the search form - * and can be modified on extensions through updateSearchContext - * - * @return \SilverStripe\ORM\Search\SearchContext - */ - public function getSearchContext() - { - $context = SiteTree::singleton()->getDefaultSearchContext(); - - $this->extend('updateSearchContext', $context); - - return $context; - } - /** * Returns the search form schema for the current model - * - * @return string */ - public function getSearchFieldSchema() + public function getSearchFieldSchema(): string { $schemaUrl = $this->Link('schema/SearchForm'); - $context = $this->getSearchContext(); + $singleton = DataObject::singleton($this->getModelClass()); + $context = $singleton->getDefaultSearchContext(); $params = $this->getRequest()->requestVar('q') ?: []; $context->setSearchParams($params); - $placeholder = _t('SilverStripe\\CMS\\Search\\SearchForm.FILTERLABELTEXT', 'Search') . ' "' . - SiteTree::singleton()->i18n_plural_name() . '"'; - + $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 ?? []); @@ -930,50 +909,59 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Returns a Form for page searching for use in templates. + * Returns a Form for record searching for use in templates. * * Can be modified from a decorator by a 'updateSearchForm' method - * - * @return Form */ - public function getSearchForm() + public function getSearchForm(): Form { + $modelClass = $this->getModelClass(); + $singleton = DataObject::singleton($modelClass); // Create the fields $dateFrom = DateField::create( 'Search__LastEditedFrom', - _t('SilverStripe\\CMS\\Search\\SearchForm.FILTERDATEFROM', 'From') + _t(SearchForm::class . '.FILTERDATEFROM', 'From') )->setLocale(Security::getCurrentUser()->Locale); $dateTo = DateField::create( 'Search__LastEditedTo', - _t('SilverStripe\\CMS\\Search\\SearchForm.FILTERDATETO', 'To') + _t(SearchForm::class . '.FILTERDATETO', 'To') )->setLocale(Security::getCurrentUser()->Locale); $filters = CMSSiteTreeFilter::get_all_filters(); - // Remove 'All pages' as we set that to empty/default value + // Remove 'All records' as we set that to empty/default value unset($filters[CMSSiteTreeFilter_Search::class]); - $pageFilter = DropdownField::create( + $recordFilter = DropdownField::create( 'Search__FilterClass', - _t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGES', 'Page status'), + _t(SearchForm::class . '.RECORD_STATUS', '{model} status', ['model' => $singleton->i18n_singular_name()]), $filters ); - $pageFilter->setEmptyString(_t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGESALLOPT', 'All pages')); - $pageClasses = DropdownField::create( + $recordFilter->setEmptyString(_t( + SearchForm::class . '.RECORDS_ALLOPT', + 'All {model}', + ['model' => mb_strtolower($singleton->i18n_plural_name())] + )); + $classes = DropdownField::create( 'Search__ClassName', - _t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGETYPEOPT', 'Page type', 'Dropdown for limiting search to a page type'), - $this->getPageTypes() + _t( + SearchForm::class . '.RECORD_TYPEOPT', + '{model} type', + 'Dropdown for limiting search to a record type', + ['model' => $singleton->i18n_singular_name()] + ), + $this->getRecordTypes() ); - $pageClasses->setEmptyString(_t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGETYPEANYOPT', 'Any')); + $classes->setEmptyString(_t(SearchForm::class . '.RECORD_TYPEANYOPT', 'Any')); // Group the Datefields $dateGroup = FieldGroup::create( - _t('SilverStripe\\CMS\\Search\\SearchForm.PAGEFILTERDATEHEADING', 'Last edited'), + _t(SearchForm::class . '.RECORD_FILTERDATEHEADING', 'Last edited'), [$dateFrom, $dateTo] )->setName('Search__LastEdited') ->addExtraClass('fieldgroup--fill-width'); // Create the Field list $fields = new FieldList( - $pageFilter, - $pageClasses, + $recordFilter, + $classes, $dateGroup ); @@ -1000,18 +988,16 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Returns a sorted array suitable for a dropdown with pagetypes and their translated name - * - * @return array + * Returns a sorted array suitable for a dropdown with classes and their localised name */ - protected function getPageTypes() + protected function getRecordTypes(): array { - $pageTypes = []; - foreach (SiteTree::page_type_classes() as $pageTypeClass) { - $pageTypes[$pageTypeClass] = SiteTree::singleton($pageTypeClass)->i18n_singular_name(); + $types = []; + foreach (SiteTree::page_type_classes() as $class) { // @TODO generalise!! Not fully into the POC way. + $types[$class] = DataObject::singleton($class)->i18n_singular_name(); } - asort($pageTypes); - return $pageTypes; + asort($types); + return $types; } public function doSearch(array $data, Form $form): HTTPResponse @@ -1028,7 +1014,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr { $breadcrumbs = $this->Breadcrumbs(); if ($breadcrumbs->count() < 2) { - return $this->LinkPages(); + return $this->LinkRecords(); } // Get second from end breadcrumb return $breadcrumbs @@ -1040,15 +1026,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr { $items = ArrayList::create(); - if (($this->getAction() !== 'index') && ($record = $this->currentPage())) { - // The page is being edited + if (($this->getAction() !== 'index') && ($record = $this->currentRecord())) { + // The record is being edited $this->buildEditFormBreadcrumb($items, $record, $unlinked); } else { - // Ensure we always have the "Pages" crumb first + // Ensure we always have the admin section crumb first $this->pushCrumb( $items, CMSPagesController::menu_title(), - $unlinked ? false : $this->LinkPages() + $unlinked ? false : $this->LinkRecords() ); if ($this->TreeIsFiltered()) { @@ -1056,13 +1042,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $this->pushCrumb( $items, _t(CMSMain::class . '.SEARCHRESULTS', 'Search results'), - ($unlinked) ? false : $this->LinkPages() + ($unlinked) ? false : $this->LinkRecords() ); } elseif ($parentID = $this->getRequest()->getVar('ParentID')) { - // We're navigating the listview. ParentID is the page whose + // We're navigating the listview. ParentID is the record whose // children are currently displayed. - if ($page = SiteTree::get()->byID($parentID)) { - $this->buildListViewBreadcrumb($items, $page); + if ($record = DataObject::get($this->getModelClass())->byID($parentID)) { + $this->buildListViewBreadcrumb($items, $record); } } } @@ -1084,30 +1070,32 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Build Breadcrumb for the Edit page form. Each crumb links back to its own edit form. + * Build Breadcrumb for the Edit form. Each crumb links back to its own edit form. */ - private function buildEditFormBreadcrumb(ArrayList $items, SiteTree $page, bool $unlinked): void + private function buildEditFormBreadcrumb(ArrayList $items, DataObject $record, bool $unlinked): void { - // Find all ancestors of the provided page - $ancestors = $page->getAncestors(true); + // Find all ancestors of the provided record + /** @var DataObject&Hierarchy $record */ + $ancestors = $record->getAncestors(true); $ancestors = array_reverse($ancestors->toArray() ?? []); foreach ($ancestors as $ancestor) { // Link to the ancestor's edit form $this->pushCrumb( $items, - $ancestor->getMenuTitle(), + $ancestor->getMenuTitle(), // @TODO generalise!! $unlinked ? false : $ancestor->getCMSEditLink() ); } } /** - * Build Breadcrumb for the List view. Each crumb links to the list view for that page. + * Build Breadcrumb for the List view. Each crumb links to the list view for that record. */ - private function buildListViewBreadcrumb(ArrayList $items, SiteTree $page): void + private function buildListViewBreadcrumb(ArrayList $items, DataObject $record): void { - // Find all ancestors of the provided page - $ancestors = $page->getAncestors(true); + // Find all ancestors of the provided record + /** @var DataObject&Hierarchy $record */ + $ancestors = $record->getAncestors(true); $ancestors = array_reverse($ancestors->toArray() ?? []); //turns the title and link of the breadcrumbs into template-friendly variables @@ -1121,7 +1109,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $params['ParentID'] = $ancestor->ID; $this->pushCrumb( $items, - $ancestor->getMenuTitle(), + $ancestor->getMenuTitle(), // @TODO generalise!! Controller::join_links($this->Link(), '?' . http_build_query($params ?? [])) ); } @@ -1130,13 +1118,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr /** * Create serialized JSON string with site tree hints data to be injected into * 'data-hints' attribute of root node of jsTree. - * - * @return string Serialized JSON */ - public function SiteTreeHints() + public function TreeHints(): string // @TODO rename { - $classes = SiteTree::page_type_classes(); - $memberID = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0; + $classes = SiteTree::page_type_classes(); // @TODO generalise!! + $memberID = Security::getCurrentUser()?->ID ?? 0; $cache = $this->getHintsCache(); $cacheKey = $this->generateHintsCacheKey($memberID); $json = $cache->get($cacheKey); @@ -1154,12 +1140,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $def['Root']['disallowedChildren'] = []; // Contains all possible classes to support UI controls listing them all, - // such as the "add page here" context menu. + // such as the "add record here" context menu. $def['All'] = []; // Identify disallows and set globals foreach ($classes as $class) { - $obj = singleton($class); + $obj = DataObject::singleton($class); if ($obj instanceof HiddenClass) { continue; } @@ -1170,8 +1156,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr ]; // Check if can be created at the root - $needsPerm = $obj->config()->get('need_permission'); - if (!$obj->config()->get('can_be_root') + $needsPerm = $obj::config()->get('need_permission'); + if ($obj::config()->get('can_be_root') === false || (!array_key_exists($class, $canCreate ?? []) || !$canCreate[$class]) || ($needsPerm && !$this->can($needsPerm)) ) { @@ -1181,18 +1167,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Hint data specific to the class $def[$class] = []; - $defaultChild = $obj->defaultChild(); - if ($defaultChild !== 'Page' && $defaultChild !== null) { + $defaultChild = $obj->defaultChild(); // @TODO generalise!! + if ($defaultChild !== 'Page' && $defaultChild !== null) { // @TODO Find out where that 'Page' string comes from and fix it $def[$class]['defaultChild'] = $defaultChild; } - $defaultParent = $obj->defaultParent(); + $defaultParent = $obj->defaultParent(); // @TODO generalise!! if ($defaultParent !== 1 && $defaultParent !== null) { $def[$class]['defaultParent'] = $defaultParent; } } - $this->extend('updateSiteTreeHints', $def); + $this->extend('updateTreeHints', $def); $json = json_encode($def); $cache->set($cacheKey, $json); @@ -1202,24 +1188,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr /** * Populates an array of classes in the CMS - * which allows the user to change the page type. - * - * @return SS_List + * which allows the user to change the record's ClassName field. */ - public function PageTypes() + public function RecordTypes(): SS_List { - $classes = SiteTree::page_type_classes(); + $classes = SiteTree::page_type_classes(); // @TODO generalise!! $result = new ArrayList(); foreach ($classes as $class) { - $instance = SiteTree::singleton($class); + $instance = DataObject::singleton($class); if ($instance instanceof HiddenClass) { continue; } // skip this type if it is restricted - $needPermissions = $instance->config()->get('need_permission'); + $needPermissions = $instance::config()->get('need_permission'); // @TODO consider renaming that since it's so vague if ($needPermissions && !$this->can($needPermissions)) { continue; } @@ -1227,8 +1211,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $result->push(new ArrayData([ 'ClassName' => $class, 'AddAction' => $instance->i18n_singular_name(), - 'Description' => $instance->i18n_classDescription(), - 'IconURL' => $instance->getPageIconURL(), + 'Description' => $instance->i18n_classDescription(), // @TODO generalise!! + 'IconURL' => $instance->getPageIconURL(), // @TODO generalise!! 'Title' => $instance->i18n_singular_name(), ])); } @@ -1243,20 +1227,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr * * @param int $id Record ID * @param int $versionID optional Version id of the given record - * @return SiteTree */ - public function getRecord($id, $versionID = null) + public function getRecord($id, ?int $versionID = null): ?DataObject { if (!$id) { return null; } - $treeClass = $this->config()->get('tree_class'); - if ($id instanceof $treeClass) { + $modelClass = $this->getModelClass(); + if ($id instanceof $modelClass) { return $id; } if (substr($id ?? '', 0, 3) == 'new') { return $this->getNewItem($id); } + if ($id === 'singleton') { + return DataObject::singleton($modelClass); + } if (!is_numeric($id)) { return null; } @@ -1267,25 +1253,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $versionID = (int) $this->getRequest()->getVar('Version'); } - /** @var SiteTree $record */ + $isVersioned = $modelClass::has_extension(Versioned::class); if ($versionID) { - $record = Versioned::get_version($treeClass, $id, $versionID); + if (!$isVersioned) { + throw new HTTPResponse_Exception("Cannot get a version of non-versioned $modelClass record", 400); + } + $record = Versioned::get_version($modelClass, $id, $versionID); } else { - $record = DataObject::get_by_id($treeClass, $id); + $record = DataObject::get_by_id($modelClass, $id); } // Then, try getting a record from the live site - if (!$record) { - // $record = Versioned::get_one_by_stage($treeClass, "Live", "\"$treeClass\".\"ID\" = $id"); + if (!$record && $isVersioned) { + // $record = Versioned::get_one_by_stage($modelClass, "Live", "\"$modelClass\".\"ID\" = $id"); Versioned::set_stage(Versioned::LIVE); - singleton($treeClass)->flushCache(); + DataObject::singleton($modelClass)->flushCache(); - $record = DataObject::get_by_id($treeClass, $id); + $record = DataObject::get_by_id($modelClass, $id); } // Then, try getting a deleted record - if (!$record) { - $record = Versioned::get_latest_version($treeClass, $id); + if (!$record && $isVersioned) { + $record = Versioned::get_latest_version($modelClass, $id); } // Set the reading mode back to what it was. @@ -1298,11 +1287,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr * {@inheritdoc} * * @param HTTPRequest $request - * @return Form */ - public function EditForm($request = null) + public function EditForm($request = null): Form { - // set page ID from request + // set record ID from request if ($request) { // Validate id is present $id = $request->param('ID'); @@ -1310,7 +1298,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $this->httpError(400); return null; } - $this->setCurrentPageID($id); + $this->setCurrentRecordID($id); } return $this->getEditForm(); } @@ -1318,13 +1306,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr /** * @param int $id * @param FieldList $fields - * @return Form */ - public function getEditForm($id = null, $fields = null) + public function getEditForm($id = null, $fields = null): Form { // Get record if (!$id) { - $id = $this->currentPageID(); + $id = $this->currentRecordID(); } $record = $this->getRecord($id); @@ -1340,18 +1327,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Add extra fields $deletedFromStage = !$record->isOnDraft(); - $fields->push($idField = new HiddenField("ID", false, $id)); + $fields->push(new HiddenField("ID", false, $id)); // Necessary for different subsites - $fields->push($liveLinkField = new HiddenField("AbsoluteLink", false, $record->AbsoluteLink())); + $fields->push($liveLinkField = new HiddenField("AbsoluteLink", false, $record->AbsoluteLink())); // @TODO generalise!! $fields->push($liveLinkField = new HiddenField("LiveLink")); $fields->push($stageLinkField = new HiddenField("StageLink")); $fields->push($archiveWarningMsgField = new HiddenField("ArchiveWarningMessage")); - $fields->push(new HiddenField("TreeTitle", false, $record->getTreeTitle())); + $fields->push(new HiddenField("TreeTitle", false, $record->getTreeTitle())); // @TODO generalise!! $archiveWarningMsgField->setValue($this->getArchiveWarningMessage($record)); // Build preview / live links - $liveLink = $record->getAbsoluteLiveLink(); + $liveLink = $record->getAbsoluteLiveLink(); // @TODO generalise!! if ($liveLink) { $liveLinkField->setValue($liveLink); } @@ -1437,10 +1424,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $form; } - public function EmptyForm() + public function EmptyForm(): Form { $fields = new FieldList( - new LabelField('PageDoesntExistLabel', _t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGENOTEXISTS', "This page doesn't exist")) + new LabelField('RecordDoesntExistLabel', _t(__CLASS__ . '.RECORDNOTEXISTS', "This record doesn't exist")) ); $form = parent::EmptyForm(); $form->setFields($fields); @@ -1449,39 +1436,32 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Build an archive warning message based on the page's children - * - * @param SiteTree $record - * @return string + * Build an archive warning message based on the record's children */ - /** - * Build an archive warning message based on the page's children - * - * @param SiteTree $record - * @return string - */ - protected function getArchiveWarningMessage($record) + protected function getArchiveWarningMessage(DataObject $record): string { - - $defaultMessage = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarningWithChildren', 'Warning: This page and all of its child pages will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?'); + $defaultMessage = _t( + LeftAndMain::class . '.ArchiveWarningWithChildren', + 'Warning: This record and all of its child records will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?' + ); // Option to disable this feature as it is slow on large sites - if (!$this->config()->enable_dynamic_archive_warning_message) { + if (!static::config()->get('enable_dynamic_archive_warning_message')) { return $defaultMessage; } - // Get all page's descendants + // Get all record's descendants $descendants = []; $this->collateDescendants([$record->ID], $descendants); if (!$descendants) { $descendants = []; } - // Get the IDs of all changeset including at least one of the pages. + // Get the IDs of all changeset including at least one of the records. $descendants[] = $record->ID; $inChangeSetIDs = ChangeSetItem::get()->filter([ 'ObjectID' => $descendants, - 'ObjectClass' => SiteTree::class + 'ObjectClass' => $this->getModelClass(), ])->column('ChangeSetID'); // Count number of affected change set @@ -1496,20 +1476,31 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $numCampaigns = mb_strtolower($numCampaigns ?? ''); if (count($descendants ?? []) > 0 && $affectedChangeSetCount > 0) { - $archiveWarningMsg = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarningWithChildrenAndCampaigns', 'Warning: This page and all of its child pages will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?', [ 'NumCampaigns' => $numCampaigns ]); + $archiveWarningMsg = _t( + LeftAndMain::class . '.ArchiveWarningWithChildrenAndCampaigns', + 'Warning: This record and all of its child records will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?', + [ 'NumCampaigns' => $numCampaigns ] + ); } elseif (count($descendants ?? []) > 0) { $archiveWarningMsg = $defaultMessage; } elseif ($affectedChangeSetCount > 0) { - $archiveWarningMsg = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarningWithCampaigns', 'Warning: This page will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?', [ 'NumCampaigns' => $numCampaigns ]); + $archiveWarningMsg = _t( + LeftAndMain::class . '.ArchiveWarningWithCampaigns', + 'Warning: This record will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?', + [ 'NumCampaigns' => $numCampaigns ] + ); } else { - $archiveWarningMsg = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarning', 'Warning: This page will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?'); + $archiveWarningMsg = _t( + LeftAndMain::class . '.ArchiveWarning', + 'Warning: This record will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?' + ); } return $archiveWarningMsg; } /** - * Find IDs of all descendant pages for the provided ID lists. + * Find IDs of all descendant records for the provided ID lists. * @param int[] $recordIDs * @param array $collator * @return bool @@ -1517,7 +1508,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr protected function collateDescendants($recordIDs, &$collator) { - $children = SiteTree::get()->filter(['ParentID' => $recordIDs])->column(); + $children = DataObject::get($this->getModelClass())->filter(['ParentID' => $recordIDs])->column(); if ($children) { foreach ($children as $item) { $collator[] = $item; @@ -1528,10 +1519,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return false; } - /** * This method exclusively handles deferred ajax requests to render the - * pages tree deferred handler (no pjax-fragment) + * records tree deferred handler (no pjax-fragment) * * @return DBHTMLText HTML response with the rendered treeview */ @@ -1569,21 +1559,21 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Callback to request the list of page types allowed under a given page instance. - * Provides a slower but more precise response over SiteTreeHints + * Callback to request the list of record types allowed under a given record instance. + * Provides a slower but more precise response over TreeHints */ public function childfilter(HTTPRequest $request): HTTPResponse { // Check valid parent specified $parentID = $request->requestVar('ParentID'); - $parent = SiteTree::get()->byID($parentID); + $parent = DataObject::get($this->getModelClass())->byID($parentID); if (!$parent || !$parent->exists()) { $this->httpError(404); } // Build hints specific to this class // Identify disallows and set globals - $classes = SiteTree::page_type_classes(); + $classes = SiteTree::page_type_classes(); // @TODO generalise!! $disallowedChildren = []; foreach ($classes as $class) { $obj = singleton($class); @@ -1623,8 +1613,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Returns the pages meet a certain criteria as {@see CMSSiteTreeFilter} or the subpages of a parent page - * defaulting to no filter and show all pages in first level. + * 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 @@ -1637,7 +1627,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr if ($filter = $this->getQueryFilter($params)) { return $filter->getFilteredPages(); } else { - $list = DataList::create($this->config()->get('tree_class')); + $list = DataObject::get($this->getModelClass()); $parentID = is_numeric($parentID) ? $parentID : 0; return $list->filter("ParentID", $parentID); } @@ -1658,7 +1648,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $gridFieldConfig = GridFieldConfig::create()->addComponents( Injector::inst()->create(GridFieldSortableHeader::class), Injector::inst()->create(GridFieldDataColumns::class), - Injector::inst()->createWithArgs(GridFieldPaginator::class, [$this->config()->get('page_length')]) + Injector::inst()->createWithArgs(GridFieldPaginator::class, [static::config()->get('page_length')]) ); if ($parentID) { $linkSpec = $this->LinkListViewChildren('%d'); @@ -1667,17 +1657,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr ->setLinkSpec($linkSpec) ->setAttributes(['data-pjax-target' => 'ListViewForm,Breadcrumbs']) ); - $this->setCurrentPageID($parentID); + $this->setCurrentRecordID($parentID); } - $gridField = GridField::create('Page', 'Pages', $list, $gridFieldConfig); + $gridField = GridField::create('Record', 'Records', $list, $gridFieldConfig); // @TODO make title string i18n $gridField->setAttribute('cms-loading-ignore-url-params', true); $columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class); // Don't allow navigating into children nodes on filtered lists + $modelClass = $this->getModelClass(); $fields = [ - 'getTreeTitle' => _t('SilverStripe\\CMS\\Model\\SiteTree.PAGETITLE', 'Page Title'), - 'i18n_singular_name' => _t('SilverStripe\\CMS\\Model\\SiteTree.PAGETYPE', 'Page Type'), - 'LastEdited' => _t('SilverStripe\\CMS\\Model\\SiteTree.LASTUPDATED', 'Last Updated'), + 'getTreeTitle' => _t($modelClass . '.TREETITLE', 'Title'), + 'i18n_singular_name' => _t($modelClass . '.TREETYPE', 'Record Type'), + 'LastEdited' => _t($modelClass . '.LASTUPDATED', 'Last Updated'), ]; $sortableHeader = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class); $sortableHeader->setFieldSorting(['getTreeTitle' => 'Title']); @@ -1696,24 +1687,24 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $columns->setFieldFormatting([ 'listChildrenLink' => function ($value, &$item) { - /** @var SiteTree $item */ - $num = $item ? $item->numChildren() : null; + /** @var DataObject&Hierarchy $item */ + $num = $item?->numChildren(); if ($num) { return sprintf( - '%s child pages', + '%s child pages', // @TODO generalise and i18n-ify $this->LinkListViewChildren((int)$item->ID), $num ); } }, 'getTreeTitle' => function ($value, &$item) { - /** @var SiteTree $item */ + /** @var DataObject $item */ $title = sprintf( '%s', $item->getCMSEditLink(), - $item->TreeTitle // returns HTML, does its own escaping + $item->TreeTitle // returns HTML, does its own escaping // @TODO generalise!! ); - $breadcrumbs = $item->Breadcrumbs(20, true, false, true, '/'); + $breadcrumbs = $item->Breadcrumbs(20, true, false, true, '/'); // @TODO generalise!! // Remove item's tile $breadcrumbs = preg_replace('/[^\/]+$/', '', trim($breadcrumbs ?? '')); // Trim spaces around delimiters @@ -1748,11 +1739,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $listview; } - public function currentPageID() + public function currentRecordID() { - $id = parent::currentPageID(); + $id = parent::currentRecordID(); - $this->extend('updateCurrentPageID', $id); + $this->extend('updateCurrentRecordID', $id); return $id; } @@ -1761,18 +1752,17 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Data saving handlers /** - * Save and Publish page handler + * Save and Publish record handler * * @throws HTTPResponse_Exception */ public function save(array $data, Form $form): HTTPResponse { - $className = $this->config()->get('tree_class'); + $className = $this->getModelClass(); // Existing or new record? $id = $data['ID']; if (substr($id ?? '', 0, 3) != 'new') { - /** @var SiteTree $record */ $record = DataObject::get_by_id($className, $id); // Check edit permissions if ($record && !$record->canEdit()) { @@ -1790,14 +1780,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Check publishing permissions $doPublish = !empty($data['publish']); - if ($record && $doPublish && !$record->canPublish()) { + $isVersioned = $record->hasExtension(Versioned::class); + if ($isVersioned && $doPublish && !$record->canPublish()) { return Security::permissionFailure($this); } - $record->HasBrokenLink = 0; - $record->HasBrokenFile = 0; + $record->HasBrokenLink = 0; // @TODO generalise!! + $record->HasBrokenFile = 0; // @TODO generalise!! - if (!$record->ObsoleteClassName) { + if ($isVersioned && !$record->ObsoleteClassName) { // @TODO I think that's specific to SiteTree?? $record->writeWithoutVersion(); } @@ -1812,19 +1803,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $form->saveInto($record); $record->write(); - // If the 'Publish' button was clicked, also publish the page + // If the 'Publish' button was clicked, also publish the record if ($doPublish) { + if (!$record->hasExtension(RecursivePublishable::class)) { + throw new HTTPResponse_Exception(get_class($record) . ' record is not publishable.', 400); + } $record->publishRecursive(); $message = _t( - __CLASS__ . '.PUBLISHED', - "Published '{title}' successfully.", - ['title' => $record->Title] + LeftAndMain::class . '.PUBLISHED_RECORD', + 'Published {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] ); } else { $message = _t( - __CLASS__ . '.SAVED', - "Saved '{title}' successfully.", - ['title' => $record->Title] + LeftAndMain::class . '.SAVED_RECORD', + 'Saved {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] ); } @@ -1835,12 +1835,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr /** * @param int|string $id * @param bool $setID - * @return mixed|DataObject * @throws HTTPResponse_Exception */ - public function getNewItem($id, $setID = true) + public function getNewItem($id, $setID = true): DataObject { - $parentClass = $this->config()->get('tree_class'); + $parentClass = $this->getModelClass(); list(, $className, $parentID) = array_pad(explode('-', $id ?? ''), 3, null); if (!is_a($className, $parentClass ?? '', true)) { @@ -1851,23 +1850,20 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr throw new HTTPResponse_Exception($response); } - /** @var SiteTree $newItem */ + /** @var DataObject $newItem */ $newItem = Injector::inst()->create($className); $newItem->Title = _t( - __CLASS__ . '.NEWPAGE', - "New {pagetype}", - 'followed by a page type title', - ['pagetype' => singleton($className)->i18n_singular_name()] + LeftAndMain::class . '.NEW_RECORD', + 'New {recordtype}', + ['recordtype' => DataObject::singleton($className)->i18n_singular_name()] ); $newItem->ClassName = $className; $newItem->ParentID = $parentID; - // DataObject::fieldExists only checks the current class, not the hierarchy - // This allows the CMS to set the correct sort value - if ($newItem->castingHelper('Sort')) { - $table = DataObject::singleton(SiteTree::class)->baseTable(); + if ($newItem->hasDatabaseField('Sort')) { // @TODO Let the field name be configurable + $table = DataObject::singleton($parentClass)->baseTable(); $maxSort = DB::prepared_query( - "SELECT MAX(\"Sort\") FROM \"$table\" WHERE \"ParentID\" = ?", + "SELECT MAX(\"Sort\") FROM \"$table\" WHERE \"ParentID\" = ?", // @TODO this won't work if sort is on a subclass! Use ORM instead [$parentID] )->value(); $newItem->Sort = (int)$maxSort + 1; @@ -1878,55 +1874,43 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } # Some modules like subsites add extra fields that need to be set when the new item is created - $this->extend('augmentNewSiteTreeItem', $newItem); + $this->extend('updateNewItem', $newItem); // @TODO rename return $newItem; } /** - * Actually perform the publication step - * - * @param Versioned|DataObject $record - * @return mixed - */ - public function performPublish($record) - { - if ($record && !$record->canPublish()) { - return Security::permissionFailure($this); - } - - $record->publishRecursive(); - } - - /** - * Reverts a page by publishing it to live. - * Use {@link restorepage()} if you want to restore a page + * Reverts a record by publishing it to live. + * Use {@link restoreRecord()} if you want to restore a record * which was deleted from draft without publishing. * - * @uses SiteTree->doRevertToLive() - * * @throws HTTPResponse_Exception */ public function revert(array $data, Form $form): HTTPResponse { + $modelClass = $this->getModelClass(); if (!isset($data['ID'])) { throw new HTTPResponse_Exception("Please pass an ID in the form content", 400); } - $id = (int) $data['ID']; - $restoredPage = Versioned::get_latest_version(SiteTree::class, $id); - if (!$restoredPage) { - throw new HTTPResponse_Exception("SiteTree #$id not found", 400); + if (!$modelClass::has_extension(Versioned::class)) { + throw new HTTPResponse_Exception("$modelClass record cannot be reverted", 400); } - $table = DataObject::singleton(SiteTree::class)->baseTable(); - $liveTable = DataObject::singleton(SiteTree::class)->stageTable($table, Versioned::LIVE); - $record = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [ + $id = (int) $data['ID']; + $restoredRecord = Versioned::get_latest_version($modelClass, $id); + if (!$restoredRecord) { + throw new HTTPResponse_Exception("Record #$id not found", 400); + } + + $table = DataObject::singleton($modelClass)->baseTable(); + $liveTable = DataObject::singleton($modelClass)->stageTable($table, Versioned::LIVE); + $record = Versioned::get_one_by_stage($modelClass, Versioned::LIVE, [ "\"$liveTable\".\"ID\"" => $id ]); - // a user can restore a page without publication rights, as it just adds a new draft state - // (this action should just be available when page has been "deleted from draft") + // a user can restore a record without publication rights, as it just adds a new draft state + // (this action should just be available when the record has been "deleted from draft") if ($record && !$record->canEdit()) { return Security::permissionFailure($this); } @@ -1939,27 +1923,27 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $this->getResponse()->addHeader( 'X-Status', rawurlencode(_t( - __CLASS__ . '.RESTORED', - "Restored '{title}' successfully", - 'Param {title} is a title', - ['title' => $record->Title] - ) ?? '') + LeftAndMain::class . '.RESTORED_RECORD', + 'Restored {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] + )) ); return $this->getResponseNegotiator()->respond($this->getRequest()); } /** - * Delete the current page from draft stage. - * - * @see deletefromlive() + * Delete the current record from draft stage. * * @throws HTTPResponse_Exception */ public function delete(array $data, Form $form): HTTPResponse { $id = $data['ID']; - $record = SiteTree::get()->byID($id); + $record = DataObject::get($this->getModelClass())->byID($id); if ($record && !$record->canDelete()) { return Security::permissionFailure(); } @@ -1970,13 +1954,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr // Delete record $record->delete(); + if ($record->hasExtension(Versioned::class) && $record->hasStages()) { + $message = _t( + LeftAndMain::class . '.ARCHIVED_RECORD', + 'Archived {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] + ); + } else { + $message = _t( + LeftAndMain::class . '.DELETED_RECORD', + 'Deleted {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] + ); + } $this->getResponse()->addHeader( 'X-Status', - rawurlencode(_t( - __CLASS__ . '.REMOVEDPAGEFROMDRAFT', - "Removed '{title}' from the draft site", - ['title' => $record->Title] - ) ?? '') + rawurlencode($message) ); // Even if the record has been deleted from stage and live, it can be viewed in "archive mode" @@ -1984,14 +1983,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Delete this page from both live and stage + * Delete this record from both live and stage * * @throws HTTPResponse_Exception */ public function archive(array $data, Form $form): HTTPResponse { $id = $data['ID']; - $record = SiteTree::get()->byID($id); + $record = DataObject::get($this->getModelClass())->byID($id); if (!$record || !$record->exists()) { throw new HTTPResponse_Exception("Bad record ID #$id", 404); } @@ -2005,10 +2004,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $this->getResponse()->addHeader( 'X-Status', rawurlencode(_t( - __CLASS__ . '.ARCHIVEDPAGE', - "Archived page '{title}'", - ['title' => $record->Title] - ) ?? '') + LeftAndMain::class . '.ARCHIVED_RECORD', + 'Archived {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] + )) ); // Even if the record has been deleted from stage and live, it can be viewed in "archive mode" @@ -2024,10 +2026,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr public function unpublish(array $data, Form $form): HTTPResponse { - $className = $this->config()->get('tree_class'); - /** @var SiteTree $record */ + $className = $this->getModelClass(); $record = DataObject::get_by_id($className, $data['ID']); + if (!$record->hasExtension(Versioned::class)) { + throw new HTTPResponse_Exception(get_class($record) . ' record cannot be unpublished.', 400); + } + if ($record && !$record->canUnpublish()) { return Security::permissionFailure($this); } @@ -2040,10 +2045,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $this->getResponse()->addHeader( 'X-Status', rawurlencode(_t( - __CLASS__ . '.REMOVEDPAGE', - "Removed '{title}' from the published site", - ['title' => $record->Title] - ) ?? '') + LeftAndMain::class . '.UNPUBLISHED_RECORD', + 'Unpublished {name} "{title}"', + [ + 'name' => $record->i18n_singular_name(), + 'title' => $record->Title, + ] + )) ); return $this->getResponseNegotiator()->respond($this->getRequest()); @@ -2055,7 +2063,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr public function rollback() { return $this->doRollback([ - 'ID' => $this->currentPageID(), + 'ID' => $this->currentRecordID(), 'Version' => $this->getRequest()->param('VersionID') ], null); } @@ -2074,8 +2082,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $id = (isset($data['ID'])) ? (int) $data['ID'] : null; $version = (isset($data['Version'])) ? (int) $data['Version'] : null; - /** @var SiteTree|Versioned $record */ - $record = Versioned::get_latest_version($this->config()->get('tree_class'), $id); + $modelClass = $this->getModelClass(); + if (!$modelClass::has_extension(Versioned::class)) { + throw new HTTPResponse_Exception("$modelClass record cannot be rolled back", 400); + } + + /** @var DataObject&Versioned $record */ + $record = Versioned::get_latest_version($modelClass, $id); if ($record && !$record->canEdit()) { return Security::permissionFailure($this); } @@ -2083,22 +2096,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr if ($version) { $record->rollbackRecursive($version); $message = _t( - __CLASS__ . '.ROLLEDBACKVERSIONv2', - "Rolled back to version #{version}.", + LeftAndMain::class . '.ROLLEDBACK_VERSION', + 'Rolled back to version #{version}.', ['version' => $data['Version']] ); } else { $record->doRevertToLive(); $record->publishRecursive(); $message = _t( - __CLASS__ . '.ROLLEDBACKPUBv2', - "Rolled back to published version." + LeftAndMain::class . '.ROLLEDBACK_PUBLISHED', + 'Rolled back to published version.' ); } $this->getResponse()->addHeader('X-Status', rawurlencode($message ?? '')); - // Can be used in different contexts: In normal page edit view, in which case the redirect won't have any effect. + // Can be used in different contexts: In normal record edit view, in which case the redirect won't have any effect. // Or in history view, in which case a revert causes the CMS to re-load the edit view. // The X-Pjax header forces a "full" content refresh on redirect. $url = $record->getCMSEditLink(); @@ -2142,11 +2155,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $forms[$urlSegment] = $formHtml; } } - $pageHtml = ''; + $recordHtml = ''; foreach ($forms as $urlSegment => $html) { - $pageHtml .= ''; + $recordHtml .= ''; } - return new LiteralField('BatchActionParameters', ''); + return new LiteralField('BatchActionParameters', ''); } /** @@ -2158,7 +2171,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Restore a completely deleted page from the SiteTree_versions table. + * Restore a completely deleted record from the *_versions table. */ public function restore(array $data, Form $form): HTTPResponse { @@ -2166,21 +2179,29 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return new HTTPResponse("Please pass an ID in the form content", 400); } - $id = (int)$data['ID']; - $restoredPage = Versioned::get_latest_version(SiteTree::class, $id); - if (!$restoredPage) { - return new HTTPResponse("SiteTree #$id not found", 400); + $modelClass = $this->getModelClass(); + if (!$modelClass::has_extension(Versioned::class)) { + throw new HTTPResponse_Exception("$modelClass record cannot be restored", 400); } - $restoredPage = $restoredPage->doRestoreToStage(); + $id = (int)$data['ID']; + $restoredRecord = Versioned::get_latest_version($modelClass, $id); + if (!$restoredRecord) { + return new HTTPResponse("Record #$id not found", 400); + } + + $restoredRecord = $restoredRecord->doRestoreToStage(); $this->getResponse()->addHeader( 'X-Status', rawurlencode(_t( - __CLASS__ . '.RESTORED', - "Restored '{title}' successfully", - ['title' => $restoredPage->Title] - ) ?? '') + LeftAndMain::class . '.RESTORED_RECORD', + 'Restored {name} "{title}"', + [ + 'name' => $restoredRecord->i18n_singular_name(), + 'title' => $restoredRecord->Title, + ] + )) ); return $this->getResponseNegotiator()->respond($this->getRequest()); @@ -2194,31 +2215,34 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } if (($id = $this->urlParams['ID']) && is_numeric($id)) { - $page = SiteTree::get()->byID($id); - if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) { + $record = DataObject::get($this->getModelClass())->byID($id); + if ($record && !$record->canCreate(null, ['Parent' => $record->Parent()])) { return Security::permissionFailure($this); } - if (!$page || !$page->ID) { + if (!$record || !$record->ID) { throw new HTTPResponse_Exception("Bad record ID #$id", 404); } - $newPage = $page->duplicate(); + $newRecord = $record->duplicate(); // ParentID can be hard-set in the URL. This is useful for pages with multiple parents if (isset($_GET['parentID']) && is_numeric($_GET['parentID'])) { - $newPage->ParentID = $_GET['parentID']; - $newPage->write(); + $newRecord->ParentID = $_GET['parentID']; + $newRecord->write(); } $this->getResponse()->addHeader( 'X-Status', rawurlencode(_t( - __CLASS__ . '.DUPLICATED', - "Duplicated '{title}' successfully", - ['title' => $newPage->Title] - ) ?? '') + LeftAndMain::class . '.DUPLICATED_RECORD', + 'Duplicated {name} "{title}"', + [ + 'name' => $newRecord->i18n_singular_name(), + 'title' => $newRecord->Title, + ] + )) ); - $url = $newPage->getCMSEditLink(); + $url = $newRecord->getCMSEditLink(); $this->getResponse()->addHeader('X-ControllerURL', $url); $this->getRequest()->addHeader('X-Pjax', 'Content'); $this->getResponse()->addHeader('X-Pjax', 'Content'); @@ -2236,25 +2260,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } Environment::increaseTimeLimitTo(); if (($id = $this->urlParams['ID']) && is_numeric($id)) { - $page = SiteTree::get()->byID($id); - if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) { + $record = DataObject::get($this->getModelClass())->byID($id); + if ($record && !$record->canCreate(null, ['Parent' => $record->Parent()])) { return Security::permissionFailure($this); } - if (!$page || !$page->ID) { + if (!$record || !$record->ID) { throw new HTTPResponse_Exception("Bad record ID #$id", 404); } - $newPage = $page->duplicateWithChildren(); + $newRecord = $record->duplicateWithChildren(); $this->getResponse()->addHeader( 'X-Status', rawurlencode(_t( - __CLASS__ . '.DUPLICATEDWITHCHILDREN', - "Duplicated '{title}' and children successfully", - ['title' => $newPage->Title] + LeftAndMain::class . '.DUPLICATED_RECORD_WITH_CHILDREN', + 'Duplicated {name} "{title}" and children', + [ + 'name' => $newRecord->i18n_singular_name(), + 'title' => $newRecord->Title, + ] ) ?? '') ); - $url = $newPage->getCMSEditLink(); + $url = $newRecord->getCMSEditLink(); $this->getResponse()->addHeader('X-ControllerURL', $url); $this->getRequest()->addHeader('X-Pjax', 'Content'); $this->getResponse()->addHeader('X-Pjax', 'Content'); @@ -2269,8 +2296,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $title = CMSPagesController::menu_title(); return [ "CMS_ACCESS_CMSMain" => [ - 'name' => _t(__CLASS__ . '.ACCESS', "Access to '{title}' section", ['title' => $title]), - 'category' => _t('SilverStripe\\Security\\Permission.CMS_ACCESS_CATEGORY', 'CMS Access'), + 'name' => _t(LeftAndMain::class . '.ACCESS', "Access to '{title}' section", ['title' => $title]), + 'category' => _t(LeftAndMain::class . '.CMS_ACCESS_CATEGORY', 'CMS Access'), 'help' => _t( __CLASS__ . '.ACCESS_HELP', 'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".' @@ -2293,7 +2320,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr } /** - * Cache key for SiteTreeHints() method + * Cache key for TreeHints() method * * @param $memberID * @return string diff --git a/code/Controllers/CMSPageEditController.php b/code/Controllers/CMSPageEditController.php index 271d5929..36f8e567 100644 --- a/code/Controllers/CMSPageEditController.php +++ b/code/Controllers/CMSPageEditController.php @@ -2,17 +2,15 @@ namespace SilverStripe\CMS\Controllers; -use Page; use SilverStripe\Admin\LeftAndMain; use SilverStripe\CampaignAdmin\AddToCampaignHandler; -use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Control\Controller; use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPResponse; use SilverStripe\Forms\Form; use SilverStripe\Core\ArrayLib; -use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\Core\Validation\ValidationResult; +use SilverStripe\ORM\DataObject; /** * @package cms @@ -57,7 +55,7 @@ class CMSPageEditController extends CMSMain public function addtocampaign(array $data, Form $form): HTTPResponse { $id = $data['ID']; - $record = \Page::get()->byID($id); + $record = DataObject::get($this->getModelClass())->byID($id); $handler = AddToCampaignHandler::create($this, $record); $response = $handler->addToCampaign($record, $data); @@ -95,15 +93,16 @@ class CMSPageEditController extends CMSMain */ public function getAddToCampaignForm($id) { + $modelClass = $this->getModelClass(); // Get record-specific fields - $record = SiteTree::get()->byID($id); + $record = DataObject::get($modelClass)->byID($id); if (!$record) { $this->httpError(404, _t( __CLASS__ . '.ErrorNotFound', 'That {Type} couldn\'t be found', '', - ['Type' => Page::singleton()->i18n_singular_name()] + ['Type' => DataObject::singleton($modelClass)->i18n_singular_name()] )); return null; } @@ -112,7 +111,7 @@ class CMSPageEditController extends CMSMain __CLASS__.'.ErrorItemPermissionDenied', 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to a campaign', '', - ['ObjectTitle' => Page::singleton()->i18n_singular_name()] + ['ObjectTitle' => DataObject::singleton($modelClass)->i18n_singular_name()] )); return null; } diff --git a/code/Controllers/CMSPageSettingsController.php b/code/Controllers/CMSPageSettingsController.php index 34353c7c..68f987bf 100644 --- a/code/Controllers/CMSPageSettingsController.php +++ b/code/Controllers/CMSPageSettingsController.php @@ -2,6 +2,7 @@ namespace SilverStripe\CMS\Controllers; +use SilverStripe\Forms\Form; use SilverStripe\Model\ArrayData; class CMSPageSettingsController extends CMSMain @@ -15,11 +16,19 @@ class CMSPageSettingsController extends CMSMain private static $required_permission_codes = 'CMS_ACCESS_CMSMain'; - public function getEditForm($id = null, $fields = null) + public function getEditForm($id = null, $fields = null): Form { - $record = $this->getRecord($id ?: $this->currentPageID()); + $record = $this->getRecord($id ?: $this->currentRecordID()); - return parent::getEditForm($id, ($record) ? $record->getSettingsFields() : null); + // @TODO ideally settings isn't its own special thing... + // can we refactor this so it's just another tab in the main form? And just have it lazyload or something? + // At the very least this tab must NOT appear if there are no fields for it. + if ($record && $record->hasMethod('getSettingsFields')) { + $fields = $record->getSettingsFields(); + } else { + $fields = null; + } + return parent::getEditForm($id, $fields); } public function getTabIdentifier() diff --git a/code/Controllers/CMSPagesController.php b/code/Controllers/CMSPagesController.php index 98e6c630..78cbfb00 100644 --- a/code/Controllers/CMSPagesController.php +++ b/code/Controllers/CMSPagesController.php @@ -2,16 +2,11 @@ namespace SilverStripe\CMS\Controllers; -use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\Control\Controller; -use SilverStripe\Model\List\ArrayList; use SilverStripe\ORM\DataObject; -use SilverStripe\Model\ArrayData; -use stdClass; +// @TODO What a pointless class!!!!!! class CMSPagesController extends CMSMain { - private static $url_segment = 'pages'; private static $url_rule = '/$Action/$ID/$OtherID'; @@ -27,7 +22,7 @@ class CMSPagesController extends CMSMain return false; } - public function isCurrentPage(DataObject $record) + public function isCurrentRecord(DataObject $record) { return false; } diff --git a/code/Controllers/CMSSiteTreeFilter.php b/code/Controllers/CMSSiteTreeFilter.php index d2c4d57c..22fb2162 100644 --- a/code/Controllers/CMSSiteTreeFilter.php +++ b/code/Controllers/CMSSiteTreeFilter.php @@ -113,7 +113,7 @@ abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter return $this->numChildrenMethod; } - public function getPageClasses($page) + public function getRecordClasses($page) { if ($this->_cache_ids === null) { $this->populateIDs(); @@ -178,7 +178,7 @@ abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter } } - public function isPageIncluded($page) + public function isRecordIncluded($page) { if ($this->_cache_ids === null) { $this->populateIDs(); diff --git a/code/Controllers/LeftAndMainPageIconsExtension.php b/code/Controllers/LeftAndMainPageIconsExtension.php index ed0e7e69..b54c239c 100644 --- a/code/Controllers/LeftAndMainPageIconsExtension.php +++ b/code/Controllers/LeftAndMainPageIconsExtension.php @@ -18,6 +18,8 @@ use SilverStripe\View\Requirements; /** * Extension to include custom page icons * + * @TODO AAAHHHHHHHHHHH + * * @extends Extension */ class LeftAndMainPageIconsExtension extends Extension implements Flushable diff --git a/code/Controllers/CMSPageAddController.php b/code/Forms/CMSMainAddForm.php similarity index 60% rename from code/Controllers/CMSPageAddController.php rename to code/Forms/CMSMainAddForm.php index 37f1e221..d796b42f 100644 --- a/code/Controllers/CMSPageAddController.php +++ b/code/Forms/CMSMainAddForm.php @@ -1,10 +1,10 @@ getModelClass(); $pageTypes = []; - $defaultIcon = Config::inst()->get(SiteTree::class, 'icon_class'); + $defaultIcon = Config::inst()->get($modelClass, 'icon_class'); // @TODO need a better place for default - maybe try default on class, and fallback to default on cmsmain? - foreach ($this->PageTypes() as $type) { + foreach ($controller->RecordTypes() as $type) { $class = $type->getField('ClassName'); $icon = Config::inst()->get($class, 'icon_class') ?: $defaultIcon; - // If the icon is the SiteTree default and there's some specific icon being provided by `getPageIconURL` + // If the icon is the default and there's some specific icon being provided by `getPageIconURL` // then we don't need to add the icon class. Otherwise the class take precedence. if ($icon === $defaultIcon && !empty(singleton($class)->getPageIconURL())) { $icon = ''; @@ -73,24 +56,28 @@ class CMSPageAddController extends CMSPageEditController $numericLabelTmpl = 'Step %d. %s'; - $topTitle = _t('SilverStripe\\CMS\\Controllers\\CMSPageAddController.ParentMode_top', 'Top level'); - $childTitle = _t('SilverStripe\\CMS\\Controllers\\CMSPageAddController.ParentMode_child', 'Under another page'); + $topTitle = _t(__CLASS__ . '.ParentMode_top', 'Top level'); + $childTitle = _t( + __CLASS__ . '.ParentMode_child', + 'Under another {type}', + ['type' => mb_strtolower(DataObject::singleton($modelClass)->i18n_singular_name())] + ); - $fields = new FieldList( - $parentModeField = new SelectionGroup( - "ParentModeField", + $fields = FieldList::create( + $parentModeField = SelectionGroup::create( + 'ParentModeField', [ - $topField = new SelectionGroup_Item( - "top", + $topField = SelectionGroup_Item::create( + 'top', null, $topTitle ), - new SelectionGroup_Item( + SelectionGroup_Item::create( 'child', - $parentField = new TreeDropdownField( - "ParentID", - "", - SiteTree::class, + $parentField = TreeDropdownField::create( + 'ParentID', + '', + $modelClass, 'ID', 'TreeTitle' ), @@ -98,7 +85,7 @@ class CMSPageAddController extends CMSPageEditController ) ] ), - new LiteralField( + LiteralField::create( 'RestrictedNote', sprintf( '

%s

', @@ -108,8 +95,8 @@ class CMSPageAddController extends CMSPageEditController ) ) ), - $typeField = new OptionsetField( - "PageType", + OptionsetField::create( + 'PageType', DBField::create_field( 'HTMLFragment', sprintf($numericLabelTmpl ?? '', 2, _t('SilverStripe\\CMS\\Controllers\\CMSMain.ChoosePageType', 'Choose page type')) @@ -134,9 +121,9 @@ class CMSPageAddController extends CMSPageEditController $parentModeField->addExtraClass('parent-mode'); - // CMSMain->currentPageID() automatically sets the homepage, + // CMSMain->currentRecordID() automatically sets the homepage, // which we need to counteract in the default selection (which should default to root, ID=0) - if ($parentID = $this->getRequest()->getVar('ParentID')) { + if ($parentID = $controller->getRequest()->getVar('ParentID')) { $parentModeField->setValue('child'); $parentField->setValue((int)$parentID); } else { @@ -145,35 +132,32 @@ class CMSPageAddController extends CMSPageEditController // Check if the current user has enough permissions to create top level pages // If not, then disable the option to do that - if (!SiteConfig::current_site_config()->canCreateTopLevel()) { + if (is_a($modelClass, SiteTree::class, true) && !SiteConfig::current_site_config()->canCreateTopLevel()) { // @TODO probably need to make this generic $topField->setDisabled(true); $parentModeField->setValue('child'); } - $actions = new FieldList( - FormAction::create("doAdd", _t('SilverStripe\\CMS\\Controllers\\CMSMain.Create', "Create")) + $actions = FieldList::create( + FormAction::create('doAdd', _t('SilverStripe\\CMS\\Controllers\\CMSMain.Create', 'Create')) ->addExtraClass('btn-primary font-icon-plus-circled') ->setUseButtonTag(true), - FormAction::create("doCancel", _t('SilverStripe\\CMS\\Controllers\\CMSMain.Cancel', "Cancel")) + FormAction::create('doCancel', _t('SilverStripe\\CMS\\Controllers\\CMSMain.Cancel', 'Cancel')) ->addExtraClass('btn-secondary') ->setUseButtonTag(true) ); - $this->extend('updatePageOptions', $fields); + $controller->extend('updatePageOptions', $fields); - $negotiator = $this->getResponseNegotiator(); - $form = Form::create( - $this, - "AddForm", - $fields, - $actions - )->setHTMLID('Form_AddForm')->setStrictFormMethodCheck(false); - $form->setAttribute('data-hints', $this->SiteTreeHints()); - $form->setAttribute('data-childfilter', $this->Link('childfilter')); - $form->setValidationResponseCallback(function (ValidationResult $errors) use ($negotiator, $form) { - $request = $this->getRequest(); + parent::__construct($controller, 'AddForm', $fields, $actions); + + $negotiator = $controller->getResponseNegotiator(); + $this->setHTMLID('Form_AddForm')->setStrictFormMethodCheck(false); + $this->setAttribute('data-hints', $controller->TreeHints()); + $this->setAttribute('data-childfilter', $controller->Link('childfilter')); + $this->setValidationResponseCallback(function () use ($negotiator, $controller) { + $request = $controller->getRequest(); if ($request->isAjax() && $negotiator) { - $result = $form->forTemplate(); + $result = $this->forTemplate(); return $negotiator->respond($request, [ 'CurrentForm' => function () use ($result) { return $result; @@ -182,26 +166,26 @@ class CMSPageAddController extends CMSPageEditController } return null; }); - $form->addExtraClass('flexbox-area-grow fill-height cms-add-form cms-content cms-edit-form ' . $this->BaseCSSClasses()); - $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); - - return $form; + $this->addExtraClass('flexbox-area-grow fill-height cms-add-form cms-content cms-edit-form ' . $controller->BaseCSSClasses()); + $this->setTemplate($controller->getTemplatesWithSuffix('_AddForm')); } public function doAdd(array $data, Form $form): HTTPResponse { + $controller = $this->getController(); + $modelClass = $controller->getModelClass(); $className = isset($data['PageType']) ? $data['PageType'] : "Page"; $parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0; if (!$parentID && isset($data['Parent'])) { - $page = SiteTree::get_by_link($data['Parent']); + $page = $modelClass::get_by_link($data['Parent']); // @TODO Obviously no good if ($page) { $parentID = $page->ID; } } if (is_numeric($parentID) && $parentID > 0) { - $parentObj = SiteTree::get()->byID($parentID); + $parentObj = DataObject::get($modelClass)->byID($parentID); } else { $parentObj = null; } @@ -211,16 +195,16 @@ class CMSPageAddController extends CMSPageEditController } if (!singleton($className)->canCreate(Security::getCurrentUser(), ['Parent' => $parentObj])) { - return Security::permissionFailure($this); + return Security::permissionFailure($controller); } - $record = $this->getNewItem("new-$className-$parentID", false); - $this->extend('updateDoAdd', $record, $form); + $record = $controller->getNewItem("new-$className-$parentID", false); + $controller->extend('updateDoAdd', $record, $form); $record->write(); $editController = CMSPageEditController::singleton(); - $editController->setRequest($this->getRequest()); - $editController->setCurrentPageID($record->ID); + $editController->setRequest($controller->getRequest()); + $editController->setCurrentRecordID($record->ID); $session = $this->getRequest()->getSession(); $session->set( @@ -229,11 +213,11 @@ class CMSPageAddController extends CMSPageEditController ); $session->set("FormInfo.Form_EditForm.formError.type", 'good'); - return $this->redirect(Controller::join_links($editController->Link('show'), $record->ID)); + return $controller->redirect($editController->Link('show/' . $record->ID)); } - public function doCancel(array $data, Form $form): HTTPResponse + public function doCancel(): HTTPResponse { - return $this->redirect(CMSMain::singleton()->Link()); + return $this->getController()->redirect(CMSMain::singleton()->Link()); // @TODO when there's no CMSPageEditController anymore, change this to $this->getController()->Link() } } diff --git a/code/Model/CurrentPageIdentifier.php b/code/Model/CurrentRecordIdentifier.php similarity index 75% rename from code/Model/CurrentPageIdentifier.php rename to code/Model/CurrentRecordIdentifier.php index bee76b82..9c004fa7 100644 --- a/code/Model/CurrentPageIdentifier.php +++ b/code/Model/CurrentRecordIdentifier.php @@ -7,19 +7,19 @@ use SilverStripe\ORM\DataObject; /** * This interface lets us set up objects that will tell us what the current page is. */ -interface CurrentPageIdentifier +interface CurrentRecordIdentifier { /** * Get the current page ID. * @return int */ - public function currentPageID(); + public function currentRecordID(); /** * Check if the given DataObject is the current page. * @param DataObject $page The page to check. * @return boolean */ - public function isCurrentPage(DataObject $page); + public function isCurrentRecord(DataObject $page); } diff --git a/code/Model/SiteTree.php b/code/Model/SiteTree.php index 2ee8e6b1..3325e5d6 100755 --- a/code/Model/SiteTree.php +++ b/code/Model/SiteTree.php @@ -783,12 +783,12 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi */ public function isCurrent() { - $currentPage = Director::get_current_page(); - if ($currentPage instanceof ContentController) { - $currentPage = $currentPage->data(); + $currentRecord = Director::get_current_page(); + if ($currentRecord instanceof ContentController) { + $currentRecord = $currentRecord->data(); } - if ($currentPage instanceof SiteTree) { - return $currentPage === $this || $currentPage->ID === $this->ID; + if ($currentRecord instanceof SiteTree) { + return $currentRecord === $this || $currentRecord->ID === $this->ID; } return false; } @@ -2778,9 +2778,9 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi // Sort alphabetically, and put current on top asort($result); if (isset($result[$this->ClassName])) { - $currentPageTypeName = $result[$this->ClassName]; + $currentRecordTypeName = $result[$this->ClassName]; unset($result[$this->ClassName]); - $result = [$this->ClassName => $currentPageTypeName] + $result; + $result = [$this->ClassName => $currentRecordTypeName] + $result; } return $result; diff --git a/lang/en.yml b/lang/en.yml index 3c012c9c..95b78778 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -256,6 +256,8 @@ en: TABCONTENT: 'Main content' TABDEPENDENT: 'Dependent pages' TOPLEVEL: 'Site Content (Top Level)' + TREETITLE: 'Page name' + TREETYPE: 'Page type' UNTITLED: 'Untitled {pagetype}' URLSegment: 'URL segment' UntitledDependentObject: 'Untitled {instanceType}' diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_AddForm.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_AddForm.ss new file mode 100644 index 00000000..707848d4 --- /dev/null +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_AddForm.ss @@ -0,0 +1,47 @@ +
+
+
+
+ +
+
+ +
+ <% if $Message %> +

$Message

+ <% else %> + + <% end_if %> + +
+ <% if $Legend %>$Legend<% end_if %> + <% loop $Fields %> + $FieldHolder + <% end_loop %> +
+
+ +
+ <% if $Actions %> +
+ <% loop $Actions %> + $Field + <% end_loop %> +
+ <% end_if %> +
+
+
diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Content.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Content.ss index 0dbb1653..60018848 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Content.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Content.ss @@ -11,17 +11,17 @@

$SiteConfig.Title

diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss index d28b1e84..aa79a7a9 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss @@ -1,6 +1,6 @@ <% include SilverStripe\\CMS\\Controllers\\CMSPagesController_ContentToolActions View='Tree' %> -
+
$AddForm
@@ -17,15 +17,15 @@ $ExtraTreeTools data-url-tree="$LinkWithSearch($Link('getsubtree')).ATT" data-url-savetreenode="$Link('savetreenode').ATT" data-url-updatetreenodes="$Link('updatetreenodes').ATT" - data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1', 'ParentID=%s&PageType=%s').ATT}" - data-url-editpage="$LinkPageEdit('%s').ATT" + data-url-addpage="{$LinkRecordAdd('AddForm/?action_doAdd=1', 'ParentID=%s&RecordType=%s').ATT}" + data-url-editpage="$LinkRecordEdit('%s').ATT" data-url-duplicate="{$Link('duplicate/%s').ATT}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s').ATT}" data-url-listview="{$Link('?view=list').ATT}" - data-hints="$SiteTreeHints.ATT" + data-hints="$TreeHints.ATT" data-childfilter="$Link('childfilter').ATT" data-extra-params="SecurityID=$SecurityID.ATT"> - $SiteTreeAsUL + $TreeAsUL
<% else %> @@ -33,14 +33,14 @@ $ExtraTreeTools data-url-tree="$LinkWithSearch($Link('getsubtree')).ATT" data-url-savetreenode="$Link('savetreenode').ATT" data-url-updatetreenodes="$Link('updatetreenodes').ATT" - data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1', 'ParentID=%s&PageType=%s').ATT}" - data-url-editpage="$LinkPageEdit('%s').ATT" + data-url-addpage="{$LinkRecordAdd('AddForm/?action_doAdd=1', 'ParentID=%s&RecordType=%s').ATT}" + data-url-editpage="$LinkRecordEdit('%s').ATT" data-url-duplicate="{$Link('duplicate/%s').ATT}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s').ATT}" data-url-listview="{$Link('?view=list').ATT}" - data-hints="$SiteTreeHints.ATT" + data-hints="$TreeHints.ATT" data-childfilter="$Link('childfilter').ATT" data-extra-params="SecurityID=$SecurityID.ATT"> - $SiteTreeAsUL + $TreeAsUL <% end_if %> diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss index c9709e9a..410f885e 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss @@ -1,6 +1,6 @@
<% if not $TreeIsFiltered %> - <%-- Change to data-pjax-target="Content-PageList" to enable in-edit listview --%> + <%-- Change to data-pjax-target="Content-RecordList" to enable in-edit listview --%> - <% with $AddForm %> -
-
-
- -
-
- -
- <% if $Message %> -

$Message

- <% else %> - - <% end_if %> - -
- <% if $Legend %>$Legend<% end_if %> - <% loop $Fields %> - $FieldHolder - <% end_loop %> -
-
- -
- <% if $Actions %> -
- <% loop $Actions %> - $Field - <% end_loop %> -
- <% end_if %> -
-
- <% end_with %> -
diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss index 956e7536..c19e5e90 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss @@ -13,6 +13,6 @@
$Tools - $PageList + $RecordList
diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_ContentToolActions.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_ContentToolActions.ss index 501a5647..aa43b943 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_ContentToolActions.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_ContentToolActions.ss @@ -1,7 +1,9 @@
<% if not $TreeIsFiltered %> - <%t SilverStripe\CMS\Controllers\CMSMain.AddNewButton 'Add new' %> + + <%t SilverStripe\Admin\\LeftAndMain.AddNew 'Add new {name}' name=$getRecord('singleton').i18n_singular_name().lowercase %> + <% if $View == 'Tree' %>
diff --git a/tests/php/Controllers/CMSMainTest.php b/tests/php/Controllers/CMSMainTest.php index 205166b4..481ba660 100644 --- a/tests/php/Controllers/CMSMainTest.php +++ b/tests/php/Controllers/CMSMainTest.php @@ -44,15 +44,15 @@ class CMSMainTest extends FunctionalTest } } - public function testSiteTreeHints() + public function testTreeHints() { - $cache = Injector::inst()->get(CacheInterface::class . '.CMSMain_SiteTreeHints'); + $cache = Injector::inst()->get(CacheInterface::class . '.CMSMain_TreeHints'); // Login as user with root creation privileges $user = $this->objFromFixture(Member::class, 'rootedituser'); Security::setCurrentUser($user); $cache->clear(); - $rawHints = singleton(CMSMain::class)->SiteTreeHints(); + $rawHints = singleton(CMSMain::class)->TreeHints(); $this->assertNotNull($rawHints); $rawHints = preg_replace('/^"(.*)"$/', '$1', Convert::xml2raw($rawHints) ?? ''); @@ -423,7 +423,7 @@ class CMSMainTest extends FunctionalTest ); $jsonStr = $response->getBody(); $data = json_decode($jsonStr, true); - + $parser = new CSSContentParser($data['Breadcrumbs']); $crumbs = $parser->getBySelector('.breadcrumbs-wrapper .crumb'); @@ -611,7 +611,7 @@ class CMSMainTest extends FunctionalTest $this->assertEquals('Class A', $newPage->Title); } - public function testSiteTreeHintsCache() + public function testTreeHintsCache() { $cms = CMSMain::create(); /** @var Member $user */ @@ -635,31 +635,31 @@ class CMSMainTest extends FunctionalTest // Initially, cache misses (1) Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Now it hits Injector::inst()->registerService($mockPageHitsCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Mutating member record invalidates cache. Misses (2) $user->FirstName = 'changed'; $user->write(); Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Now it hits again Injector::inst()->registerService($mockPageHitsCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Different user. Misses. (3) $user = $this->objFromFixture(Member::class, 'allcmssectionsuser'); Security::setCurrentUser($user); Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); } @@ -703,21 +703,21 @@ class CMSMainTest extends FunctionalTest ); } - public function testCanOrganiseSitetree() + public function testCanOrganiseTree() { $cms = CMSMain::create(); - $this->assertFalse($cms->CanOrganiseSitetree()); + $this->assertFalse($cms->CanOrganiseTree()); $this->logInWithPermission('CMS_ACCESS_CMSMain'); - $this->assertFalse($cms->CanOrganiseSitetree()); + $this->assertFalse($cms->CanOrganiseTree()); $this->logOut(); $this->logInWithPermission('SITETREE_REORGANISE'); - $this->assertTrue($cms->CanOrganiseSitetree()); + $this->assertTrue($cms->CanOrganiseTree()); $this->logOut(); $this->logInWithPermission('ADMIN'); - $this->assertTrue($cms->CanOrganiseSitetree()); + $this->assertTrue($cms->CanOrganiseTree()); } } diff --git a/tests/php/Controllers/CMSSiteTreeFilterTest.php b/tests/php/Controllers/CMSSiteTreeFilterTest.php index bf2a9253..9eefb1ce 100644 --- a/tests/php/Controllers/CMSSiteTreeFilterTest.php +++ b/tests/php/Controllers/CMSSiteTreeFilterTest.php @@ -24,8 +24,8 @@ class CMSSiteTreeFilterTest extends SapphireTest $f = new CMSSiteTreeFilter_Search(); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($page1)); - $this->assertTrue($f->isPageIncluded($page2)); + $this->assertTrue($f->isRecordIncluded($page1)); + $this->assertTrue($f->isRecordIncluded($page2)); } public function testSearchFilterByTitle() @@ -36,8 +36,8 @@ class CMSSiteTreeFilterTest extends SapphireTest $f = new CMSSiteTreeFilter_Search(['Title' => 'Page 1']); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($page1)); - $this->assertFalse($f->isPageIncluded($page2)); + $this->assertTrue($f->isRecordIncluded($page1)); + $this->assertFalse($f->isRecordIncluded($page2)); $this->assertEquals(1, count($results ?? [])); $this->assertEquals( ['ID' => $page1->ID, 'ParentID' => 0], @@ -50,10 +50,10 @@ class CMSSiteTreeFilterTest extends SapphireTest $page = $this->objFromFixture(SiteTree::class, 'page8'); $filter = CMSSiteTreeFilter_Search::create(['Term' => 'lake-wanaka+adventure']); - $this->assertTrue($filter->isPageIncluded($page)); + $this->assertTrue($filter->isRecordIncluded($page)); $filter = CMSSiteTreeFilter_Search::create(['URLSegment' => 'lake-wanaka+adventure']); - $this->assertTrue($filter->isPageIncluded($page)); + $this->assertTrue($filter->isRecordIncluded($page)); } public function testIncludesParentsForNestedMatches() @@ -64,8 +64,8 @@ class CMSSiteTreeFilterTest extends SapphireTest $f = new CMSSiteTreeFilter_Search(['Title' => 'Page 3b']); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($parent)); - $this->assertTrue($f->isPageIncluded($child)); + $this->assertTrue($f->isRecordIncluded($parent)); + $this->assertTrue($f->isRecordIncluded($child)); $this->assertEquals(1, count($results ?? [])); $this->assertEquals( ['ID' => $child->ID, 'ParentID' => $parent->ID], @@ -91,8 +91,8 @@ class CMSSiteTreeFilterTest extends SapphireTest $f = new CMSSiteTreeFilter_ChangedPages(['Term' => 'Changed']); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($changedPage)); - $this->assertFalse($f->isPageIncluded($unchangedPage)); + $this->assertTrue($f->isRecordIncluded($changedPage)); + $this->assertFalse($f->isRecordIncluded($unchangedPage)); $this->assertEquals(1, count($results ?? [])); $this->assertEquals( ['ID' => $changedPage->ID, 'ParentID' => 0], @@ -130,11 +130,11 @@ class CMSSiteTreeFilterTest extends SapphireTest ); $f = new CMSSiteTreeFilter_DeletedPages(['Term' => 'Page']); - $this->assertTrue($f->isPageIncluded($deletedPage)); + $this->assertTrue($f->isRecordIncluded($deletedPage)); // Check that only changed pages are returned $f = new CMSSiteTreeFilter_DeletedPages(['Term' => 'No Matches']); - $this->assertFalse($f->isPageIncluded($deletedPage)); + $this->assertFalse($f->isRecordIncluded($deletedPage)); } public function testStatusDraftPagesFilter() @@ -148,16 +148,16 @@ class CMSSiteTreeFilterTest extends SapphireTest // Check draft page is shown $f = new CMSSiteTreeFilter_StatusDraftPages(['Term' => 'Page']); - $this->assertTrue($f->isPageIncluded($draftPage)); + $this->assertTrue($f->isRecordIncluded($draftPage)); // Check filter respects parameters $f = new CMSSiteTreeFilter_StatusDraftPages(['Term' => 'No Match']); - $this->assertEmpty($f->isPageIncluded($draftPage)); + $this->assertEmpty($f->isRecordIncluded($draftPage)); // Ensures empty array returned if no data to show $f = new CMSSiteTreeFilter_StatusDraftPages(); $draftPage->delete(); - $this->assertEmpty($f->isPageIncluded($draftPage)); + $this->assertEmpty($f->isRecordIncluded($draftPage)); } public function testDateFromToLastSameDate() @@ -171,7 +171,7 @@ class CMSSiteTreeFilterTest extends SapphireTest 'LastEditedTo' => $date, ]); $this->assertTrue( - $filter->isPageIncluded($draftPage), + $filter->isRecordIncluded($draftPage), 'Using the same date for from and to should show find that page' ); } @@ -189,16 +189,16 @@ class CMSSiteTreeFilterTest extends SapphireTest // Check live-only page is included $f = new CMSSiteTreeFilter_StatusRemovedFromDraftPages(['LastEditedFrom' => '2000-01-01 00:00']); - $this->assertTrue($f->isPageIncluded($removedDraftPage)); + $this->assertTrue($f->isRecordIncluded($removedDraftPage)); // Check filter is respected $f = new CMSSiteTreeFilter_StatusRemovedFromDraftPages(['LastEditedTo' => '1999-01-01 00:00']); - $this->assertEmpty($f->isPageIncluded($removedDraftPage)); + $this->assertEmpty($f->isRecordIncluded($removedDraftPage)); // Ensures empty array returned if no data to show $f = new CMSSiteTreeFilter_StatusRemovedFromDraftPages(); $removedDraftPage->delete(); - $this->assertEmpty($f->isPageIncluded($removedDraftPage)); + $this->assertEmpty($f->isRecordIncluded($removedDraftPage)); } public function testStatusDeletedFilter() @@ -214,10 +214,10 @@ class CMSSiteTreeFilterTest extends SapphireTest // Check deleted page is included $f = new CMSSiteTreeFilter_StatusDeletedPages(['Title' => 'Page']); - $this->assertTrue($f->isPageIncluded($checkParentExists)); + $this->assertTrue($f->isRecordIncluded($checkParentExists)); // Check filter is respected $f = new CMSSiteTreeFilter_StatusDeletedPages(['Title' => 'Bobby']); - $this->assertFalse($f->isPageIncluded($checkParentExists)); + $this->assertFalse($f->isRecordIncluded($checkParentExists)); } }