From 769136f8de253aa21fcdccbe90a407884fff65b4 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Thu, 9 May 2024 15:11:30 +1200 Subject: [PATCH] POC DO NOT MERGE - Make CMS Main work with other models --- code/Controllers/CMSMain.php | 335 +++++++++++++----- code/Controllers/CMSPageAddController.php | 15 +- code/Controllers/CMSPageEditController.php | 10 +- .../Controllers/CMSPageSettingsController.php | 8 +- code/Controllers/CMSPagesController.php | 3 +- templates/BreadcrumbsTemplate.ss | 18 +- .../Controllers/Includes/CMSMain_Content.ss | 2 + .../Controllers/Includes/CMSMain_TreeNode.ss | 12 +- 8 files changed, 300 insertions(+), 103 deletions(-) diff --git a/code/Controllers/CMSMain.php b/code/Controllers/CMSMain.php index a30c798200..a3cf70947f 100644 --- a/code/Controllers/CMSMain.php +++ b/code/Controllers/CMSMain.php @@ -25,6 +25,7 @@ use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\Control\PjaxResponseNegotiator; use SilverStripe\Core\Cache\MemberCacheFlusher; +use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Convert; use SilverStripe\Core\Environment; @@ -73,6 +74,7 @@ use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController; use SilverStripe\View\ArrayData; use SilverStripe\View\Requirements; +use SilverStripe\View\SSViewer; /** * The main "content" area of the CMS. @@ -178,6 +180,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr */ protected $hintsCache; + public function getTreeClass(): string + { + return static::config()->get('tree_class'); + } + protected function init() { parent::init(); @@ -252,7 +259,7 @@ public function ShowSwitchView() * Overloads the LeftAndMain::ShowView. Allows to pass a page as a parameter, so we are able * to switch view also for archived versions. * - * @param SiteTree $page + * @param DataObject $page * @return array */ public function SwitchView($page = null) @@ -372,7 +379,7 @@ public function LinkListViewDeferred() * * @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); } @@ -389,6 +396,9 @@ public function LinkPageEdit($id = null) public function LinkPageSettings() { + if (!$this->getTreeClass()::singleton()->hasMethod('getSettingsFields')) { + return null; + } if ($id = $this->currentPageID()) { return $this->LinkWithSearch( Controller::join_links(CMSPageSettingsController::singleton()->Link('show'), $id) @@ -488,7 +498,7 @@ public function LinkPreview() { $record = $this->getRecord($this->currentPageID()); $baseLink = Director::absoluteBaseURL(); - if ($record && $record instanceof SiteTree) { + if ($record && $record->hasMethod('Link')) { // if we are an external redirector don't show a link if ($record instanceof RedirectorPage && $record->RedirectionType == 'External') { $baseLink = false; @@ -504,7 +514,7 @@ public function LinkPreview() */ public function SiteTreeAsUL() { - $treeClass = $this->config()->get('tree_class'); + $treeClass = $this->getTreeClass(); $filter = $this->getSearchFilter(); DataObject::singleton($treeClass)->prepopulateTreeDataCache(null, [ @@ -577,7 +587,8 @@ public function getSiteTreeFor( } // Pre-cache permissions - $checker = SiteTree::getPermissionChecker(); + $singleton = $this->getTreeClass()::singleton(); + $checker = $singleton->hasMethod('getPermissionChecker') ? $this->getTreeClass()::getPermissionChecker() : null; if ($checker instanceof InheritedPermissions) { $checker->prePopulatePermissionCache( InheritedPermissions::EDIT, @@ -601,7 +612,7 @@ public function getSiteTreeFor( 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, @@ -618,19 +629,69 @@ protected function getTreeNodeCustomisations() }; } + protected function getStatusFlags(DataObject $record, $cached = true) + { + if ($record->hasMethod('getStatusFlags')) { + return $record->getStatusFlags($cached); + } + + $flags = []; + if ($record->hasExtension(Versioned::class)) { + if ($record->isOnLiveOnly()) { + $flags['removedfromdraft'] = [ + 'text' => _t(__CLASS__.'.ONLIVEONLYSHORT', 'On live only'), + 'title' => _t(__CLASS__.'.ONLIVEONLYSHORTHELP', 'Page is published, but has been deleted from draft'), + ]; + } elseif ($record->isArchived()) { + $flags['archived'] = [ + 'text' => _t(__CLASS__.'.ARCHIVEDPAGESHORT', 'Archived'), + 'title' => _t(__CLASS__.'.ARCHIVEDPAGEHELP', 'Page is removed from draft and live'), + ]; + } elseif ($record->isOnDraftOnly()) { + $flags['addedtodraft'] = [ + 'text' => _t(__CLASS__.'.ADDEDTODRAFTSHORT', 'Draft'), + 'title' => _t(__CLASS__.'.ADDEDTODRAFTHELP', "Page has not been published yet") + ]; + } elseif ($record->isModifiedOnDraft()) { + $flags['modified'] = [ + 'text' => _t(__CLASS__.'.MODIFIEDONDRAFTSHORT', 'Modified'), + 'title' => _t(__CLASS__.'.MODIFIEDONDRAFTHELP', 'Page has unpublished changes'), + ]; + } + } + + $record->extend('updateStatusFlags', $flags); + + return $flags; + } + /** * Get extra CSS classes for a page's tree node * - * @param SiteTree $node * @return string */ - public function getTreeNodeClasses(SiteTree $node) + public function getTreeNodeClasses(DataObject $node) { // Get classes from object - $classes = $node->CMSTreeClasses(); + if ($node->hasMethod('CMSTreeClasses')) { + $classes = $node->CMSTreeClasses(); + } else { + $classes = ''; + if (!$node->canEdit()) { + if (!$node->canView()) { + $classes .= " disabled"; + } else { + $classes .= " edit-disabled"; + } + } + + if ($node->hasField('ShowInMenus') && !$node->ShowInMenus) { + $classes .= " notinmenu"; + } + } // Get status flag classes - $flags = $node->getStatusFlags(); + $flags = $this->getStatusFlags($node); if ($flags) { $statuses = array_keys($flags ?? []); foreach ($statuses as $s) { @@ -657,7 +718,7 @@ public function getTreeNodeClasses(SiteTree $node) public function getsubtree(HTTPRequest $request): HTTPResponse { $html = $this->getSiteTreeFor( - $this->config()->get('tree_class'), + $this->getTreeClass(), $request->getVar('ID'), null, null, @@ -702,7 +763,7 @@ public function updatetreenodes(HTTPRequest $request): HTTPResponse // 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->getTreeClass(); $next = DataObject::get($className) ->filter('ParentID', $record->ParentID) ->filter('Sort:GreaterThan', $record->Sort) @@ -762,7 +823,7 @@ public function savetreenode(HTTPRequest $request): HTTPResponse ); } - $className = $this->config()->get('tree_class'); + $className = $this->getTreeClass(); $id = $request->requestVar('ID'); $parentID = $request->requestVar('ParentID'); if (!is_numeric($id) || !is_numeric($parentID)) { @@ -770,7 +831,7 @@ public function savetreenode(HTTPRequest $request): HTTPResponse } // Check record exists in the DB - /** @var SiteTree $node */ + /** @var DataObject $node */ $node = DataObject::get_by_id($className, $id); if (!$node) { $this->httpError( @@ -783,7 +844,7 @@ public function savetreenode(HTTPRequest $request): HTTPResponse } // Check top level permissions - $root = $node->getParentType(); + $root = $node->ParentID == 0 ? 'root' : 'subpage'; if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) { $this->httpError( 403, @@ -807,14 +868,14 @@ public function savetreenode(HTTPRequest $request): HTTPResponse $node->write(); $statusUpdates['modified'][$node->ID] = [ - 'TreeTitle' => $node->TreeTitle + 'TreeTitle' => $this->getTreeTitle($node) ]; // Update all dependent pages $virtualPages = VirtualPage::get()->filter("CopyContentFromID", $node->ID); foreach ($virtualPages as $virtualPage) { $statusUpdates['modified'][$virtualPage->ID] = [ - 'TreeTitle' => $virtualPage->TreeTitle + 'TreeTitle' => $this->getTreeTitle($virtualPage) ]; } @@ -832,7 +893,7 @@ public function savetreenode(HTTPRequest $request): HTTPResponse $node->Sort = ++$counter; $node->write(); $statusUpdates['modified'][$node->ID] = [ - 'TreeTitle' => $node->TreeTitle + 'TreeTitle' => $this->getTreeTitle($node) ]; } elseif (is_numeric($id)) { // Nodes that weren't "actually moved" shouldn't be registered as @@ -892,7 +953,7 @@ public function ExtraTreeTools() */ public function getSearchContext() { - $context = SiteTree::singleton()->getDefaultSearchContext(); + $context = $this->getTreeClass()::singleton()->getDefaultSearchContext(); $this->extend('updateSearchContext', $context); @@ -913,7 +974,7 @@ public function getSearchFieldSchema() $context->setSearchParams($params); $placeholder = _t('SilverStripe\\CMS\\Search\\SearchForm.FILTERLABELTEXT', 'Search') . ' "' . - SiteTree::singleton()->i18n_plural_name() . '"'; + $this->getTreeClass()::singleton()->i18n_plural_name() . '"'; $searchParams = $context->getSearchParams(); @@ -1001,6 +1062,13 @@ public function getSearchForm() return $form; } + protected function getPageTypeClasses() + { + return $this->getTreeClass()::singleton()->hasMethod('page_type_classes') + ? $this->getTreeClass()::page_type_classes() + : ClassInfo::subclassesFor($this->getTreeClass()); + } + /** * Returns a sorted array suitable for a dropdown with pagetypes and their translated name * @@ -1009,8 +1077,8 @@ public function getSearchForm() protected function getPageTypes() { $pageTypes = []; - foreach (SiteTree::page_type_classes() as $pageTypeClass) { - $pageTypes[$pageTypeClass] = SiteTree::singleton($pageTypeClass)->i18n_singular_name(); + foreach ($this->getPageTypeClasses() as $pageTypeClass) { + $pageTypes[$pageTypeClass] = $this->getTreeClass()::singleton($pageTypeClass)->i18n_singular_name(); } asort($pageTypes); return $pageTypes; @@ -1058,7 +1126,7 @@ public function Breadcrumbs($unlinked = false) } // Check if we are editing a page - /** @var SiteTree $record */ + /** @var DataObject $record */ $record = $this->currentPage(); if (!$record) { $items->push(new ArrayData([ @@ -1075,10 +1143,14 @@ public function Breadcrumbs($unlinked = false) $ancestors = $record->getAncestors(); $ancestors = new ArrayList(array_reverse($ancestors->toArray() ?? [])); $ancestors->push($record); - /** @var SiteTree $ancestor */ + /** @var DataObject $ancestor */ foreach ($ancestors as $ancestor) { + $title = $ancestor->hasField('MenuTitle') ? $ancestor->MenuTitle : $ancestor->Title; + if (!$title) { + $title = '(untitled)'; + } $items->push(new ArrayData([ - 'Title' => $ancestor->getMenuTitle(), + 'Title' => $title, 'Link' => ($unlinked) ? false : $ancestor->CMSEditLink() @@ -1098,7 +1170,7 @@ public function Breadcrumbs($unlinked = false) */ public function SiteTreeHints() { - $classes = SiteTree::page_type_classes(); + $classes = $this->getPageTypeClasses(); $memberID = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0; $cache = $this->getHintsCache(); $cacheKey = $this->generateHintsCacheKey($memberID); @@ -1134,7 +1206,7 @@ public function SiteTreeHints() // Check if can be created at the root $needsPerm = $obj->config()->get('need_permission'); - if (!$obj->config()->get('can_be_root') + if ($obj->config()->get('can_be_root') === false || (!array_key_exists($class, $canCreate ?? []) || !$canCreate[$class]) || ($needsPerm && !$this->can($needsPerm)) ) { @@ -1144,12 +1216,12 @@ public function SiteTreeHints() // Hint data specific to the class $def[$class] = []; - $defaultChild = $obj->defaultChild(); + $defaultChild = $obj->hasMethod('defaultChild') ? $obj->defaultChild() : null; if ($defaultChild !== 'Page' && $defaultChild !== null) { $def[$class]['defaultChild'] = $defaultChild; } - $defaultParent = $obj->defaultParent(); + $defaultParent = $obj->hasMethod('defaultParent') ? $obj->defaultParent() : null; if ($defaultParent !== 1 && $defaultParent !== null) { $def[$class]['defaultParent'] = $defaultParent; } @@ -1171,12 +1243,12 @@ public function SiteTreeHints() */ public function PageTypes() { - $classes = SiteTree::page_type_classes(); + $classes = $this->getPageTypeClasses(); $result = new ArrayList(); foreach ($classes as $class) { - $instance = SiteTree::singleton($class); + $instance = $this->getTreeClass()::singleton($class); if ($instance instanceof HiddenClass) { continue; } @@ -1190,8 +1262,8 @@ public function PageTypes() $result->push(new ArrayData([ 'ClassName' => $class, 'AddAction' => $instance->i18n_singular_name(), - 'Description' => $instance->i18n_classDescription(), - 'IconURL' => $instance->getPageIconURL(), + 'Description' => $instance->hasMethod('i18n_classDescription') ? $instance->i18n_classDescription() : '', + 'IconURL' => $instance->hasMethod('getPageIconURL') ? $instance->getPageIconURL() : null, 'Title' => $instance->i18n_singular_name(), ])); } @@ -1206,14 +1278,14 @@ public function PageTypes() * * @param int $id Record ID * @param int $versionID optional Version id of the given record - * @return SiteTree + * @return DataObject */ public function getRecord($id, $versionID = null) { if (!$id) { return null; } - $treeClass = $this->config()->get('tree_class'); + $treeClass = $this->getTreeClass(); if ($id instanceof $treeClass) { return $id; } @@ -1230,7 +1302,7 @@ public function getRecord($id, $versionID = null) $versionID = (int) $this->getRequest()->getVar('Version'); } - /** @var SiteTree $record */ + /** @var DataObject $record */ if ($versionID) { $record = Versioned::get_version($treeClass, $id, $versionID); } else { @@ -1278,6 +1350,75 @@ public function EditForm($request = null) return $this->getEditForm(); } + protected function getIconClass(DataObject $record) + { + if ($record->hasMethod('getIconClass')) { + return $record->getIconClass(); + } + if ($record->config()->get('icon')) { + return ''; + } + return $record->config()->get('icon_class'); + } + + protected function getTreeTitle(DataObject $record) + { + if ($record->hasMethod('getTreeTitle')) { + return $record->getTreeTitle(); + } + $title = $record->hasField('MenuTitle') ? $record->MenuTitle : $record->Title; + if (!$title) { + $title = '(untitled)'; + } + $children = $this->creatableChildPages($record); + $flags = $this->getStatusFlags($record); + $treeTitle = sprintf( + '%s', + $this->getIconClass($record), + Convert::raw2htmlid(static::class), + '', + Convert::raw2att(json_encode($children)), + Convert::raw2xml(str_replace(["\n","\r"], "", $title ?? '')) + ); + foreach ($flags as $class => $data) { + if (is_string($data)) { + $data = ['text' => $data]; + } + $treeTitle .= sprintf( + "%s", + 'status-' . Convert::raw2xml($class), + (isset($data['title'])) ? sprintf(' title="%s"', Convert::raw2xml($data['title'])) : '', + Convert::raw2xml($data['text']) + ); + } + + return $treeTitle; + } + + protected function creatableChildPages(DataObject $record) + { + if ($record->hasMethod('creatableChildPages')) { + return $record->creatableChildPages(); + } + // Build the list of candidate children + $children[$this->ID] = []; + $candidates = $this->getPageTypeClasses(); + + foreach ($candidates as $childClass) { + $child = singleton($childClass); + + if ($child->canCreate(null, ['Parent' => $this])) { + $children[$this->ID][] = [ + 'ClassName' => $childClass, + 'Title' => $child->i18n_singular_name(), + 'IconClass' => $this->getIconClass($child), + ]; + } + } + + return $children[$this->ID]; + } + /** * @param int $id * @param FieldList $fields @@ -1302,26 +1443,28 @@ public function getEditForm($id = null, $fields = null) } // Add extra fields - $deletedFromStage = !$record->isOnDraft(); + $deletedFromStage = $record->hasExtension(Versioned::class) && !$record->isOnDraft(); $fields->push($idField = 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->hasMethod('AbsoluteLink') ? $record->AbsoluteLink() : null)); $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, $this->getTreeTitle($record))); $archiveWarningMsgField->setValue($this->getArchiveWarningMessage($record)); // Build preview / live links - $liveLink = $record->getAbsoluteLiveLink(); - if ($liveLink) { - $liveLinkField->setValue($liveLink); - } - if (!$deletedFromStage) { - $stageLink = Controller::join_links($record->AbsoluteLink(), '?stage=Stage'); - if ($stageLink) { - $stageLinkField->setValue($stageLink); + if ($record->hasMethod('AbsoluteLink')) { + $liveLink = $record->getAbsoluteLiveLink(); + if ($liveLink) { + $liveLinkField->setValue($liveLink); + } + if (!$deletedFromStage) { + $stageLink = Controller::join_links($record->AbsoluteLink(), '?stage=Stage'); + if ($stageLink) { + $stageLinkField->setValue($stageLink); + } } } @@ -1414,13 +1557,7 @@ public function EmptyForm() /** * Build an archive warning message based on the page's children * - * @param SiteTree $record - * @return string - */ - /** - * Build an archive warning message based on the page's children - * - * @param SiteTree $record + * @param DataObject $record * @return string */ protected function getArchiveWarningMessage($record) @@ -1444,7 +1581,7 @@ protected function getArchiveWarningMessage($record) $descendants[] = $record->ID; $inChangeSetIDs = ChangeSetItem::get()->filter([ 'ObjectID' => $descendants, - 'ObjectClass' => SiteTree::class + 'ObjectClass' => $this->getTreeClass(), ])->column('ChangeSetID'); // Count number of affected change set @@ -1480,7 +1617,7 @@ protected function getArchiveWarningMessage($record) protected function collateDescendants($recordIDs, &$collator) { - $children = SiteTree::get()->filter(['ParentID' => $recordIDs])->column(); + $children = $this->getTreeClass()::get()->filter(['ParentID' => $recordIDs])->column(); if ($children) { foreach ($children as $item) { $collator[] = $item; @@ -1539,14 +1676,14 @@ public function childfilter(HTTPRequest $request): HTTPResponse { // Check valid parent specified $parentID = $request->requestVar('ParentID'); - $parent = SiteTree::get()->byID($parentID); + $parent = $this->getTreeClass()::get()->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 = $this->getPageTypeClasses(); $disallowedChildren = []; foreach ($classes as $class) { $obj = singleton($class); @@ -1600,12 +1737,46 @@ public function getList($params = [], $parentID = 0) if ($filter = $this->getQueryFilter($params)) { return $filter->getFilteredPages(); } else { - $list = DataList::create($this->config()->get('tree_class')); + $list = DataList::create($this->getTreeClass()); $parentID = is_numeric($parentID) ? $parentID : 0; return $list->filter("ParentID", $parentID); } } + protected function BreadcrumbsForListView(DataObject $record, $maxDepth = 20, $unlinked = false, $stopAtPageType = false, $showHidden = false, $delimiter = '»') + { + if ($record->hasMethod('Breadcrumbs')) { + return $record->Breadcrumbs($maxDepth, $unlinked, $stopAtPageType, $showHidden, $delimiter); + } + $pages = $this->getBreadcrumbItemsForListView($record, $maxDepth, $stopAtPageType, $showHidden); + $template = SSViewer::create('BreadcrumbsTemplate'); + return $template->process($this->customise(new ArrayData([ + "Pages" => $pages, + "Unlinked" => $unlinked, + "Delimiter" => $delimiter, + ]))); + } + + protected function getBreadcrumbItemsForListView(DataObject $record, $maxDepth = 20, $stopAtPageType = false, $showHidden = false) + { + $page = $record; + $pages = []; + + while ($page + && $page->exists() + && (!$maxDepth || count($pages ?? []) < $maxDepth) + && (!$stopAtPageType || $page->ClassName != $stopAtPageType) + ) { + if ($showHidden || $page->ShowInMenus || ($page->ID == $this->ID)) { + $pages[] = $page; + } + + $page = $page->Parent(); + } + + return new ArrayList(array_reverse($pages ?? [])); + } + /** * @return Form */ @@ -1659,7 +1830,7 @@ public function ListViewForm() $columns->setFieldFormatting([ 'listChildrenLink' => function ($value, &$item) { - /** @var SiteTree $item */ + /** @var DataObject $item */ $num = $item ? $item->numChildren() : null; if ($num) { return sprintf( @@ -1670,13 +1841,13 @@ public function ListViewForm() } }, 'getTreeTitle' => function ($value, &$item) { - /** @var SiteTree $item */ + /** @var DataObject $item */ $title = sprintf( '%s', $item->CMSEditLink(), - $item->TreeTitle // returns HTML, does its own escaping + $this->getTreeTitle($item) // returns HTML, does its own escaping ); - $breadcrumbs = $item->Breadcrumbs(20, true, false, true, '/'); + $breadcrumbs = $this->BreadcrumbsForListView($item, 20, true, false, true, '/'); // Remove item's tile $breadcrumbs = preg_replace('/[^\/]+$/', '', trim($breadcrumbs ?? '')); // Trim spaces around delimiters @@ -1730,12 +1901,11 @@ public function currentPageID() */ public function save(array $data, Form $form): HTTPResponse { - $className = $this->config()->get('tree_class'); + $className = $this->getTreeClass(); // 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()) { @@ -1760,7 +1930,7 @@ public function save(array $data, Form $form): HTTPResponse $record->HasBrokenLink = 0; $record->HasBrokenFile = 0; - if (!$record->ObsoleteClassName) { + if ($record->hasExtension(Versioned::class) && !$record->ObsoleteClassName) { $record->writeWithoutVersion(); } @@ -1805,7 +1975,7 @@ public function save(array $data, Form $form): HTTPResponse */ public function getNewItem($id, $setID = true) { - $parentClass = $this->config()->get('tree_class'); + $parentClass = $this->getTreeClass(); list(, $className, $parentID) = array_pad(explode('-', $id ?? ''), 3, null); if (!is_a($className, $parentClass ?? '', true)) { @@ -1816,7 +1986,6 @@ public function getNewItem($id, $setID = true) throw new HTTPResponse_Exception($response); } - /** @var SiteTree $newItem */ $newItem = Injector::inst()->create($className); $newItem->Title = _t( __CLASS__ . '.NEWPAGE', @@ -1830,7 +1999,7 @@ public function getNewItem($id, $setID = true) // 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(); + $table = DataObject::singleton($this->getTreeClass())->baseTable(); $maxSort = DB::prepared_query( "SELECT MAX(\"Sort\") FROM \"$table\" WHERE \"ParentID\" = ?", [$parentID] @@ -1879,14 +2048,14 @@ public function revert(array $data, Form $form): HTTPResponse } $id = (int) $data['ID']; - $restoredPage = Versioned::get_latest_version(SiteTree::class, $id); + $restoredPage = Versioned::get_latest_version($this->getTreeClass(), $id); if (!$restoredPage) { throw new HTTPResponse_Exception("SiteTree #$id not found", 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, [ + $table = DataObject::singleton($this->getTreeClass())->baseTable(); + $liveTable = DataObject::singleton($this->getTreeClass())->stageTable($table, Versioned::LIVE); + $record = Versioned::get_one_by_stage($this->getTreeClass(), Versioned::LIVE, [ "\"$liveTable\".\"ID\"" => $id ]); @@ -1924,7 +2093,7 @@ public function revert(array $data, Form $form): HTTPResponse public function delete(array $data, Form $form): HTTPResponse { $id = $data['ID']; - $record = SiteTree::get()->byID($id); + $record = $this->getTreeClass()::get()->byID($id); if ($record && !$record->canDelete()) { return Security::permissionFailure(); } @@ -1956,7 +2125,7 @@ public function delete(array $data, Form $form): HTTPResponse public function archive(array $data, Form $form): HTTPResponse { $id = $data['ID']; - $record = SiteTree::get()->byID($id); + $record = $this->getTreeClass()::get()->byID($id); if (!$record || !$record->exists()) { throw new HTTPResponse_Exception("Bad record ID #$id", 404); } @@ -1989,8 +2158,7 @@ public function publish(array $data, Form $form): HTTPResponse public function unpublish(array $data, Form $form): HTTPResponse { - $className = $this->config()->get('tree_class'); - /** @var SiteTree $record */ + $className = $this->getTreeClass(); $record = DataObject::get_by_id($className, $data['ID']); if ($record && !$record->canUnpublish()) { @@ -2039,8 +2207,7 @@ public function doRollback($data, $form) $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); + $record = Versioned::get_latest_version($this->getTreeClass(), $id); if ($record && !$record->canEdit()) { return Security::permissionFailure($this); } @@ -2123,7 +2290,7 @@ public function BatchActionList() } /** - * Restore a completely deleted page from the SiteTree_versions table. + * Restore a completely deleted page from the _versions table. */ public function restore(array $data, Form $form): HTTPResponse { @@ -2132,9 +2299,9 @@ public function restore(array $data, Form $form): HTTPResponse } $id = (int)$data['ID']; - $restoredPage = Versioned::get_latest_version(SiteTree::class, $id); + $restoredPage = Versioned::get_latest_version($this->getTreeClass(), $id); if (!$restoredPage) { - return new HTTPResponse("SiteTree #$id not found", 400); + return new HTTPResponse("Model #$id not found", 400); } $restoredPage = $restoredPage->doRestoreToStage(); @@ -2159,7 +2326,7 @@ public function duplicate(HTTPRequest $request): HTTPResponse } if (($id = $this->urlParams['ID']) && is_numeric($id)) { - $page = SiteTree::get()->byID($id); + $page = $this->getTreeClass()::get()->byID($id); if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) { return Security::permissionFailure($this); } @@ -2201,7 +2368,7 @@ public function duplicatewithchildren(HTTPRequest $request): HTTPResponse } Environment::increaseTimeLimitTo(); if (($id = $this->urlParams['ID']) && is_numeric($id)) { - $page = SiteTree::get()->byID($id); + $page = $this->getTreeClass()::get()->byID($id); if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) { return Security::permissionFailure($this); } diff --git a/code/Controllers/CMSPageAddController.php b/code/Controllers/CMSPageAddController.php index 87a236e85e..1e4d7fe768 100644 --- a/code/Controllers/CMSPageAddController.php +++ b/code/Controllers/CMSPageAddController.php @@ -44,15 +44,16 @@ class CMSPageAddController extends CMSPageEditController public function AddForm() { $pageTypes = []; - $defaultIcon = Config::inst()->get(SiteTree::class, 'icon_class'); + $defaultIcon = Config::inst()->get($this->getTreeClass(), 'icon_class'); foreach ($this->PageTypes() 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())) { + $singleton = $class::singleton(); + if ($icon === $defaultIcon && !empty($singleton->hasMethod('getPageIconURL') ? $singleton->getPageIconURL() : null)) { $icon = ''; } @@ -90,7 +91,7 @@ public function AddForm() $parentField = new TreeDropdownField( "ParentID", "", - SiteTree::class, + $this->getTreeClass(), 'ID', 'TreeTitle' ), @@ -145,7 +146,7 @@ public function AddForm() // 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($this->getTreeClass(), SiteTree::class, true) && !SiteConfig::current_site_config()->canCreateTopLevel()) { $topField->setDisabled(true); $parentModeField->setValue('child'); } @@ -194,14 +195,14 @@ public function doAdd(array $data, Form $form): HTTPResponse $parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0; if (!$parentID && isset($data['Parent'])) { - $page = SiteTree::get_by_link($data['Parent']); + $page = $this->getTreeClass()::get_by_link($data['Parent']); if ($page) { $parentID = $page->ID; } } if (is_numeric($parentID) && $parentID > 0) { - $parentObj = SiteTree::get()->byID($parentID); + $parentObj = $this->getTreeClass()::get()->byID($parentID); } else { $parentObj = null; } diff --git a/code/Controllers/CMSPageEditController.php b/code/Controllers/CMSPageEditController.php index b15ff27527..fb92dc965c 100644 --- a/code/Controllers/CMSPageEditController.php +++ b/code/Controllers/CMSPageEditController.php @@ -2,10 +2,8 @@ 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; @@ -57,7 +55,7 @@ public function getClientConfig() public function addtocampaign(array $data, Form $form): HTTPResponse { $id = $data['ID']; - $record = \Page::get()->byID($id); + $record = $this->getTreeClass()::get()->byID($id); $handler = AddToCampaignHandler::create($this, $record); $response = $handler->addToCampaign($record, $data); @@ -96,14 +94,14 @@ public function AddToCampaignForm($request) public function getAddToCampaignForm($id) { // Get record-specific fields - $record = SiteTree::get()->byID($id); + $record = $this->getTreeClass()::get()->byID($id); if (!$record) { $this->httpError(404, _t( __CLASS__ . '.ErrorNotFound', 'That {Type} couldn\'t be found', '', - ['Type' => Page::singleton()->i18n_singular_name()] + ['Type' => $this->getTreeClass()::singleton()->i18n_singular_name()] )); return null; } @@ -112,7 +110,7 @@ public function getAddToCampaignForm($id) __CLASS__.'.ErrorItemPermissionDenied', 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to a campaign', '', - ['ObjectTitle' => Page::singleton()->i18n_singular_name()] + ['ObjectTitle' => $this->getTreeClass()::singleton()->i18n_singular_name()] )); return null; } diff --git a/code/Controllers/CMSPageSettingsController.php b/code/Controllers/CMSPageSettingsController.php index 1450a33387..7fdc1b7106 100644 --- a/code/Controllers/CMSPageSettingsController.php +++ b/code/Controllers/CMSPageSettingsController.php @@ -18,8 +18,12 @@ class CMSPageSettingsController extends CMSMain public function getEditForm($id = null, $fields = null) { $record = $this->getRecord($id ?: $this->currentPageID()); - - return parent::getEditForm($id, ($record) ? $record->getSettingsFields() : null); + 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 5fc962a977..097151dc94 100644 --- a/code/Controllers/CMSPagesController.php +++ b/code/Controllers/CMSPagesController.php @@ -2,7 +2,6 @@ namespace SilverStripe\CMS\Controllers; -use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Control\Controller; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\DataObject; @@ -37,7 +36,7 @@ public function Breadcrumbs($unlinked = false) $this->beforeExtending('updateBreadcrumbs', function (ArrayList $items) { //special case for building the breadcrumbs when calling the listchildren Pages ListView action if ($parentID = $this->getRequest()->getVar('ParentID')) { - $page = SiteTree::get()->byID($parentID); + $page = $this->getTreeClass()::get()->byID($parentID); //build a reversed list of the parent tree $pages = []; diff --git a/templates/BreadcrumbsTemplate.ss b/templates/BreadcrumbsTemplate.ss index 912382aa3a..1b91101450 100755 --- a/templates/BreadcrumbsTemplate.ss +++ b/templates/BreadcrumbsTemplate.ss @@ -1,4 +1,20 @@ <%-- Loop is all on one line to prevent whitespace bug in older versions of IE --%> <% if $Pages %> - <% loop $Pages %><% if $IsLast %>$MenuTitle.XML<% else %><% if not Up.Unlinked %><% end_if %>$MenuTitle.XML<% if not Up.Unlinked %><% end_if %> $Up.Delimiter.RAW <% end_if %><% end_loop %> + <% loop $Pages %> + <% if $IsLast %> + <% if $hasField('MenuTitle') %> + $MenuTitle.XML + <% else %> + $Title.XML + <% end_if %> + <% else %> + <% if not Up.Unlinked %><% end_if %> + <% if $hasField('MenuTitle') %> + $MenuTitle.XML + <% else %> + $Title.XML + <% end_if %> + <% if not Up.Unlinked %><% end_if %> $Up.Delimiter.RAW + <% end_if %> + <% 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 0dbb16530b..e945fcbdf6 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Content.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Content.ss @@ -15,11 +15,13 @@ <%t SilverStripe\\CMS\\Controllers\\CMSMain.TabContent 'Content' %> + <% if $LinkPageSettings %> + <% end_if %>
  •     - {$node.TreeTitle} + + <% if $node.hasField('TreeTitle') %> + $node.TreeTitle + <% else %> + <% if $node.hasField('MenuTitle') %> + $node.MenuTitle + <% else %> + $node.Title + <% end_if %> + <% end_if %> + $SubTree