mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
Merge pull request #1783 from open-sausages/pulls/4.0/leftandmain-cleanup
BUG Ensure all CMS forms include full ID / VersionID in path
This commit is contained in:
commit
41badd9bbb
@ -62,3 +62,4 @@ mappings:
|
|||||||
MigrateSiteTreeLinkingTask: SilverStripe\CMS\Tasks\MigrateSiteTreeLinkingTask
|
MigrateSiteTreeLinkingTask: SilverStripe\CMS\Tasks\MigrateSiteTreeLinkingTask
|
||||||
RemoveOrphanedPagesTask: SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask
|
RemoveOrphanedPagesTask: SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask
|
||||||
SiteTreeMaintenanceTask: SilverStripe\CMS\Tasks\SiteTreeMaintenanceTask
|
SiteTreeMaintenanceTask: SilverStripe\CMS\Tasks\SiteTreeMaintenanceTask
|
||||||
|
LeftAndMain_TreeNode: SilverStripe\CMS\Controllers\CMSTreeNode
|
||||||
|
@ -4,6 +4,9 @@ namespace SilverStripe\CMS\Controllers;
|
|||||||
|
|
||||||
use SilverStripe\Admin\AdminRootController;
|
use SilverStripe\Admin\AdminRootController;
|
||||||
use SilverStripe\Admin\CMSBatchActionHandler;
|
use SilverStripe\Admin\CMSBatchActionHandler;
|
||||||
|
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
||||||
|
use SilverStripe\CMS\Model\VirtualPage;
|
||||||
|
use SilverStripe\Forms\Tab;
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
use SilverStripe\ORM\CMSPreviewable;
|
||||||
use SilverStripe\Admin\LeftAndMain;
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
|
use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
|
||||||
@ -38,7 +41,6 @@ use SilverStripe\Forms\GridField\GridFieldSortableHeader;
|
|||||||
use SilverStripe\Forms\HiddenField;
|
use SilverStripe\Forms\HiddenField;
|
||||||
use SilverStripe\Forms\LabelField;
|
use SilverStripe\Forms\LabelField;
|
||||||
use SilverStripe\Forms\LiteralField;
|
use SilverStripe\Forms\LiteralField;
|
||||||
use SilverStripe\Forms\RequiredFields;
|
|
||||||
use SilverStripe\Forms\ResetFormAction;
|
use SilverStripe\Forms\ResetFormAction;
|
||||||
use SilverStripe\Forms\TabSet;
|
use SilverStripe\Forms\TabSet;
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
@ -50,6 +52,7 @@ use SilverStripe\ORM\FieldType\DBHTMLText;
|
|||||||
use SilverStripe\ORM\HiddenClass;
|
use SilverStripe\ORM\HiddenClass;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
use SilverStripe\ORM\ValidationResult;
|
use SilverStripe\ORM\ValidationResult;
|
||||||
|
use SilverStripe\SiteConfig\SiteConfig;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
@ -94,7 +97,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
private static $subitem_class = Member::class;
|
private static $subitem_class = Member::class;
|
||||||
|
|
||||||
private static $session_namespace = 'SilverStripe\\CMS\\Controllers\\CMSMain';
|
private static $session_namespace = self::class;
|
||||||
|
|
||||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||||
|
|
||||||
@ -121,6 +124,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
'SearchForm',
|
'SearchForm',
|
||||||
'SiteTreeAsUL',
|
'SiteTreeAsUL',
|
||||||
'getshowdeletedsubtree',
|
'getshowdeletedsubtree',
|
||||||
|
'savetreenode',
|
||||||
|
'getsubtree',
|
||||||
|
'updatetreenodes',
|
||||||
'batchactions',
|
'batchactions',
|
||||||
'treeview',
|
'treeview',
|
||||||
'listview',
|
'listview',
|
||||||
@ -128,6 +134,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
'childfilter',
|
'childfilter',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static $url_handlers = [
|
||||||
|
'EditForm/$ID' => 'EditForm',
|
||||||
|
];
|
||||||
|
|
||||||
private static $casting = array(
|
private static $casting = array(
|
||||||
'TreeIsFiltered' => 'Boolean',
|
'TreeIsFiltered' => 'Boolean',
|
||||||
'AddForm' => 'HTMLFragment',
|
'AddForm' => 'HTMLFragment',
|
||||||
@ -389,8 +399,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
public function SiteTreeAsUL()
|
public function SiteTreeAsUL()
|
||||||
{
|
{
|
||||||
// Pre-cache sitetree version numbers for querying efficiency
|
// Pre-cache sitetree version numbers for querying efficiency
|
||||||
Versioned::prepopulate_versionnumber_cache(SiteTree::class, "Stage");
|
Versioned::prepopulate_versionnumber_cache(SiteTree::class, Versioned::DRAFT);
|
||||||
Versioned::prepopulate_versionnumber_cache(SiteTree::class, "Live");
|
Versioned::prepopulate_versionnumber_cache(SiteTree::class, Versioned::LIVE);
|
||||||
$html = $this->getSiteTreeFor($this->stat('tree_class'));
|
$html = $this->getSiteTreeFor($this->stat('tree_class'));
|
||||||
|
|
||||||
$this->extend('updateSiteTreeAsUL', $html);
|
$this->extend('updateSiteTreeAsUL', $html);
|
||||||
@ -398,6 +408,368 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a site tree HTML listing which displays the nodes under the given criteria.
|
||||||
|
*
|
||||||
|
* @param string $className The class of the root object
|
||||||
|
* @param string $rootID The ID of the root object. If this is null then a complete tree will be
|
||||||
|
* shown
|
||||||
|
* @param string $childrenMethod The method to call to get the children of the tree. For example,
|
||||||
|
* Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
|
||||||
|
* @param string $numChildrenMethod
|
||||||
|
* @param callable $filterFunction
|
||||||
|
* @param int $nodeCountThreshold
|
||||||
|
* @return string Nested unordered list with links to each page
|
||||||
|
*/
|
||||||
|
public function getSiteTreeFor(
|
||||||
|
$className,
|
||||||
|
$rootID = null,
|
||||||
|
$childrenMethod = null,
|
||||||
|
$numChildrenMethod = null,
|
||||||
|
$filterFunction = null,
|
||||||
|
$nodeCountThreshold = 30
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Filter criteria
|
||||||
|
$filter = $this->getSearchFilter();
|
||||||
|
|
||||||
|
// Default childrenMethod and numChildrenMethod
|
||||||
|
if (!$childrenMethod) {
|
||||||
|
$childrenMethod = ($filter && $filter->getChildrenMethod())
|
||||||
|
? $filter->getChildrenMethod()
|
||||||
|
: 'AllChildrenIncludingDeleted';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$numChildrenMethod) {
|
||||||
|
$numChildrenMethod = 'numChildren';
|
||||||
|
if ($filter && $filter->getNumChildrenMethod()) {
|
||||||
|
$numChildrenMethod = $filter->getNumChildrenMethod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$filterFunction && $filter) {
|
||||||
|
$filterFunction = function ($node) use ($filter) {
|
||||||
|
return $filter->isPageIncluded($node);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the tree root
|
||||||
|
$record = ($rootID) ? $this->getRecord($rootID) : null;
|
||||||
|
$obj = $record ? $record : singleton($className);
|
||||||
|
|
||||||
|
// Get the current page
|
||||||
|
// NOTE: This *must* be fetched before markPartialTree() is called, as this
|
||||||
|
// causes the Hierarchy::$marked cache to be flushed (@see CMSMain::getRecord)
|
||||||
|
// which means that deleted pages stored in the marked tree would be removed
|
||||||
|
$currentPage = $this->currentPage();
|
||||||
|
|
||||||
|
// Mark the nodes of the tree to return
|
||||||
|
if ($filterFunction) {
|
||||||
|
$obj->setMarkingFilterFunction($filterFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod);
|
||||||
|
|
||||||
|
// Ensure current page is exposed
|
||||||
|
if ($currentPage) {
|
||||||
|
$obj->markToExpose($currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
SiteTree::prepopulate_permission_cache(
|
||||||
|
'CanEditType',
|
||||||
|
$obj->markedNodeIDs(),
|
||||||
|
[ SiteTree::class, 'can_edit_multiple']
|
||||||
|
);
|
||||||
|
|
||||||
|
// getChildrenAsUL is a flexible and complex way of traversing the tree
|
||||||
|
$controller = $this;
|
||||||
|
$recordController = CMSPageEditController::singleton();
|
||||||
|
$titleFn = function (&$child, $numChildrenMethod) use (&$controller, &$recordController, $filter) {
|
||||||
|
$link = Controller::join_links($recordController->Link("show"), $child->ID);
|
||||||
|
$node = CMSTreeNode::create($child, $link, $controller->isCurrentPage($child), $numChildrenMethod, $filter);
|
||||||
|
return $node->forTemplate();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Limit the amount of nodes shown for performance reasons.
|
||||||
|
// Skip the check if we're filtering the tree, since its not clear how many children will
|
||||||
|
// match the filter criteria until they're queried (and matched up with previously marked nodes).
|
||||||
|
$nodeThresholdLeaf = SiteTree::config()->get('node_threshold_leaf');
|
||||||
|
if ($nodeThresholdLeaf && !$filterFunction) {
|
||||||
|
$nodeCountCallback = function ($parent, $numChildren) use (&$controller, $nodeThresholdLeaf) {
|
||||||
|
if ( !$parent->ID || $numChildren <= $nodeThresholdLeaf) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sprintf(
|
||||||
|
'<ul><li class="readonly"><span class="item">'
|
||||||
|
. '%s (<a href="%s" class="cms-panel-link" data-pjax-target="Content">%s</a>)'
|
||||||
|
. '</span></li></ul>',
|
||||||
|
_t('LeftAndMain.TooManyPages', 'Too many pages'),
|
||||||
|
Controller::join_links(
|
||||||
|
$controller->LinkWithSearch($controller->Link()),
|
||||||
|
'?view=listview&ParentID=' . $parent->ID
|
||||||
|
),
|
||||||
|
_t(
|
||||||
|
'LeftAndMain.ShowAsList',
|
||||||
|
'show as list',
|
||||||
|
'Show large amount of pages in list instead of tree view'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
$nodeCountCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the amount of pages exceeds the node thresholds set, use the callback
|
||||||
|
$html = null;
|
||||||
|
if ($obj->ParentID && $nodeCountCallback) {
|
||||||
|
$html = $nodeCountCallback($obj, $obj->$numChildrenMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise return the actual tree (which might still filter leaf thresholds on children)
|
||||||
|
if (!$html) {
|
||||||
|
$html = $obj->getChildrenAsUL(
|
||||||
|
"",
|
||||||
|
$titleFn,
|
||||||
|
CMSPagesController::singleton(),
|
||||||
|
true,
|
||||||
|
$childrenMethod,
|
||||||
|
$numChildrenMethod,
|
||||||
|
$nodeCountThreshold,
|
||||||
|
$nodeCountCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the root if needs be.
|
||||||
|
if (!$rootID) {
|
||||||
|
// This lets us override the tree title with an extension
|
||||||
|
if ($this->hasMethod('getCMSTreeTitle') && $customTreeTitle = $this->getCMSTreeTitle()) {
|
||||||
|
$treeTitle = $customTreeTitle;
|
||||||
|
} elseif (class_exists(SiteConfig::class)) {
|
||||||
|
$siteConfig = SiteConfig::current_site_config();
|
||||||
|
$treeTitle = Convert::raw2xml($siteConfig->Title);
|
||||||
|
} else {
|
||||||
|
$treeTitle = '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = "<ul><li id=\"record-0\" data-id=\"0\" class=\"Root nodelete\"><strong>$treeTitle</strong>"
|
||||||
|
. $html . "</li></ul>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a subtree underneath the request param 'ID'.
|
||||||
|
* If ID = 0, then get the whole tree.
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getsubtree($request)
|
||||||
|
{
|
||||||
|
$html = $this->getSiteTreeFor(
|
||||||
|
$this->stat('tree_class'),
|
||||||
|
$request->getVar('ID'),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$request->getVar('minNodeCount')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Trim off the outer tag
|
||||||
|
$html = preg_replace('/^[\s\t\r\n]*<ul[^>]*>/', '', $html);
|
||||||
|
$html = preg_replace('/<\/ul[^>]*>[\s\t\r\n]*$/', '', $html);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows requesting a view update on specific tree nodes.
|
||||||
|
* Similar to {@link getsubtree()}, but doesn't enforce loading
|
||||||
|
* all children with the node. Useful to refresh views after
|
||||||
|
* state modifications, e.g. saving a form.
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return HTTPResponse
|
||||||
|
*/
|
||||||
|
public function updatetreenodes($request)
|
||||||
|
{
|
||||||
|
$data = array();
|
||||||
|
$ids = explode(',', $request->getVar('ids'));
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if ($id === "") {
|
||||||
|
continue; // $id may be a blank string, which is invalid and should be skipped over
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $this->getRecord($id);
|
||||||
|
if (!$record) {
|
||||||
|
continue; // In case a page is no longer available
|
||||||
|
}
|
||||||
|
$recordController = CMSPageEditController::singleton();
|
||||||
|
|
||||||
|
// Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset)
|
||||||
|
// TODO: These methods should really be in hierarchy - for a start it assumes Sort exists
|
||||||
|
$prev = null;
|
||||||
|
|
||||||
|
$className = $this->stat('tree_class');
|
||||||
|
$next = DataObject::get($className)
|
||||||
|
->filter('ParentID', $record->ParentID)
|
||||||
|
->filter('Sort:GreaterThan', $record->Sort)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$next) {
|
||||||
|
$prev = DataObject::get($className)
|
||||||
|
->filter('ParentID', $record->ParentID)
|
||||||
|
->filter('Sort:LessThan', $record->Sort)
|
||||||
|
->reverse()
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
$link = Controller::join_links($recordController->Link("show"), $record->ID);
|
||||||
|
$html = CMSTreeNode::create($record, $link, $this->isCurrentPage($record))->forTemplate(). '</li>';
|
||||||
|
|
||||||
|
$data[$id] = array(
|
||||||
|
'html' => $html,
|
||||||
|
'ParentID' => $record->ParentID,
|
||||||
|
'NextID' => $next ? $next->ID : null,
|
||||||
|
'PrevID' => $prev ? $prev->ID : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $this
|
||||||
|
->getResponse()
|
||||||
|
->addHeader('Content-Type', 'application/json')
|
||||||
|
->setBody(Convert::raw2json($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the position and parent of a tree node.
|
||||||
|
* Only saves the node if changes were made.
|
||||||
|
*
|
||||||
|
* Required data:
|
||||||
|
* - 'ID': The moved node
|
||||||
|
* - 'ParentID': New parent relation of the moved node (0 for root)
|
||||||
|
* - 'SiblingIDs': Array of all sibling nodes to the moved node (incl. the node itself).
|
||||||
|
* In case of a 'ParentID' change, relates to the new siblings under the new parent.
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return HTTPResponse JSON string with a
|
||||||
|
* @throws HTTPResponse_Exception
|
||||||
|
*/
|
||||||
|
public function savetreenode($request)
|
||||||
|
{
|
||||||
|
if (!SecurityToken::inst()->checkRequest($request)) {
|
||||||
|
return $this->httpError(400);
|
||||||
|
}
|
||||||
|
if (!Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN')) {
|
||||||
|
return $this->httpError(
|
||||||
|
403,
|
||||||
|
_t(
|
||||||
|
'LeftAndMain.CANT_REORGANISE',
|
||||||
|
"You do not have permission to rearange the site tree. Your change was not saved."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$className = $this->stat('tree_class');
|
||||||
|
$id = $request->requestVar('ID');
|
||||||
|
$parentID = $request->requestVar('ParentID');
|
||||||
|
if (!is_numeric($id) || !is_numeric($parentID)) {
|
||||||
|
return $this->httpError(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check record exists in the DB
|
||||||
|
/** @var SiteTree $node */
|
||||||
|
$node = DataObject::get_by_id($className, $id);
|
||||||
|
if (!$node) {
|
||||||
|
return $this->httpError(
|
||||||
|
500,
|
||||||
|
_t(
|
||||||
|
'LeftAndMain.PLEASESAVE',
|
||||||
|
"Please Save Page: This page could not be updated because it hasn't been saved yet."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check top level permissions
|
||||||
|
$root = $node->getParentType();
|
||||||
|
if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) {
|
||||||
|
return $this->httpError(
|
||||||
|
403,
|
||||||
|
_t(
|
||||||
|
'LeftAndMain.CANT_REORGANISE',
|
||||||
|
"You do not have permission to alter Top level pages. Your change was not saved."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$siblingIDs = $request->requestVar('SiblingIDs');
|
||||||
|
$statusUpdates = array('modified'=>array());
|
||||||
|
|
||||||
|
if (!$node->canEdit()) {
|
||||||
|
return Security::permissionFailure($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hierarchy (only if ParentID changed)
|
||||||
|
if ($node->ParentID != $parentID) {
|
||||||
|
$node->ParentID = (int)$parentID;
|
||||||
|
$node->write();
|
||||||
|
|
||||||
|
$statusUpdates['modified'][$node->ID] = array(
|
||||||
|
'TreeTitle' => $node->TreeTitle
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update all dependent pages
|
||||||
|
$virtualPages = VirtualPage::get()->filter("CopyContentFromID", $node->ID);
|
||||||
|
foreach ($virtualPages as $virtualPage) {
|
||||||
|
$statusUpdates['modified'][$virtualPage->ID] = array(
|
||||||
|
'TreeTitle' => $virtualPage->TreeTitle()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getResponse()->addHeader(
|
||||||
|
'X-Status',
|
||||||
|
rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update sorting
|
||||||
|
if (is_array($siblingIDs)) {
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($siblingIDs as $id) {
|
||||||
|
if ($id == $node->ID) {
|
||||||
|
$node->Sort = ++$counter;
|
||||||
|
$node->write();
|
||||||
|
$statusUpdates['modified'][$node->ID] = array(
|
||||||
|
'TreeTitle' => $node->TreeTitle
|
||||||
|
);
|
||||||
|
} elseif (is_numeric($id)) {
|
||||||
|
// Nodes that weren't "actually moved" shouldn't be registered as
|
||||||
|
// having been edited; do a direct SQL update instead
|
||||||
|
++$counter;
|
||||||
|
$table = DataObject::getSchema()->baseDataTable($className);
|
||||||
|
DB::prepared_query(
|
||||||
|
"UPDATE \"$table\" SET \"Sort\" = ? WHERE \"ID\" = ?",
|
||||||
|
array($counter, $id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getResponse()->addHeader(
|
||||||
|
'X-Status',
|
||||||
|
rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this
|
||||||
|
->getResponse()
|
||||||
|
->addHeader('Content-Type', 'application/json')
|
||||||
|
->setBody(Convert::raw2json($statusUpdates));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CanOrganiseSitetree()
|
||||||
|
{
|
||||||
|
return !Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN') ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
@ -662,55 +1034,72 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
*/
|
*/
|
||||||
public function getRecord($id, $versionID = null)
|
public function getRecord($id, $versionID = null)
|
||||||
{
|
{
|
||||||
|
if (!$id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
$treeClass = $this->stat('tree_class');
|
$treeClass = $this->stat('tree_class');
|
||||||
|
|
||||||
if ($id instanceof $treeClass) {
|
if ($id instanceof $treeClass) {
|
||||||
return $id;
|
return $id;
|
||||||
} elseif ($id && is_numeric($id)) {
|
}
|
||||||
$currentStage = Versioned::get_reading_mode();
|
if (substr($id, 0, 3) == 'new') {
|
||||||
|
|
||||||
if ($this->getRequest()->getVar('Version')) {
|
|
||||||
$versionID = (int) $this->getRequest()->getVar('Version');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($versionID) {
|
|
||||||
$record = Versioned::get_version($treeClass, $id, $versionID);
|
|
||||||
} else {
|
|
||||||
$record = DataObject::get_by_id($treeClass, $id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then, try getting a record from the live site
|
|
||||||
if (!$record) {
|
|
||||||
// $record = Versioned::get_one_by_stage($treeClass, "Live", "\"$treeClass\".\"ID\" = $id");
|
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
|
||||||
singleton($treeClass)->flushCache();
|
|
||||||
|
|
||||||
$record = DataObject::get_by_id($treeClass, $id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then, try getting a deleted record
|
|
||||||
if (!$record) {
|
|
||||||
$record = Versioned::get_latest_version($treeClass, $id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't open a page from a different locale
|
|
||||||
/** The record's Locale is saved in database in 2.4, and not related with Session,
|
|
||||||
* we should not check their locale matches the Translatable::get_current_locale,
|
|
||||||
* here as long as we all the HTTPRequest is init with right locale.
|
|
||||||
* This bit breaks the all FileIFrameField functions if the field is used in CMS
|
|
||||||
* and its relevent ajax calles, like loading the tree dropdown for TreeSelectorField.
|
|
||||||
*/
|
|
||||||
/* if($record && SiteTree::has_extension('Translatable') && $record->Locale && $record->Locale != Translatable::get_current_locale()) {
|
|
||||||
$record = null;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Set the reading mode back to what it was.
|
|
||||||
Versioned::set_reading_mode($currentStage);
|
|
||||||
|
|
||||||
return $record;
|
|
||||||
} elseif (substr($id, 0, 3) == 'new') {
|
|
||||||
return $this->getNewItem($id);
|
return $this->getNewItem($id);
|
||||||
}
|
}
|
||||||
|
if (!is_numeric($id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentStage = Versioned::get_reading_mode();
|
||||||
|
|
||||||
|
if ($this->getRequest()->getVar('Version')) {
|
||||||
|
$versionID = (int) $this->getRequest()->getVar('Version');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var SiteTree $record */
|
||||||
|
if ($versionID) {
|
||||||
|
$record = Versioned::get_version($treeClass, $id, $versionID);
|
||||||
|
} else {
|
||||||
|
$record = DataObject::get_by_id($treeClass, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, try getting a record from the live site
|
||||||
|
if (!$record) {
|
||||||
|
// $record = Versioned::get_one_by_stage($treeClass, "Live", "\"$treeClass\".\"ID\" = $id");
|
||||||
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
|
singleton($treeClass)->flushCache();
|
||||||
|
|
||||||
|
$record = DataObject::get_by_id($treeClass, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, try getting a deleted record
|
||||||
|
if (!$record) {
|
||||||
|
$record = Versioned::get_latest_version($treeClass, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the reading mode back to what it was.
|
||||||
|
Versioned::set_reading_mode($currentStage);
|
||||||
|
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
public function EditForm($request = null)
|
||||||
|
{
|
||||||
|
// set page ID from request
|
||||||
|
if ($request) {
|
||||||
|
// Validate id is present
|
||||||
|
$id = $request->param('ID');
|
||||||
|
if (!isset($id)) {
|
||||||
|
$this->httpError(400);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$this->setCurrentPageID($id);
|
||||||
|
}
|
||||||
|
return $this->getEditForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -720,119 +1109,130 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
*/
|
*/
|
||||||
public function getEditForm($id = null, $fields = null)
|
public function getEditForm($id = null, $fields = null)
|
||||||
{
|
{
|
||||||
|
// Get record
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
$id = $this->currentPageID();
|
$id = $this->currentPageID();
|
||||||
}
|
}
|
||||||
$form = parent::getEditForm($id, $fields);
|
/** @var SiteTree $record */
|
||||||
|
|
||||||
// TODO Duplicate record fetching (see parent implementation)
|
|
||||||
$record = $this->getRecord($id);
|
$record = $this->getRecord($id);
|
||||||
if ($record && !$record->canView()) {
|
|
||||||
return Security::permissionFailure($this);
|
// Check parent form can be generated
|
||||||
|
$form = parent::getEditForm($record, $fields);
|
||||||
|
if (!$form || !$record) {
|
||||||
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$fields) {
|
if (!$fields) {
|
||||||
$fields = $form->Fields();
|
$fields = $form->Fields();
|
||||||
}
|
}
|
||||||
$actions = $form->Actions();
|
|
||||||
|
|
||||||
if ($record) {
|
// Add extra fields
|
||||||
$deletedFromStage = !$record->isOnDraft();
|
$deletedFromStage = !$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("LiveLink"));
|
||||||
|
$fields->push($stageLinkField = new HiddenField("StageLink"));
|
||||||
|
$fields->push($archiveWarningMsgField = new HiddenField("ArchiveWarningMessage"));
|
||||||
|
$fields->push(new HiddenField("TreeTitle", false, $record->getTreeTitle()));
|
||||||
|
|
||||||
$fields->push($idField = new HiddenField("ID", false, $id));
|
$archiveWarningMsgField->setValue($this->getArchiveWarningMessage($record));
|
||||||
// Necessary for different subsites
|
|
||||||
$fields->push($liveLinkField = new HiddenField("AbsoluteLink", false, $record->AbsoluteLink()));
|
|
||||||
$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->TreeTitle));
|
|
||||||
|
|
||||||
$archiveWarningMsgField->setValue($this->getArchiveWarningMessage($record));
|
// Build preview / live links
|
||||||
|
$liveLink = $record->getAbsoluteLiveLink();
|
||||||
if ($record->ID && is_numeric($record->ID)) {
|
if ($liveLink) {
|
||||||
$liveLink = $record->getAbsoluteLiveLink();
|
$liveLinkField->setValue($liveLink);
|
||||||
if ($liveLink) {
|
}
|
||||||
$liveLinkField->setValue($liveLink);
|
if (!$deletedFromStage) {
|
||||||
}
|
$stageLink = Controller::join_links($record->AbsoluteLink(), '?stage=Stage');
|
||||||
if (!$deletedFromStage) {
|
if ($stageLink) {
|
||||||
$stageLink = Controller::join_links($record->AbsoluteLink(), '?stage=Stage');
|
$stageLinkField->setValue($stageLink);
|
||||||
if ($stageLink) {
|
|
||||||
$stageLinkField->setValue($stageLink);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Added in-line to the form, but plucked into different view by LeftAndMain.Preview.js upon load
|
// Added in-line to the form, but plucked into different view by LeftAndMain.Preview.js upon load
|
||||||
/** @skipUpgrade */
|
/** @skipUpgrade */
|
||||||
if ($record instanceof CMSPreviewable && !$fields->fieldByName('SilverStripeNavigator')) {
|
if ($record instanceof CMSPreviewable && !$fields->fieldByName('SilverStripeNavigator')) {
|
||||||
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
|
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
|
||||||
$navField->setAllowHTML(true);
|
$navField->setAllowHTML(true);
|
||||||
$fields->push($navField);
|
$fields->push($navField);
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAllCMSActions can be used to completely redefine the action list
|
// getAllCMSActions can be used to completely redefine the action list
|
||||||
if ($record->hasMethod('getAllCMSActions')) {
|
if ($record->hasMethod('getAllCMSActions')) {
|
||||||
$actions = $record->getAllCMSActions();
|
$actions = $record->getAllCMSActions();
|
||||||
} else {
|
} else {
|
||||||
$actions = $record->getCMSActions();
|
$actions = $record->getCMSActions();
|
||||||
|
|
||||||
// Find and remove action menus that have no actions.
|
// Find and remove action menus that have no actions.
|
||||||
if ($actions && $actions->count()) {
|
if ($actions && $actions->count()) {
|
||||||
/** @var TabSet $tabset */
|
/** @var TabSet $tabset */
|
||||||
$tabset = $actions->fieldByName('ActionMenus');
|
$tabset = $actions->fieldByName('ActionMenus');
|
||||||
if ($tabset) {
|
if ($tabset) {
|
||||||
foreach ($tabset->getChildren() as $tab) {
|
/** @var Tab $tab */
|
||||||
if (!$tab->getChildren()->count()) {
|
foreach ($tabset->getChildren() as $tab) {
|
||||||
$tabset->removeByName($tab->getName());
|
if (!$tab->getChildren()->count()) {
|
||||||
}
|
$tabset->removeByName($tab->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use <button> to allow full jQuery UI styling
|
|
||||||
$actionsFlattened = $actions->dataFields();
|
|
||||||
if ($actionsFlattened) {
|
|
||||||
/** @var FormAction $action */
|
|
||||||
foreach ($actionsFlattened as $action) {
|
|
||||||
$action->setUseButtonTag(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($record->hasMethod('getCMSValidator')) {
|
|
||||||
$validator = $record->getCMSValidator();
|
|
||||||
} else {
|
|
||||||
$validator = new RequiredFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Can't merge $FormAttributes in template at the moment
|
|
||||||
$form->addExtraClass('center ' . $this->BaseCSSClasses());
|
|
||||||
// Set validation exemptions for specific actions
|
|
||||||
$form->setValidationExemptActions(array('restore', 'revert', 'deletefromlive', 'delete', 'unpublish', 'rollback', 'doRollback'));
|
|
||||||
|
|
||||||
// Announce the capability so the frontend can decide whether to allow preview or not.
|
|
||||||
if ($record instanceof CMSPreviewable) {
|
|
||||||
$form->addExtraClass('cms-previewable');
|
|
||||||
}
|
|
||||||
$form->addExtraClass('fill-height flexbox-area-grow');
|
|
||||||
|
|
||||||
if (!$record->canEdit() || $deletedFromStage) {
|
|
||||||
$readonlyFields = $form->Fields()->makeReadonly();
|
|
||||||
$form->setFields($readonlyFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
$form->Fields()->setForm($form);
|
|
||||||
|
|
||||||
$this->extend('updateEditForm', $form);
|
|
||||||
return $form;
|
|
||||||
} elseif ($id) {
|
|
||||||
$form = Form::create($this, "EditForm", new FieldList(
|
|
||||||
new LabelField('PageDoesntExistLabel', _t('CMSMain.PAGENOTEXISTS', "This page doesn't exist"))
|
|
||||||
), new FieldList())->setHTMLID('Form_EditForm');
|
|
||||||
return $form;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use <button> to allow full jQuery UI styling
|
||||||
|
$actionsFlattened = $actions->dataFields();
|
||||||
|
if ($actionsFlattened) {
|
||||||
|
/** @var FormAction $action */
|
||||||
|
foreach ($actionsFlattened as $action) {
|
||||||
|
$action->setUseButtonTag(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Can't merge $FormAttributes in template at the moment
|
||||||
|
$form->addExtraClass('center ' . $this->BaseCSSClasses());
|
||||||
|
// Set validation exemptions for specific actions
|
||||||
|
$form->setValidationExemptActions(array('restore', 'revert', 'deletefromlive', 'delete', 'unpublish', 'rollback', 'doRollback'));
|
||||||
|
|
||||||
|
// Announce the capability so the frontend can decide whether to allow preview or not.
|
||||||
|
if ($record instanceof CMSPreviewable) {
|
||||||
|
$form->addExtraClass('cms-previewable');
|
||||||
|
}
|
||||||
|
$form->addExtraClass('fill-height flexbox-area-grow');
|
||||||
|
|
||||||
|
if (!$record->canEdit() || $deletedFromStage) {
|
||||||
|
$readonlyFields = $form->Fields()->makeReadonly();
|
||||||
|
$form->setFields($readonlyFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->Fields()->setForm($form);
|
||||||
|
|
||||||
|
$this->extend('updateEditForm', $form);
|
||||||
|
|
||||||
|
// Use custom reqest handler for LeftAndMain requests;
|
||||||
|
// CMS Forms cannot be identified solely by name, but also need ID (and sometimes OtherID)
|
||||||
|
$form->setRequestHandler(
|
||||||
|
LeftAndMainFormRequestHandler::create($form, [$id])
|
||||||
|
);
|
||||||
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function EmptyForm()
|
||||||
|
{
|
||||||
|
$fields = new FieldList(
|
||||||
|
new LabelField('PageDoesntExistLabel', _t('CMSMain.PAGENOTEXISTS', "This page doesn't exist"))
|
||||||
|
);
|
||||||
|
$form = parent::EmptyForm();
|
||||||
|
$form->setFields($fields);
|
||||||
|
$fields->setForm($form);
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an archive warning message based on the page's children
|
||||||
|
*
|
||||||
|
* @param SiteTree $record
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
protected function getArchiveWarningMessage($record)
|
protected function getArchiveWarningMessage($record)
|
||||||
{
|
{
|
||||||
// Get all page's descendants
|
// Get all page's descendants
|
||||||
@ -955,7 +1355,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$filterClass = $params['FilterClass'];
|
$filterClass = $params['FilterClass'];
|
||||||
if (!is_subclass_of($filterClass, 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter')) {
|
if (!is_subclass_of($filterClass, CMSSiteTreeFilter::class)) {
|
||||||
throw new InvalidArgumentException("Invalid filter class passed: {$filterClass}");
|
throw new InvalidArgumentException("Invalid filter class passed: {$filterClass}");
|
||||||
}
|
}
|
||||||
return $filterClass::create($params);
|
return $filterClass::create($params);
|
||||||
|
@ -16,7 +16,6 @@ use SilverStripe\Forms\SelectionGroup_Item;
|
|||||||
use SilverStripe\Forms\TreeDropdownField;
|
use SilverStripe\Forms\TreeDropdownField;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
use SilverStripe\ORM\ValidationException;
|
|
||||||
use SilverStripe\ORM\ValidationResult;
|
use SilverStripe\ORM\ValidationResult;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
|
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
namespace SilverStripe\CMS\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Forms\CheckboxField;
|
use SilverStripe\Forms\CheckboxField;
|
||||||
use SilverStripe\Forms\CompositeField;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\Forms\HiddenField;
|
use SilverStripe\Forms\HiddenField;
|
||||||
use SilverStripe\Forms\HTMLReadonlyField;
|
use SilverStripe\Forms\HTMLReadonlyField;
|
||||||
use SilverStripe\Forms\LiteralField;
|
use SilverStripe\Forms\LiteralField;
|
||||||
|
use SilverStripe\Forms\Tab;
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\View\ArrayData;
|
use SilverStripe\View\ArrayData;
|
||||||
@ -35,6 +35,7 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||||
|
|
||||||
private static $allowed_actions = array(
|
private static $allowed_actions = array(
|
||||||
|
'EditForm',
|
||||||
'VersionsForm',
|
'VersionsForm',
|
||||||
'CompareVersionsForm',
|
'CompareVersionsForm',
|
||||||
'show',
|
'show',
|
||||||
@ -42,15 +43,23 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
);
|
);
|
||||||
|
|
||||||
private static $url_handlers = array(
|
private static $url_handlers = array(
|
||||||
'$Action/$ID/$VersionID/$OtherVersionID' => 'handleAction'
|
'$Action/$ID/$VersionID/$OtherVersionID' => 'handleAction',
|
||||||
|
'EditForm/$ID/$VersionID' => 'EditForm',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current version ID for this request. Can be 0 for latest version
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $versionID = null;
|
||||||
|
|
||||||
public function getResponseNegotiator()
|
public function getResponseNegotiator()
|
||||||
{
|
{
|
||||||
$negotiator = parent::getResponseNegotiator();
|
$negotiator = parent::getResponseNegotiator();
|
||||||
$controller = $this;
|
$controller = $this;
|
||||||
$negotiator->setCallback('CurrentForm', function () use (&$controller) {
|
$negotiator->setCallback('CurrentForm', function () use (&$controller) {
|
||||||
$form = $controller->ShowVersionForm($controller->getRequest()->param('VersionID'));
|
$form = $controller->getEditForm();
|
||||||
if ($form) {
|
if ($form) {
|
||||||
return $form->forTemplate();
|
return $form->forTemplate();
|
||||||
} else {
|
} else {
|
||||||
@ -65,19 +74,30 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HTTPRequest $request
|
* @param HTTPRequest $request
|
||||||
* @return array
|
* @return HTTPResponse
|
||||||
*/
|
*/
|
||||||
public function show($request)
|
public function show($request)
|
||||||
{
|
{
|
||||||
$form = $this->ShowVersionForm($request->param('VersionID'));
|
// Record id and version for this request
|
||||||
|
$id = $request->param('ID');
|
||||||
|
$this->setCurrentPageID($id);
|
||||||
|
$versionID = $request->param('VersionID');
|
||||||
|
$this->setVersionID($versionID);
|
||||||
|
|
||||||
|
// Show id
|
||||||
|
$form = $this->getEditForm();
|
||||||
|
|
||||||
$negotiator = $this->getResponseNegotiator();
|
$negotiator = $this->getResponseNegotiator();
|
||||||
$controller = $this;
|
$controller = $this;
|
||||||
$negotiator->setCallback('CurrentForm', function () use (&$controller, &$form) {
|
$negotiator->setCallback('CurrentForm', function () use (&$controller, &$form) {
|
||||||
return $form ? $form->forTemplate() : $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
|
return $form
|
||||||
|
? $form->forTemplate()
|
||||||
|
: $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
|
||||||
});
|
});
|
||||||
$negotiator->setCallback('default', function () use (&$controller, &$form) {
|
$negotiator->setCallback('default', function () use (&$controller, &$form) {
|
||||||
return $controller->customise(array('EditForm' => $form))->renderWith($controller->getViewer('show'));
|
return $controller
|
||||||
|
->customise(array('EditForm' => $form))
|
||||||
|
->renderWith($controller->getViewer('show'));
|
||||||
});
|
});
|
||||||
|
|
||||||
return $negotiator->respond($request);
|
return $negotiator->respond($request);
|
||||||
@ -85,7 +105,7 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HTTPRequest $request
|
* @param HTTPRequest $request
|
||||||
* @return array
|
* @return HTTPResponse
|
||||||
*/
|
*/
|
||||||
public function compare($request)
|
public function compare($request)
|
||||||
{
|
{
|
||||||
@ -117,6 +137,24 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
public function EditForm($request = null)
|
||||||
|
{
|
||||||
|
if ($request) {
|
||||||
|
// Validate VersionID is present
|
||||||
|
$versionID = $request->param('VersionID');
|
||||||
|
if (!isset($versionID)) {
|
||||||
|
$this->httpError(400);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$this->setVersionID($versionID);
|
||||||
|
}
|
||||||
|
return parent::EditForm($request);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the read only version of the edit form. Detaches all {@link FormAction}
|
* Returns the read only version of the edit form. Detaches all {@link FormAction}
|
||||||
* instances attached since only action relates to revert.
|
* instances attached since only action relates to revert.
|
||||||
@ -135,11 +173,21 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
if (!$id) {
|
if (!$id) {
|
||||||
$id = $this->currentPageID();
|
$id = $this->currentPageID();
|
||||||
}
|
}
|
||||||
|
if (!$versionID) {
|
||||||
|
$versionID = $this->getVersionID();
|
||||||
|
}
|
||||||
|
|
||||||
$record = $this->getRecord($id, $versionID);
|
$record = $this->getRecord($id, $versionID);
|
||||||
$versionID = ($record) ? $record->Version : $versionID;
|
if (!$record) {
|
||||||
|
return $this->EmptyForm();
|
||||||
|
}
|
||||||
|
|
||||||
$form = parent::getEditForm($record, ($record) ? $record->getCMSFields() : null);
|
// Refresh version ID
|
||||||
|
$versionID = $record->Version;
|
||||||
|
$this->setVersionID($versionID);
|
||||||
|
|
||||||
|
// Get edit form
|
||||||
|
$form = parent::getEditForm($record, $record->getCMSFields());
|
||||||
// Respect permission failures from parent implementation
|
// Respect permission failures from parent implementation
|
||||||
if (!($form instanceof Form)) {
|
if (!($form instanceof Form)) {
|
||||||
return $form;
|
return $form;
|
||||||
@ -148,9 +196,14 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
// TODO: move to the SilverStripeNavigator structure so the new preview can pick it up.
|
// TODO: move to the SilverStripeNavigator structure so the new preview can pick it up.
|
||||||
//$nav = new SilverStripeNavigatorItem_ArchiveLink($record);
|
//$nav = new SilverStripeNavigatorItem_ArchiveLink($record);
|
||||||
|
|
||||||
$form->setActions(new FieldList(
|
$actions = new FieldList(
|
||||||
$revert = FormAction::create('doRollback', _t('CMSPageHistoryController.REVERTTOTHISVERSION', 'Revert to this version'))->setUseButtonTag(true)
|
$revert = FormAction::create(
|
||||||
));
|
'doRollback',
|
||||||
|
_t('CMSPageHistoryController.REVERTTOTHISVERSION', 'Revert to this version')
|
||||||
|
)->setUseButtonTag(true)
|
||||||
|
);
|
||||||
|
$actions->setForm($form);
|
||||||
|
$form->setActions($actions);
|
||||||
|
|
||||||
$fields = $form->Fields();
|
$fields = $form->Fields();
|
||||||
$fields->removeByName("Status");
|
$fields->removeByName("Status");
|
||||||
@ -189,7 +242,9 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$fields->fieldByName('Root.Main')->unshift(
|
/** @var Tab $mainTab */
|
||||||
|
$mainTab = $fields->fieldByName('Root.Main');
|
||||||
|
$mainTab->unshift(
|
||||||
new LiteralField('CurrentlyViewingMessage', ArrayData::create(array(
|
new LiteralField('CurrentlyViewingMessage', ArrayData::create(array(
|
||||||
'Content' => DBField::create_field('HTMLFragment', $message),
|
'Content' => DBField::create_field('HTMLFragment', $message),
|
||||||
'Classes' => 'notice'
|
'Classes' => 'notice'
|
||||||
@ -202,12 +257,17 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
"Version" => $versionID,
|
"Version" => $versionID,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (($record && $record->isLatestVersion())) {
|
if ($record->isLatestVersion()) {
|
||||||
$revert->setReadonly(true);
|
$revert->setReadonly(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$form->removeExtraClass('cms-content');
|
$form->removeExtraClass('cms-content');
|
||||||
|
|
||||||
|
// History form has both ID and VersionID as suffixes
|
||||||
|
$form->setRequestHandler(
|
||||||
|
LeftAndMainFormRequestHandler::create($form, [$id, $versionID])
|
||||||
|
);
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,28 +336,11 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
$hiddenID = new HiddenField('ID', false, "")
|
$hiddenID = new HiddenField('ID', false, "")
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
|
||||||
new FormAction(
|
|
||||||
'doCompare',
|
|
||||||
_t('CMSPageHistoryController.COMPAREVERSIONS', 'Compare Versions')
|
|
||||||
),
|
|
||||||
new FormAction(
|
|
||||||
'doShowVersion',
|
|
||||||
_t('CMSPageHistoryController.SHOWVERSION', 'Show Version')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use <button> to allow full jQuery UI styling
|
|
||||||
foreach ($actions->dataFields() as $action) {
|
|
||||||
/** @var FormAction $action */
|
|
||||||
$action->setUseButtonTag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$form = Form::create(
|
$form = Form::create(
|
||||||
$this,
|
$this,
|
||||||
'VersionsForm',
|
'VersionsForm',
|
||||||
$fields,
|
$fields,
|
||||||
$actions
|
new FieldList()
|
||||||
)->setHTMLID('Form_VersionsForm');
|
)->setHTMLID('Form_VersionsForm');
|
||||||
$form->loadDataFrom($this->getRequest()->requestVars());
|
$form->loadDataFrom($this->getRequest()->requestVars());
|
||||||
$hiddenID->setValue($id);
|
$hiddenID->setValue($id);
|
||||||
@ -311,97 +354,6 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the {@link VersionsForm} compare function between two pages.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse|DBHTMLText
|
|
||||||
*/
|
|
||||||
public function doCompare($data, $form)
|
|
||||||
{
|
|
||||||
$versions = $data['Versions'];
|
|
||||||
if (count($versions) < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$version1 = array_shift($versions);
|
|
||||||
$version2 = array_shift($versions);
|
|
||||||
|
|
||||||
$form = $this->CompareVersionsForm($version1, $version2);
|
|
||||||
|
|
||||||
// javascript solution, render into template
|
|
||||||
if ($this->getRequest()->isAjax()) {
|
|
||||||
return $this->customise(array(
|
|
||||||
"EditForm" => $form
|
|
||||||
))->renderWith(array(
|
|
||||||
static::class . '_EditForm',
|
|
||||||
'LeftAndMain_Content'
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// non javascript, redirect the user to the page
|
|
||||||
return $this->redirect(Controller::join_links(
|
|
||||||
$this->Link('compare'),
|
|
||||||
$version1,
|
|
||||||
$version2
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the {@link VersionsForm} show version function. Only requires
|
|
||||||
* one page to be selected.
|
|
||||||
*
|
|
||||||
* @param array
|
|
||||||
* @param Form
|
|
||||||
*
|
|
||||||
* @return DBHTMLText|HTTPResponse
|
|
||||||
*/
|
|
||||||
public function doShowVersion($data, $form)
|
|
||||||
{
|
|
||||||
$versionID = null;
|
|
||||||
|
|
||||||
if (isset($data['Versions']) && is_array($data['Versions'])) {
|
|
||||||
$versionID = array_shift($data['Versions']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$versionID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = $this->getRequest();
|
|
||||||
if ($request->isAjax()) {
|
|
||||||
return $this->customise(array(
|
|
||||||
"EditForm" => $this->ShowVersionForm($versionID)
|
|
||||||
))->renderWith(array(
|
|
||||||
static::class . '_EditForm',
|
|
||||||
'LeftAndMain_Content'
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// non javascript, redirect the user to the page
|
|
||||||
return $this->redirect(Controller::join_links(
|
|
||||||
$this->Link('version'),
|
|
||||||
$versionID
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int|null $versionID
|
|
||||||
* @return Form
|
|
||||||
*/
|
|
||||||
public function ShowVersionForm($versionID = null)
|
|
||||||
{
|
|
||||||
if (!$versionID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $this->currentPageID();
|
|
||||||
$form = $this->getEditForm($id, null, $versionID);
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $versionID
|
* @param int $versionID
|
||||||
* @param int $otherVersionID
|
* @param int $otherVersionID
|
||||||
@ -434,8 +386,8 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
$record = $page->compareVersions($fromVersion, $toVersion);
|
$record = $page->compareVersions($fromVersion, $toVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fromVersionRecord = Versioned::get_version('SilverStripe\\CMS\\Model\\SiteTree', $id, $fromVersion);
|
$fromVersionRecord = Versioned::get_version(SiteTree::class, $id, $fromVersion);
|
||||||
$toVersionRecord = Versioned::get_version('SilverStripe\\CMS\\Model\\SiteTree', $id, $toVersion);
|
$toVersionRecord = Versioned::get_version(SiteTree::class, $id, $toVersion);
|
||||||
|
|
||||||
if (!$fromVersionRecord) {
|
if (!$fromVersionRecord) {
|
||||||
user_error("Can't find version $fromVersion of page $id", E_USER_ERROR);
|
user_error("Can't find version $fromVersion of page $id", E_USER_ERROR);
|
||||||
@ -490,4 +442,26 @@ class CMSPageHistoryController extends CMSMain
|
|||||||
}
|
}
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set current version ID
|
||||||
|
*
|
||||||
|
* @param int $versionID
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setVersionID($versionID)
|
||||||
|
{
|
||||||
|
$this->versionID = $versionID;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current version ID
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getVersionID()
|
||||||
|
{
|
||||||
|
return $this->versionID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ namespace SilverStripe\CMS\Controllers;
|
|||||||
use SilverStripe\Admin\LeftAndMain_SearchFilter;
|
use SilverStripe\Admin\LeftAndMain_SearchFilter;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
use SilverStripe\Forms\DateField;
|
use SilverStripe\Forms\DateField;
|
||||||
use SilverStripe\ORM\DataList;
|
use SilverStripe\ORM\DataList;
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
|
||||||
@ -24,8 +24,7 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
*/
|
*/
|
||||||
abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter
|
abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter
|
||||||
{
|
{
|
||||||
|
use Injectable;
|
||||||
use \SilverStripe\Core\Injector\Injectable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search parameters, mostly properties on {@link SiteTree}.
|
* Search parameters, mostly properties on {@link SiteTree}.
|
||||||
|
200
code/Controllers/CMSTreeNode.php
Normal file
200
code/Controllers/CMSTreeNode.php
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\CMS\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\LeftAndMain_SearchFilter;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
use SilverStripe\View\ViewableData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around objects being displayed in a tree.
|
||||||
|
* Caution: Volatile API.
|
||||||
|
*
|
||||||
|
* @todo Implement recursive tree node rendering.
|
||||||
|
*/
|
||||||
|
class CMSTreeNode extends ViewableData
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object represented by this node
|
||||||
|
*
|
||||||
|
* @var SiteTree
|
||||||
|
*/
|
||||||
|
protected $obj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit link to the current record in the CMS
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $link;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if this is the currently selected node in the tree
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $isCurrent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of method to count the number of children
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $numChildrenMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var LeftAndMain_SearchFilter
|
||||||
|
*/
|
||||||
|
protected $filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Object $obj
|
||||||
|
* @param string $link
|
||||||
|
* @param bool $isCurrent
|
||||||
|
* @param string $numChildrenMethod
|
||||||
|
* @param LeftAndMain_SearchFilter $filter
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$obj,
|
||||||
|
$link = null,
|
||||||
|
$isCurrent = false,
|
||||||
|
$numChildrenMethod = 'numChildren',
|
||||||
|
$filter = null
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
$this->obj = $obj;
|
||||||
|
$this->link = $link;
|
||||||
|
$this->isCurrent = $isCurrent;
|
||||||
|
$this->numChildrenMethod = $numChildrenMethod;
|
||||||
|
$this->filter = $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns template, for further processing by {@link Hierarchy->getChildrenAsUL()}.
|
||||||
|
* Does not include closing tag to allow this method to inject its own children.
|
||||||
|
*
|
||||||
|
* @todo Remove hardcoded assumptions around returning an <li>, by implementing recursive tree node rendering
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function forTemplate()
|
||||||
|
{
|
||||||
|
$obj = $this->obj;
|
||||||
|
|
||||||
|
return (string)SSViewer::execute_template(
|
||||||
|
[ 'type' => 'Includes', self::class ],
|
||||||
|
$obj,
|
||||||
|
array(
|
||||||
|
'Classes' => $this->getClasses(),
|
||||||
|
'Link' => $this->getLink(),
|
||||||
|
'Title' => sprintf(
|
||||||
|
'(%s: %s) %s',
|
||||||
|
trim(_t('LeftAndMain.PAGETYPE', 'Page type'), " :"),
|
||||||
|
$obj->i18n_singular_name(),
|
||||||
|
$obj->Title
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the CSS classes to apply to this node
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getClasses()
|
||||||
|
{
|
||||||
|
// Get classes from object
|
||||||
|
$classes = $this->obj->CMSTreeClasses($this->numChildrenMethod);
|
||||||
|
if ($this->isCurrent) {
|
||||||
|
$classes .= ' current';
|
||||||
|
}
|
||||||
|
// Get status flag classes
|
||||||
|
$flags = $this->obj->hasMethod('getStatusFlags')
|
||||||
|
? $this->obj->getStatusFlags()
|
||||||
|
: false;
|
||||||
|
if ($flags) {
|
||||||
|
$statuses = array_keys($flags);
|
||||||
|
foreach ($statuses as $s) {
|
||||||
|
$classes .= ' status-' . $s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get additional filter classes
|
||||||
|
if ($this->filter && ($filterClasses = $this->filter->getPageClasses($this->obj))) {
|
||||||
|
if (is_array($filterClasses)) {
|
||||||
|
$filterClasses = implode(' ', $filterClasses);
|
||||||
|
}
|
||||||
|
$classes .= ' ' . $filterClasses;
|
||||||
|
}
|
||||||
|
return $classes ?: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get page backing this node
|
||||||
|
*
|
||||||
|
* @return SiteTree
|
||||||
|
*/
|
||||||
|
public function getObj()
|
||||||
|
{
|
||||||
|
return $this->obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set object backing this node
|
||||||
|
*
|
||||||
|
* @param SiteTree $obj
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setObj($obj)
|
||||||
|
{
|
||||||
|
$this->obj = $obj;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get link to this node
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLink()
|
||||||
|
{
|
||||||
|
return $this->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set link to this node
|
||||||
|
*
|
||||||
|
* @param string $link
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLink($link)
|
||||||
|
{
|
||||||
|
$this->link = $link;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this is the currently selected node
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getIsCurrent()
|
||||||
|
{
|
||||||
|
return $this->isCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this node to current, or not current
|
||||||
|
*
|
||||||
|
* @param bool $bool
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setIsCurrent($bool)
|
||||||
|
{
|
||||||
|
$this->isCurrent = $bool;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -314,6 +314,8 @@ de:
|
|||||||
SINGULARNAME: Weiterleitungsseite
|
SINGULARNAME: Weiterleitungsseite
|
||||||
SilverStripe\CMS\Model\SiteTree:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
DESCRIPTION: 'Allgemeine Inhaltsseite'
|
DESCRIPTION: 'Allgemeine Inhaltsseite'
|
||||||
|
SINGULARNAME: Seite
|
||||||
|
PLURALNAME: Seiten
|
||||||
SilverStripe\CMS\Model\VirtualPage:
|
SilverStripe\CMS\Model\VirtualPage:
|
||||||
DESCRIPTION: 'Zeigt den Inhalt einer anderen Seite an'
|
DESCRIPTION: 'Zeigt den Inhalt einer anderen Seite an'
|
||||||
PLURALNAME: 'Virtuelle Seiten'
|
PLURALNAME: 'Virtuelle Seiten'
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
<li id="record-$ID" data-id="$ID" data-pagetype="$ClassName" class="$Classes"><ins class="jstree-icon"> </ins>
|
||||||
|
<a href="$Link" title="$Title.ATT"><ins class="jstree-icon"> </ins>
|
||||||
|
<span class="text">$TreeTitle</span>
|
||||||
|
</a>
|
130
tests/controller/CMSTreeTest.php
Normal file
130
tests/controller/CMSTreeTest.php
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for tree-operations refactored out of LeftAndMain
|
||||||
|
*/
|
||||||
|
class CMSTreeTest extends FunctionalTest
|
||||||
|
{
|
||||||
|
protected static $fixture_file = 'CMSTreeTest.yml';
|
||||||
|
|
||||||
|
public function testSaveTreeNodeSorting()
|
||||||
|
{
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
// forcing sorting for non-MySQL
|
||||||
|
$rootPages = SiteTree::get()
|
||||||
|
->filter("ParentID", 0)
|
||||||
|
->sort('"ID"');
|
||||||
|
$siblingIDs = $rootPages->column('ID');
|
||||||
|
$page1 = $rootPages->offsetGet(0);
|
||||||
|
$page2 = $rootPages->offsetGet(1);
|
||||||
|
$page3 = $rootPages->offsetGet(2);
|
||||||
|
|
||||||
|
// Move page2 before page1
|
||||||
|
$siblingIDs[0] = $page2->ID;
|
||||||
|
$siblingIDs[1] = $page1->ID;
|
||||||
|
$data = array(
|
||||||
|
'SiblingIDs' => $siblingIDs,
|
||||||
|
'ID' => $page2->ID,
|
||||||
|
'ParentID' => 0
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->post('admin/pages/edit/savetreenode', $data);
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
/** @var SiteTree $page1 */
|
||||||
|
$page1 = SiteTree::get()->byID($page1->ID);
|
||||||
|
/** @var SiteTree $page2 */
|
||||||
|
$page2 = SiteTree::get()->byID($page2->ID);
|
||||||
|
/** @var SiteTree $page3 */
|
||||||
|
$page3 = SiteTree::get()->byID($page3->ID);
|
||||||
|
|
||||||
|
$this->assertEquals(2, $page1->Sort, 'Page1 is sorted after Page2');
|
||||||
|
$this->assertEquals(1, $page2->Sort, 'Page2 is sorted before Page1');
|
||||||
|
$this->assertEquals(3, $page3->Sort, 'Sort order for other pages is unaffected');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaveTreeNodeParentID()
|
||||||
|
{
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
|
$page31 = $this->objFromFixture(SiteTree::class, 'page31');
|
||||||
|
$page32 = $this->objFromFixture(SiteTree::class, 'page32');
|
||||||
|
|
||||||
|
// Move page2 into page3, between page3.1 and page 3.2
|
||||||
|
$siblingIDs = array(
|
||||||
|
$page31->ID,
|
||||||
|
$page2->ID,
|
||||||
|
$page32->ID
|
||||||
|
);
|
||||||
|
$data = array(
|
||||||
|
'SiblingIDs' => $siblingIDs,
|
||||||
|
'ID' => $page2->ID,
|
||||||
|
'ParentID' => $page3->ID
|
||||||
|
);
|
||||||
|
$response = $this->post('admin/pages/edit/savetreenode', $data);
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
$page2 = DataObject::get_by_id(SiteTree::class, $page2->ID, false);
|
||||||
|
$page31 = DataObject::get_by_id(SiteTree::class, $page31->ID, false);
|
||||||
|
$page32 = DataObject::get_by_id(SiteTree::class, $page32->ID, false);
|
||||||
|
|
||||||
|
$this->assertEquals($page3->ID, $page2->ParentID, 'Moved page gets new parent');
|
||||||
|
$this->assertEquals(1, $page31->Sort, 'Children pages before insertaion are unaffected');
|
||||||
|
$this->assertEquals(2, $page2->Sort, 'Moved page is correctly sorted');
|
||||||
|
$this->assertEquals(3, $page32->Sort, 'Children pages after insertion are resorted');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test {@see CMSMain::updatetreenodes}
|
||||||
|
*/
|
||||||
|
public function testUpdateTreeNodes()
|
||||||
|
{
|
||||||
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
|
$page31 = $this->objFromFixture(SiteTree::class, 'page31');
|
||||||
|
$page32 = $this->objFromFixture(SiteTree::class, 'page32');
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
// Check page
|
||||||
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids='.$page1->ID);
|
||||||
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
|
$data = json_decode($result->getBody(), true);
|
||||||
|
$pageData = $data[$page1->ID];
|
||||||
|
$this->assertEquals(0, $pageData['ParentID']);
|
||||||
|
$this->assertEquals($page2->ID, $pageData['NextID']);
|
||||||
|
$this->assertEmpty($pageData['PrevID']);
|
||||||
|
|
||||||
|
// check subpage
|
||||||
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids='.$page31->ID);
|
||||||
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
|
$data = json_decode($result->getBody(), true);
|
||||||
|
$pageData = $data[$page31->ID];
|
||||||
|
$this->assertEquals($page3->ID, $pageData['ParentID']);
|
||||||
|
$this->assertEquals($page32->ID, $pageData['NextID']);
|
||||||
|
$this->assertEmpty($pageData['PrevID']);
|
||||||
|
|
||||||
|
// Multiple pages
|
||||||
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids='.$page1->ID.','.$page2->ID);
|
||||||
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
|
$data = json_decode($result->getBody(), true);
|
||||||
|
$this->assertEquals(2, count($data));
|
||||||
|
|
||||||
|
// Invalid IDs
|
||||||
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids=-3');
|
||||||
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
|
$data = json_decode($result->getBody(), true);
|
||||||
|
$this->assertEquals(0, count($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
tests/controller/CMSTreeTest.yml
Normal file
91
tests/controller/CMSTreeTest.yml
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
|
page1:
|
||||||
|
Title: Page 1
|
||||||
|
Sort: 1
|
||||||
|
page2:
|
||||||
|
Title: Page 2
|
||||||
|
Sort: 2
|
||||||
|
page3:
|
||||||
|
Title: Page 3
|
||||||
|
Sort: 3
|
||||||
|
page31:
|
||||||
|
Title: Page 3.1
|
||||||
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
|
||||||
|
Sort: 1
|
||||||
|
page32:
|
||||||
|
Title: Page 3.2
|
||||||
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
|
||||||
|
Sort: 2
|
||||||
|
page4:
|
||||||
|
Title: Page 4
|
||||||
|
Sort: 4
|
||||||
|
page5:
|
||||||
|
Title: Page 5
|
||||||
|
Sort: 5
|
||||||
|
page6:
|
||||||
|
Title: Page 6
|
||||||
|
Sort: 6
|
||||||
|
page7:
|
||||||
|
Title: Page 7
|
||||||
|
Sort: 7
|
||||||
|
page8:
|
||||||
|
Title: Page 8
|
||||||
|
Sort: 8
|
||||||
|
page9:
|
||||||
|
Title: Page 9
|
||||||
|
Sort: 9
|
||||||
|
page10:
|
||||||
|
Title: Page 10
|
||||||
|
Sort: 10
|
||||||
|
page11:
|
||||||
|
Title: Page 11
|
||||||
|
Sort: 11
|
||||||
|
page12:
|
||||||
|
Title: Page 12
|
||||||
|
Sort: 12
|
||||||
|
page13:
|
||||||
|
Title: Page 13
|
||||||
|
Sort: 13
|
||||||
|
page14:
|
||||||
|
Title: Page 14
|
||||||
|
Sort: 14
|
||||||
|
page15:
|
||||||
|
Title: Page 15
|
||||||
|
Sort: 15
|
||||||
|
page16:
|
||||||
|
Title: Page 16
|
||||||
|
Sort: 16
|
||||||
|
page17:
|
||||||
|
Title: Page 17
|
||||||
|
Sort: 17
|
||||||
|
page18:
|
||||||
|
Title: Page 18
|
||||||
|
Sort: 18
|
||||||
|
page19:
|
||||||
|
Title: Page 19
|
||||||
|
Sort: 19
|
||||||
|
page20:
|
||||||
|
Title: Page 20
|
||||||
|
Sort: 20
|
||||||
|
page21:
|
||||||
|
Title: Page 21
|
||||||
|
Sort: 21
|
||||||
|
page22:
|
||||||
|
Title: Page 22
|
||||||
|
Sort: 22
|
||||||
|
page23:
|
||||||
|
Title: Page 23
|
||||||
|
Sort: 23
|
||||||
|
page24:
|
||||||
|
Title: Page 24
|
||||||
|
Sort: 24
|
||||||
|
page25:
|
||||||
|
Title: Page 25
|
||||||
|
Sort: 25
|
||||||
|
page26:
|
||||||
|
Title: Page 26
|
||||||
|
Sort: 26
|
||||||
|
home:
|
||||||
|
Title: Home
|
||||||
|
URLSegment: home
|
||||||
|
Sort: 0
|
Loading…
Reference in New Issue
Block a user