mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
API Update to use new Versioned API
API CMS notifications for recursive operations API Un-deprecated delete action and batch actions API remove deprecated API
This commit is contained in:
parent
849cd898a4
commit
4cc7b0806d
@ -119,7 +119,6 @@ class CMSBatchAction_Restore extends CMSBatchAction {
|
||||
*
|
||||
* @package cms
|
||||
* @subpackage batchaction
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
class CMSBatchAction_Delete extends CMSBatchAction {
|
||||
public function getActionTitle() {
|
||||
@ -127,7 +126,6 @@ class CMSBatchAction_Delete extends CMSBatchAction {
|
||||
}
|
||||
|
||||
public function run(SS_List $pages) {
|
||||
Deprecation::notice('4.0', 'Delete is deprecated. Use Archive instead');
|
||||
$status = array(
|
||||
'modified'=>array(),
|
||||
'deleted'=>array(),
|
||||
@ -163,53 +161,3 @@ class CMSBatchAction_Delete extends CMSBatchAction {
|
||||
return $this->applicablePagesHelper($ids, 'canDelete', true, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublish (delete from live site) items batch action.
|
||||
*
|
||||
* @package cms
|
||||
* @subpackage batchaction
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
class CMSBatchAction_DeleteFromLive extends CMSBatchAction {
|
||||
public function getActionTitle() {
|
||||
return _t('CMSBatchActions.DELETE_PAGES', 'Delete from published site');
|
||||
}
|
||||
|
||||
public function run(SS_List $pages) {
|
||||
Deprecation::notice('4.0', 'Delete From Live is deprecated. Use Unpublish instead');
|
||||
$status = array(
|
||||
'modified'=>array(),
|
||||
'deleted'=>array()
|
||||
);
|
||||
|
||||
/** @var SiteTree $page */
|
||||
foreach($pages as $page) {
|
||||
$id = $page->ID;
|
||||
|
||||
// Perform the action
|
||||
if($page->canUnpublish()) {
|
||||
$page->doUnpublish();
|
||||
}
|
||||
|
||||
// check to see if the record exists on the stage site, if it doesn't remove the tree node
|
||||
$stageRecord = Versioned::get_one_by_stage( 'SiteTree', 'Stage', array(
|
||||
'"SiteTree"."ID"' => $id
|
||||
));
|
||||
if($stageRecord) {
|
||||
$status['modified'][$stageRecord->ID] = array(
|
||||
'TreeTitle' => $stageRecord->TreeTitle,
|
||||
);
|
||||
} else {
|
||||
$status['deleted'][$id] = array();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->response(_t('CMSBatchActions.DELETED_PAGES', 'Deleted %d pages from published site, %d failures'), $status);
|
||||
}
|
||||
|
||||
public function applicablePages($ids) {
|
||||
return $this->applicablePagesHelper($ids, 'canDelete', false, true);
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
Versioned::reading_stage("Stage");
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
|
||||
Requirements::javascript(CMS_DIR . "/javascript/dist/AssetAdmin.js");
|
||||
Requirements::add_i18n_javascript(CMS_DIR . '/javascript/lang', false, true);
|
||||
|
@ -58,14 +58,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
'childfilter',
|
||||
);
|
||||
|
||||
/**
|
||||
* Enable legacy batch actions.
|
||||
* @deprecated since version 4.0
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
private static $enabled_legacy_actions = array();
|
||||
|
||||
public function init() {
|
||||
// set reading lang
|
||||
if(SiteTree::has_extension('Translatable') && !$this->getRequest()->isAjax()) {
|
||||
@ -92,25 +84,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
CMSBatchActionHandler::register('publish', 'CMSBatchAction_Publish');
|
||||
CMSBatchActionHandler::register('unpublish', 'CMSBatchAction_Unpublish');
|
||||
|
||||
|
||||
// Check legacy actions
|
||||
$legacy = $this->config()->enabled_legacy_actions;
|
||||
|
||||
// Delete from live is unnecessary since we have unpublish which does the same thing
|
||||
if(in_array('CMSBatchAction_DeleteFromLive', $legacy)) {
|
||||
Deprecation::notice('4.0', 'Delete From Live is deprecated. Use Un-publish instead');
|
||||
CMSBatchActionHandler::register('deletefromlive', 'CMSBatchAction_DeleteFromLive');
|
||||
}
|
||||
|
||||
// Delete action
|
||||
if(in_array('CMSBatchAction_Delete', $legacy)) {
|
||||
Deprecation::notice('4.0', 'Delete from Stage is deprecated. Use Archive instead.');
|
||||
CMSBatchActionHandler::register('delete', 'CMSBatchAction_Delete');
|
||||
} else {
|
||||
CMSBatchActionHandler::register('archive', 'CMSBatchAction_Archive');
|
||||
CMSBatchActionHandler::register('restore', 'CMSBatchAction_Restore');
|
||||
}
|
||||
CMSBatchActionHandler::register('delete', 'CMSBatchAction_Delete');
|
||||
CMSBatchActionHandler::register('archive', 'CMSBatchAction_Archive');
|
||||
CMSBatchActionHandler::register('restore', 'CMSBatchAction_Restore');
|
||||
}
|
||||
|
||||
public function index($request) {
|
||||
@ -555,7 +531,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Then, try getting a record from the live site
|
||||
if(!$record) {
|
||||
// $record = Versioned::get_one_by_stage($treeClass, "Live", "\"$treeClass\".\"ID\" = $id");
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
singleton($treeClass)->flushCache();
|
||||
|
||||
$record = DataObject::get_by_id($treeClass, $id);
|
||||
@ -872,6 +848,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
/**
|
||||
* Save and Publish page handler
|
||||
*
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
* @return SS_HTTPResponse
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function save($data, $form) {
|
||||
$className = $this->stat('tree_class');
|
||||
@ -879,19 +860,35 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Existing or new record?
|
||||
$id = $data['ID'];
|
||||
if(substr($id,0,3) != 'new') {
|
||||
/** @var SiteTree $record */
|
||||
$record = DataObject::get_by_id($className, $id);
|
||||
if($record && !$record->canEdit()) return Security::permissionFailure($this);
|
||||
if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
// Check edit permissions
|
||||
if($record && !$record->canEdit()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
if(!$record || !$record->ID) {
|
||||
throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
}
|
||||
} else {
|
||||
if(!singleton($this->stat('tree_class'))->canCreate()) return Security::permissionFailure($this);
|
||||
if(!$className::singleton()->canCreate()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
$record = $this->getNewItem($id, false);
|
||||
}
|
||||
|
||||
// Check publishing permissions
|
||||
$doPublish = !empty($data['publish']);
|
||||
if($record && $doPublish && !$record->canPublish()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
// TODO Coupling to SiteTree
|
||||
$record->HasBrokenLink = 0;
|
||||
$record->HasBrokenFile = 0;
|
||||
|
||||
if (!$record->ObsoleteClassName) $record->writeWithoutVersion();
|
||||
if (!$record->ObsoleteClassName) {
|
||||
$record->writeWithoutVersion();
|
||||
}
|
||||
|
||||
// Update the class instance if necessary
|
||||
if(isset($data['ClassName']) && $data['ClassName'] != $record->ClassName) {
|
||||
@ -909,10 +906,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$record->write();
|
||||
|
||||
// If the 'Save & Publish' button was clicked, also publish the page
|
||||
if (isset($data['publish']) && $data['publish'] == 1) {
|
||||
if ($doPublish) {
|
||||
$record->doPublish();
|
||||
$message = _t(
|
||||
'CMSMain.PUBLISHED',
|
||||
"Published '{title}' successfully.",
|
||||
['title' => $record->Title]
|
||||
);
|
||||
} else {
|
||||
$message = _t(
|
||||
'CMSMain.SAVED',
|
||||
"Saved '{title}' successfully.",
|
||||
['title' => $record->Title]
|
||||
);
|
||||
}
|
||||
|
||||
$this->getResponse()->addHeader('X-Status', rawurlencode($message));
|
||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||
}
|
||||
|
||||
@ -967,98 +976,56 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return $newItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the page from live. This means a page in draft mode might still exist.
|
||||
*
|
||||
* @see delete()
|
||||
*/
|
||||
public function deletefromlive($data, $form) {
|
||||
Versioned::reading_stage('Live');
|
||||
|
||||
/** @var SiteTree $record */
|
||||
$record = DataObject::get_by_id("SiteTree", $data['ID']);
|
||||
if($record && !($record->canDelete() && $record->canUnpublish())) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$descendantsRemoved = 0;
|
||||
$recordTitle = $record->Title;
|
||||
|
||||
// before deleting the records, get the descendants of this tree
|
||||
if($record) {
|
||||
$descendantIDs = $record->getDescendantIDList();
|
||||
|
||||
// then delete them from the live site too
|
||||
$descendantsRemoved = 0;
|
||||
foreach( $descendantIDs as $descID )
|
||||
/** @var SiteTree $descendant */
|
||||
if( $descendant = DataObject::get_by_id('SiteTree', $descID) ) {
|
||||
$descendant->doUnpublish();
|
||||
$descendantsRemoved++;
|
||||
}
|
||||
|
||||
// delete the record
|
||||
$record->doUnpublish();
|
||||
}
|
||||
|
||||
Versioned::reading_stage('Stage');
|
||||
|
||||
if(isset($descendantsRemoved)) {
|
||||
$descRemoved = ' ' . _t(
|
||||
'CMSMain.DESCREMOVED',
|
||||
'and {count} descendants',
|
||||
array('count' => $descendantsRemoved)
|
||||
);
|
||||
} else {
|
||||
$descRemoved = '';
|
||||
}
|
||||
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(
|
||||
_t(
|
||||
'CMSMain.REMOVED',
|
||||
'Deleted \'{title}\'{description} from live site',
|
||||
array('title' => $recordTitle, 'description' => $descRemoved)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Even if the record has been deleted from stage and live, it can be viewed in "archive mode"
|
||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually perform the publication step
|
||||
*
|
||||
* @param Versioned|DataObject $record
|
||||
* @return mixed
|
||||
*/
|
||||
public function performPublish($record) {
|
||||
if($record && !$record->canPublish()) return Security::permissionFailure($this);
|
||||
if($record && !$record->canPublish()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$record->doPublish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts a page by publishing it to live.
|
||||
* Use {@link restorepage()} if you want to restore a page
|
||||
* which was deleted from draft without publishing.
|
||||
*
|
||||
* @uses SiteTree->doRevertToLive()
|
||||
* Reverts a page by publishing it to live.
|
||||
* Use {@link restorepage()} if you want to restore a page
|
||||
* which was deleted from draft without publishing.
|
||||
*
|
||||
* @uses SiteTree->doRevertToLive()
|
||||
*
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
* @return SS_HTTPResponse
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function revert($data, $form) {
|
||||
if(!isset($data['ID'])) return new SS_HTTPResponse("Please pass an ID in the form content", 400);
|
||||
if(!isset($data['ID'])) {
|
||||
throw new SS_HTTPResponse_Exception("Please pass an ID in the form content", 400);
|
||||
}
|
||||
|
||||
$id = (int) $data['ID'];
|
||||
$restoredPage = Versioned::get_latest_version("SiteTree", $id);
|
||||
if(!$restoredPage) return new SS_HTTPResponse("SiteTree #$id not found", 400);
|
||||
if(!$restoredPage) {
|
||||
throw new SS_HTTPResponse_Exception("SiteTree #$id not found", 400);
|
||||
}
|
||||
|
||||
/** @var SiteTree $record */
|
||||
$record = Versioned::get_one_by_stage('SiteTree', 'Live', array(
|
||||
'"SiteTree_Live"."ID"' => $id
|
||||
));
|
||||
|
||||
// a user can restore a page without publication rights, as it just adds a new draft state
|
||||
// (this action should just be available when page has been "deleted from draft")
|
||||
if($record && !$record->canEdit()) return Security::permissionFailure($this);
|
||||
if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
if($record && !$record->canEdit()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
if(!$record || !$record->ID) {
|
||||
throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
}
|
||||
|
||||
$record->doRevertToLive();
|
||||
|
||||
@ -1077,14 +1044,23 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
/**
|
||||
* Delete the current page from draft stage.
|
||||
*
|
||||
* @see deletefromlive()
|
||||
*
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
* @return SS_HTTPResponse
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function delete($data, $form) {
|
||||
Deprecation::notice('4.0', 'Delete from stage is deprecated. Use archive instead');
|
||||
$id = $data['ID'];
|
||||
$record = DataObject::get_by_id("SiteTree", $id);
|
||||
if($record && !$record->canDelete()) return Security::permissionFailure();
|
||||
if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
if($record && !$record->canDelete()) {
|
||||
return Security::permissionFailure();
|
||||
}
|
||||
if(!$record || !$record->ID) {
|
||||
throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
}
|
||||
|
||||
// Delete record
|
||||
$record->delete();
|
||||
@ -1103,9 +1079,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
*
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
* @return SS_HTTPResponse
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function archive($data, $form) {
|
||||
$id = $data['ID'];
|
||||
/** @var SiteTree $record */
|
||||
$record = DataObject::get_by_id("SiteTree", $id);
|
||||
if(!$record || !$record->exists()) {
|
||||
throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
@ -1167,10 +1146,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
/**
|
||||
* Rolls a site back to a given version ID
|
||||
*
|
||||
* @param array
|
||||
* @param Form
|
||||
*
|
||||
* @return html
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function doRollback($data, $form) {
|
||||
$this->extend('onBeforeRollback', $data['ID']);
|
||||
@ -1178,8 +1156,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$id = (isset($data['ID'])) ? (int) $data['ID'] : null;
|
||||
$version = (isset($data['Version'])) ? (int) $data['Version'] : null;
|
||||
|
||||
/** @var DataObject|Versioned $record */
|
||||
$record = DataObject::get_by_id($this->stat('tree_class'), $id);
|
||||
if($record && !$record->canEdit()) return Security::permissionFailure($this);
|
||||
if($record && !$record->canEdit()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
if($version) {
|
||||
$record->doRollbackTo($version);
|
||||
|
@ -297,7 +297,7 @@ class SilverStripeNavigatorItem_StageLink extends SilverStripeNavigatorItem {
|
||||
|
||||
public function isActive() {
|
||||
return (
|
||||
Versioned::current_stage() == 'Stage'
|
||||
Versioned::get_stage() == 'Stage'
|
||||
&& !(ClassInfo::exists('SiteTreeFutureState') && SiteTreeFutureState::get_future_datetime())
|
||||
&& !$this->isArchived()
|
||||
);
|
||||
@ -350,7 +350,7 @@ class SilverStripeNavigatorItem_LiveLink extends SilverStripeNavigatorItem {
|
||||
|
||||
public function isActive() {
|
||||
return (
|
||||
(!Versioned::current_stage() || Versioned::current_stage() == 'Live')
|
||||
(!Versioned::get_stage() || Versioned::get_stage() == 'Live')
|
||||
&& !$this->isArchived()
|
||||
);
|
||||
}
|
||||
|
@ -132,6 +132,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
"EditorGroups" => "Group",
|
||||
);
|
||||
|
||||
private static $has_many = array(
|
||||
"VirtualPages" => "VirtualPage.CopyContentFrom"
|
||||
);
|
||||
|
||||
private static $owned_by = array(
|
||||
"VirtualPages"
|
||||
);
|
||||
|
||||
private static $casting = array(
|
||||
"Breadcrumbs" => "HTMLText",
|
||||
"LastEdited" => "SS_Datetime",
|
||||
@ -181,7 +189,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
|
||||
private static $extensions = array(
|
||||
"Hierarchy",
|
||||
"Versioned('Stage', 'Live')",
|
||||
"Versioned",
|
||||
"SiteTreeLinkTracking"
|
||||
);
|
||||
|
||||
@ -233,77 +241,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
|
||||
protected $_cache_statusFlags = null;
|
||||
|
||||
/**
|
||||
* Determines if the system should avoid orphaned pages
|
||||
* by deleting all children when the their parent is deleted (TRUE),
|
||||
* or rather preserve this data even if its not reachable through any navigation path (FALSE).
|
||||
*
|
||||
* @deprecated 4.0 Use the "SiteTree.enforce_strict_hierarchy" config setting instead
|
||||
* @param boolean
|
||||
*/
|
||||
static public function set_enforce_strict_hierarchy($to) {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.enforce_strict_hierarchy" config setting instead');
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Use the "SiteTree.enforce_strict_hierarchy" config setting instead
|
||||
* @return boolean
|
||||
*/
|
||||
static public function get_enforce_strict_hierarchy() {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.enforce_strict_hierarchy" config setting instead');
|
||||
return Config::inst()->get('SiteTree', 'enforce_strict_hierarchy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if nested URLs (e.g. page/sub-page/) are currently enabled on this site.
|
||||
*
|
||||
* @deprecated 4.0 Use the "SiteTree.nested_urls" config setting instead
|
||||
* @return bool
|
||||
*/
|
||||
static public function nested_urls() {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.nested_urls" config setting instead');
|
||||
return Config::inst()->get('SiteTree', 'nested_urls');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Use the "SiteTree.nested_urls" config setting instead
|
||||
*/
|
||||
static public function enable_nested_urls() {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.nested_urls" config setting instead');
|
||||
Config::inst()->update('SiteTree', 'nested_urls', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Use the "SiteTree.nested_urls" config setting instead
|
||||
*/
|
||||
static public function disable_nested_urls() {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.nested_urls" config setting instead');
|
||||
Config::inst()->update('SiteTree', 'nested_urls', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the (re)creation of default pages on /dev/build
|
||||
*
|
||||
* @deprecated 4.0 Use the "SiteTree.create_default_pages" config setting instead
|
||||
* @param bool $option
|
||||
*/
|
||||
static public function set_create_default_pages($option = true) {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.create_default_pages" config setting instead');
|
||||
Config::inst()->update('SiteTree', 'create_default_pages', $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if default pages should be created on /dev/build.
|
||||
*
|
||||
* @deprecated 4.0 Use the "SiteTree.create_default_pages" config setting instead
|
||||
* @return bool
|
||||
*/
|
||||
static public function get_create_default_pages() {
|
||||
Deprecation::notice('4.0', 'Use the "SiteTree.create_default_pages" config setting instead');
|
||||
return Config::inst()->get('SiteTree', 'create_default_pages');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the {@link SiteTree} object that maps to a link.
|
||||
*
|
||||
@ -439,7 +376,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
!($page = DataObject::get_by_id('SiteTree', $arguments['id'])) // Get the current page by ID.
|
||||
&& !($page = Versioned::get_latest_version('SiteTree', $arguments['id'])) // Attempt link to old version.
|
||||
) {
|
||||
return; // There were no suitable matches at all.
|
||||
return null; // There were no suitable matches at all.
|
||||
}
|
||||
|
||||
$link = Convert::raw2att($page->Link());
|
||||
@ -538,9 +475,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteLiveLink($includeStageEqualsLive = true) {
|
||||
$oldStage = Versioned::current_stage();
|
||||
Versioned::reading_stage('Live');
|
||||
$live = Versioned::get_one_by_stage('SiteTree', 'Live', array(
|
||||
$oldStage = Versioned::get_stage();
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$live = Versioned::get_one_by_stage('SiteTree', Versioned::LIVE, array(
|
||||
'"SiteTree"."ID"' => $this->ID
|
||||
));
|
||||
if($live) {
|
||||
@ -550,7 +487,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$link = null;
|
||||
}
|
||||
|
||||
Versioned::reading_stage($oldStage);
|
||||
Versioned::set_stage($oldStage);
|
||||
return $link;
|
||||
}
|
||||
|
||||
@ -1067,22 +1004,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function canDeleteFromLive($member = null) {
|
||||
Deprecation::notice('4.0', 'Use canUnpublish');
|
||||
|
||||
// Deprecated extension
|
||||
$extended = $this->extendedCan('canDeleteFromLive', $member);
|
||||
if($extended !== null) {
|
||||
Deprecation::notice('4.0', 'Use canUnpublish in your extension instead');
|
||||
return $extended;
|
||||
}
|
||||
|
||||
return $this->canUnpublish($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub method to get the site config, unless the current class can provide an alternate.
|
||||
*
|
||||
@ -1531,26 +1452,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$this->extend('augmentSyncLinkTracking');
|
||||
}
|
||||
|
||||
public function onAfterWrite() {
|
||||
// Need to flush cache to avoid outdated versionnumber references
|
||||
$this->flushCache();
|
||||
|
||||
$linkedPages = $this->VirtualPages();
|
||||
if($linkedPages) {
|
||||
// The only way after a write() call to determine if it was triggered by a writeWithoutVersion(),
|
||||
// which we have to pass on to the virtual page writes as well.
|
||||
$previous = ($this->Version > 1) ? Versioned::get_version($this->class, $this->ID, $this->Version-1) : null;
|
||||
$withoutVersion = $this->getExtensionInstance('Versioned')->_nextWriteWithoutVersion;
|
||||
foreach($linkedPages as $page) {
|
||||
$page->copyFrom($page->CopyContentFrom());
|
||||
if($withoutVersion) $page->writeWithoutVersion();
|
||||
else $page->write();
|
||||
}
|
||||
}
|
||||
|
||||
parent::onAfterWrite();
|
||||
}
|
||||
|
||||
public function onBeforeDelete() {
|
||||
parent::onBeforeDelete();
|
||||
|
||||
@ -1693,7 +1594,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* @return string
|
||||
*/
|
||||
public function getStageURLSegment() {
|
||||
$stageRecord = Versioned::get_one_by_stage('SiteTree', 'Stage', array(
|
||||
$stageRecord = Versioned::get_one_by_stage('SiteTree', Versioned::DRAFT, array(
|
||||
'"SiteTree"."ID"' => $this->ID
|
||||
));
|
||||
return ($stageRecord) ? $stageRecord->URLSegment : null;
|
||||
@ -1705,7 +1606,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* @return string
|
||||
*/
|
||||
public function getLiveURLSegment() {
|
||||
$liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', array(
|
||||
$liveRecord = Versioned::get_one_by_stage('SiteTree', Versioned::LIVE, array(
|
||||
'"SiteTree"."ID"' => $this->ID
|
||||
));
|
||||
return ($liveRecord) ? $liveRecord->URLSegment : null;
|
||||
@ -1720,7 +1621,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
*/
|
||||
public function rewriteFileLinks() {
|
||||
// Skip live stage
|
||||
if(\Versioned::current_stage() === \Versioned::get_live_stage()) {
|
||||
if(\Versioned::get_stage() === \Versioned::LIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1814,26 +1715,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* @return DataList
|
||||
*/
|
||||
public function VirtualPages() {
|
||||
$pages = parent::VirtualPages();
|
||||
|
||||
// Ignore new records
|
||||
if(!$this->ID) return null;
|
||||
|
||||
// Check subsite virtual pages
|
||||
// @todo Refactor out subsite module specific code
|
||||
if(class_exists('Subsite')) {
|
||||
return Subsite::get_from_all_subsites('VirtualPage', array(
|
||||
'"VirtualPage"."CopyContentFromID"' => $this->ID
|
||||
));
|
||||
// Disable subsite filter for these pages
|
||||
if($pages instanceof DataList) {
|
||||
return $pages->setDataQueryParam('Subsite.filter', false);
|
||||
} else {
|
||||
return $pages;
|
||||
}
|
||||
|
||||
// Check existing virtualpages
|
||||
if(class_exists('VirtualPage')) {
|
||||
return VirtualPage::get()->where(array(
|
||||
'"VirtualPage"."CopyContentFromID"' => $this->ID
|
||||
));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2213,7 +2102,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$rootTabSet->addExtraClass('ss-ui-action-tabset action-menus noborder');
|
||||
|
||||
// Render page information into the "more-options" drop-up, on the top.
|
||||
$live = Versioned::get_one_by_stage('SiteTree', 'Live', array(
|
||||
$live = Versioned::get_one_by_stage('SiteTree', Versioned::LIVE, array(
|
||||
'"SiteTree"."ID"' => $this->ID
|
||||
));
|
||||
$moreOptions->push(
|
||||
@ -2226,7 +2115,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
);
|
||||
|
||||
// "readonly"/viewing version that isn't the current version of the record
|
||||
$stageOrLiveRecord = Versioned::get_one_by_stage($this->class, Versioned::current_stage(), array(
|
||||
$stageOrLiveRecord = Versioned::get_one_by_stage($this->class, Versioned::get_stage(), array(
|
||||
'"SiteTree"."ID"' => $this->ID
|
||||
));
|
||||
if($stageOrLiveRecord && $stageOrLiveRecord->Version != $this->Version) {
|
||||
@ -2292,19 +2181,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Detect use of legacy actions
|
||||
// {@see CMSMain::enabled_legacy_actions}
|
||||
$legacy = CMSMain::config()->enabled_legacy_actions;
|
||||
if(in_array('CMSBatchAction_Delete', $legacy)) {
|
||||
Deprecation::notice('4.0', 'Delete from Stage is deprecated. Use Archive instead.');
|
||||
if($this->canDelete()) {
|
||||
// delete
|
||||
$moreOptions->push(
|
||||
FormAction::create('delete',_t('CMSMain.DELETE','Delete draft'))
|
||||
->addExtraClass('delete ss-ui-action-destructive')
|
||||
);
|
||||
}
|
||||
} elseif($this->canArchive()) {
|
||||
if($this->canDelete()) {
|
||||
// delete
|
||||
$moreOptions->push(
|
||||
FormAction::create('delete',_t('CMSMain.DELETE','Delete draft'))
|
||||
->addExtraClass('delete ss-ui-action-destructive')
|
||||
);
|
||||
}
|
||||
if($this->canArchive()) {
|
||||
// "archive"
|
||||
$moreOptions->push(
|
||||
FormAction::create('archive',_t('CMSMain.ARCHIVE','Archive'))
|
||||
@ -2349,123 +2233,29 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish this page.
|
||||
*
|
||||
* @uses SiteTreeExtension->onBeforePublish()
|
||||
* @uses SiteTreeExtension->onAfterPublish()
|
||||
* @return bool True if published
|
||||
*/
|
||||
public function doPublish() {
|
||||
if (!$this->canPublish()) return false;
|
||||
|
||||
$original = Versioned::get_one_by_stage("SiteTree", "Live", array(
|
||||
'"SiteTree"."ID"' => $this->ID
|
||||
));
|
||||
if(!$original) $original = new SiteTree();
|
||||
|
||||
// Handle activities undertaken by extensions
|
||||
$this->invokeWithExtensions('onBeforePublish', $original);
|
||||
//$this->PublishedByID = Member::currentUser()->ID;
|
||||
$this->write();
|
||||
$this->publish("Stage", "Live");
|
||||
|
||||
public function onAfterPublish() {
|
||||
// Force live sort order to match stage sort order
|
||||
DB::prepared_query('UPDATE "SiteTree_Live"
|
||||
SET "Sort" = (SELECT "SiteTree"."Sort" FROM "SiteTree" WHERE "SiteTree_Live"."ID" = "SiteTree"."ID")
|
||||
WHERE EXISTS (SELECT "SiteTree"."Sort" FROM "SiteTree" WHERE "SiteTree_Live"."ID" = "SiteTree"."ID") AND "ParentID" = ?',
|
||||
array($this->ParentID)
|
||||
);
|
||||
|
||||
// Publish any virtual pages that might need publishing
|
||||
$linkedPages = $this->VirtualPages();
|
||||
if($linkedPages) foreach($linkedPages as $page) {
|
||||
$page->copyFrom($page->CopyContentFrom());
|
||||
$page->write();
|
||||
if($page->getExistsOnLive()) $page->doPublish();
|
||||
}
|
||||
|
||||
// Need to update pages linking to this one as no longer broken, on the live site
|
||||
$origMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage('Live');
|
||||
foreach($this->DependentPages(false) as $page) {
|
||||
// $page->write() calls syncLinkTracking, which does all the hard work for us.
|
||||
$page->write();
|
||||
}
|
||||
Versioned::set_reading_mode($origMode);
|
||||
|
||||
// Handle activities undertaken by extensions
|
||||
$this->invokeWithExtensions('onAfterPublish', $original);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublish this page - remove it from the live site
|
||||
*
|
||||
* Overrides {@see Versioned::doUnpublish()}
|
||||
*
|
||||
* @uses SiteTreeExtension->onBeforeUnpublish()
|
||||
* @uses SiteTreeExtension->onAfterUnpublish()
|
||||
* Update draft dependant pages
|
||||
*/
|
||||
public function doUnpublish() {
|
||||
if(!$this->canUnpublish()) return false;
|
||||
if(!$this->ID) return false;
|
||||
|
||||
$this->invokeWithExtensions('onBeforeUnpublish', $this);
|
||||
|
||||
$origStage = Versioned::current_stage();
|
||||
Versioned::reading_stage('Live');
|
||||
|
||||
// We should only unpublish virtualpages that exist on live
|
||||
$virtualPages = $this->VirtualPages();
|
||||
|
||||
// This way our ID won't be unset
|
||||
$clone = clone $this;
|
||||
$clone->delete();
|
||||
|
||||
// Rewrite backlinks
|
||||
$dependentPages = $this->DependentPages(false);
|
||||
if($dependentPages) foreach($dependentPages as $page) {
|
||||
// $page->write() calls syncLinkTracking, which does all the hard work for us.
|
||||
$page->write();
|
||||
}
|
||||
Versioned::reading_stage($origStage);
|
||||
|
||||
// Unpublish any published virtual pages
|
||||
if ($virtualPages) foreach($virtualPages as $vp) $vp->doUnpublish();
|
||||
|
||||
// If we're on the draft site, then we can update the status.
|
||||
// Otherwise, these lines will resurrect an inappropriate record
|
||||
if(DB::prepared_query("SELECT \"ID\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($this->ID))->value()
|
||||
&& Versioned::current_stage() != 'Live') {
|
||||
$this->write();
|
||||
}
|
||||
|
||||
$this->invokeWithExtensions('onAfterUnpublish', $this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the draft changes: replace the draft content with the content on live
|
||||
*/
|
||||
public function doRevertToLive() {
|
||||
$this->invokeWithExtensions('onBeforeRevertToLive', $this);
|
||||
|
||||
$this->publish("Live", "Stage", false);
|
||||
|
||||
// Use a clone to get the updates made by $this->publish
|
||||
$clone = DataObject::get_by_id("SiteTree", $this->ID);
|
||||
$clone->writeWithoutVersion();
|
||||
public function onAfterRevertToLive() {
|
||||
// Use an alias to get the updates made by $this->publish
|
||||
/** @var SiteTree $stageSelf */
|
||||
$stageSelf = Versioned::get_by_stage('SiteTree', Versioned::DRAFT)->byID($this->ID);
|
||||
$stageSelf->writeWithoutVersion();
|
||||
|
||||
// Need to update pages linking to this one as no longer broken
|
||||
foreach($this->DependentPages(false) as $page) {
|
||||
// $page->write() calls syncLinkTracking, which does all the hard work for us.
|
||||
$page->write();
|
||||
foreach($stageSelf->DependentPages() as $page) {
|
||||
/** @var SiteTree $page */
|
||||
$page->writeWithoutVersion();
|
||||
}
|
||||
|
||||
$this->invokeWithExtensions('onAfterRevertToLive', $this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2505,8 +2295,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('SiteTree', false);
|
||||
}
|
||||
|
||||
$oldStage = Versioned::current_stage();
|
||||
Versioned::reading_stage('Stage');
|
||||
$oldStage = Versioned::get_stage();
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
$this->forceChange();
|
||||
$this->write();
|
||||
|
||||
@ -2518,21 +2308,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$page->write();
|
||||
}
|
||||
|
||||
Versioned::reading_stage($oldStage);
|
||||
Versioned::set_stage($oldStage);
|
||||
|
||||
$this->invokeWithExtensions('onAfterRestoreToStage', $this);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function doDeleteFromLive() {
|
||||
Deprecation::notice("4.0", "Use doUnpublish instead");
|
||||
return $this->doUnpublish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this page is new - that is, if it has yet to have been written to the database.
|
||||
*
|
||||
|
@ -109,7 +109,7 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
*/
|
||||
public function onAfterDelete() {
|
||||
// Skip live stage
|
||||
if(\Versioned::current_stage() === \Versioned::get_live_stage()) {
|
||||
if(\Versioned::get_stage() === Versioned::LIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
*/
|
||||
public function updateLinks() {
|
||||
// Skip live stage
|
||||
if(\Versioned::current_stage() === \Versioned::get_live_stage()) {
|
||||
if(\Versioned::get_stage() === \Versioned::LIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,39 @@
|
||||
*/
|
||||
class SiteTreeLinkTracking extends DataExtension {
|
||||
|
||||
public $parser;
|
||||
/**
|
||||
* @var SiteTreeLinkTracking_Parser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Inject parser for each page
|
||||
*
|
||||
* @var array
|
||||
* @config
|
||||
*/
|
||||
private static $dependencies = array(
|
||||
'parser' => '%$SiteTreeLinkTracking_Parser'
|
||||
'Parser' => '%$SiteTreeLinkTracking_Parser'
|
||||
);
|
||||
|
||||
/**
|
||||
* Parser for link tracking
|
||||
*
|
||||
* @return SiteTreeLinkTracking_Parser
|
||||
*/
|
||||
public function getParser() {
|
||||
return $this->parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SiteTreeLinkTracking_Parser $parser
|
||||
* @return $this
|
||||
*/
|
||||
public function setParser($parser) {
|
||||
$this->parser = $parser;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private static $db = array(
|
||||
"HasBrokenFile" => "Boolean",
|
||||
"HasBrokenLink" => "Boolean"
|
||||
@ -178,7 +205,7 @@ class SiteTreeLinkTracking extends DataExtension {
|
||||
*/
|
||||
public function augmentSyncLinkTracking() {
|
||||
// Skip live tracking
|
||||
if(\Versioned::current_stage() == \Versioned::get_live_stage()) {
|
||||
if(\Versioned::get_stage() == \Versioned::LIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,9 @@
|
||||
* Virtual Page creates an instance of a page, with the same fields that the original page had, but readonly.
|
||||
* This allows you can have a page in mulitple places in the site structure, with different children without duplicating the content
|
||||
* Note: This Only duplicates $db fields and not the $has_one etc..
|
||||
*
|
||||
* @method SiteTree CopyContentFrom()
|
||||
*
|
||||
* @package cms
|
||||
*/
|
||||
class VirtualPage extends Page {
|
||||
@ -18,8 +21,10 @@ class VirtualPage extends Page {
|
||||
private static $non_virtual_fields = array(
|
||||
"ID",
|
||||
"ClassName",
|
||||
"ObsoleteClassName",
|
||||
"SecurityTypeID",
|
||||
"OwnerID",
|
||||
"ParentID",
|
||||
"URLSegment",
|
||||
"Sort",
|
||||
"Status",
|
||||
@ -31,6 +36,8 @@ class VirtualPage extends Page {
|
||||
"Expiry",
|
||||
"CanViewType",
|
||||
"CanEditType",
|
||||
"CopyContentFromID",
|
||||
"HasBrokenLink",
|
||||
);
|
||||
|
||||
/**
|
||||
@ -46,57 +53,43 @@ class VirtualPage extends Page {
|
||||
"CopyContentFrom" => "SiteTree",
|
||||
);
|
||||
|
||||
private static $owns = array(
|
||||
"CopyContentFrom",
|
||||
);
|
||||
|
||||
private static $db = array(
|
||||
"VersionID" => "Int",
|
||||
);
|
||||
|
||||
/**
|
||||
* Generates the array of fields required for the page type.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getVirtualFields() {
|
||||
$nonVirtualFields = array_merge(self::config()->non_virtual_fields, self::config()->initially_copied_fields);
|
||||
// Check if copied page exists
|
||||
$record = $this->CopyContentFrom();
|
||||
if(!$record || !$record->exists()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$virtualFields = array();
|
||||
foreach($record->db() as $field => $type) {
|
||||
if(!in_array($field, $nonVirtualFields)) {
|
||||
$virtualFields[] = $field;
|
||||
}
|
||||
}
|
||||
return $virtualFields;
|
||||
// Diff db with non-virtual fields
|
||||
$fields = array_keys($record->db());
|
||||
$nonVirtualFields = $this->getNonVirtualisedFields();
|
||||
return array_diff($fields, $nonVirtualFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the linked page, or failing that, a new object.
|
||||
* List of fields or properties to never virtualise
|
||||
*
|
||||
* Always returns a non-empty object
|
||||
*
|
||||
* @return SiteTree
|
||||
* @return array
|
||||
*/
|
||||
public function CopyContentFrom() {
|
||||
$copyContentFromID = $this->CopyContentFromID;
|
||||
if(!$copyContentFromID) {
|
||||
return new SiteTree();
|
||||
}
|
||||
|
||||
if(!isset($this->components['CopyContentFrom'])) {
|
||||
$this->components['CopyContentFrom'] = DataObject::get_by_id("SiteTree", $copyContentFromID);
|
||||
|
||||
// Don't let VirtualPages point to other VirtualPages
|
||||
if($this->components['CopyContentFrom'] instanceof VirtualPage) {
|
||||
$this->components['CopyContentFrom'] = null;
|
||||
}
|
||||
|
||||
// has_one component semantics incidate than an empty object should be returned
|
||||
if(!$this->components['CopyContentFrom']) {
|
||||
$this->components['CopyContentFrom'] = new SiteTree();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->components['CopyContentFrom'] ? $this->components['CopyContentFrom'] : new SiteTree();
|
||||
public function getNonVirtualisedFields() {
|
||||
return array_merge($this->config()->non_virtual_fields, $this->config()->initially_copied_fields);
|
||||
}
|
||||
|
||||
public function setCopyContentFromID($val) {
|
||||
// Sanity check to prevent pages virtualising other virtual pages
|
||||
if($val && DataObject::get_by_id('SiteTree', $val) instanceof VirtualPage) {
|
||||
$val = 0;
|
||||
}
|
||||
@ -104,7 +97,11 @@ class VirtualPage extends Page {
|
||||
}
|
||||
|
||||
public function ContentSource() {
|
||||
return $this->CopyContentFrom();
|
||||
$copied = $this->CopyContentFrom();
|
||||
if($copied && $copied->exists()) {
|
||||
return $copied;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,15 +113,18 @@ class VirtualPage extends Page {
|
||||
*/
|
||||
public function MetaTags($includeTitle = true) {
|
||||
$tags = parent::MetaTags($includeTitle);
|
||||
if ($this->CopyContentFrom()->ID) {
|
||||
$tags .= "<link rel=\"canonical\" href=\"{$this->CopyContentFrom()->Link()}\" />\n";
|
||||
$copied = $this->CopyContentFrom();
|
||||
if ($copied && $copied->exists()) {
|
||||
$link = Convert::raw2att($copied->Link());
|
||||
$tags .= "<link rel=\"canonical\" href=\"{$link}\" />\n";
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function allowedChildren() {
|
||||
if($this->CopyContentFrom()) {
|
||||
return $this->CopyContentFrom()->allowedChildren();
|
||||
$copy = $this->CopyContentFrom();
|
||||
if($copy && $copy->exists()) {
|
||||
return $copy->allowedChildren();
|
||||
}
|
||||
return array();
|
||||
}
|
||||
@ -237,6 +237,35 @@ class VirtualPage extends Page {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function onBeforeWrite() {
|
||||
parent::onBeforeWrite();
|
||||
$this->refreshFromCopied();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy any fields from the copied record to bootstrap /backup
|
||||
*/
|
||||
protected function refreshFromCopied() {
|
||||
// Skip if copied record isn't available
|
||||
$source = $this->CopyContentFrom();
|
||||
if(!$source || !$source->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also want to copy certain, but only if we're copying the source page for the first
|
||||
// time. After this point, the user is free to customise these for the virtual page themselves.
|
||||
if($this->isChanged('CopyContentFromID', 2) && $this->CopyContentFromID) {
|
||||
foreach (self::config()->initially_copied_fields as $fieldName) {
|
||||
$this->$fieldName = $source->$fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy fields to the original record in case the class type changes
|
||||
foreach($this->getVirtualFields() as $virtualField) {
|
||||
$this->$virtualField = $source->$virtualField;
|
||||
}
|
||||
}
|
||||
|
||||
public function getSettingsFields() {
|
||||
$fields = parent::getSettingsFields();
|
||||
if(!$this->CopyContentFrom()->exists()) {
|
||||
@ -257,106 +286,12 @@ class VirtualPage extends Page {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to change it to copy all the content from the original page first.
|
||||
*/
|
||||
public function onBeforeWrite() {
|
||||
$performCopyFrom = null;
|
||||
|
||||
// Determine if we need to copy values.
|
||||
if(
|
||||
$this->extension_instances['Versioned']->migratingVersion
|
||||
&& Versioned::current_stage() == 'Live'
|
||||
&& $this->CopyContentFromID
|
||||
) {
|
||||
// On publication to live, copy from published source.
|
||||
$performCopyFrom = true;
|
||||
|
||||
$stageSourceVersion = DB::prepared_query(
|
||||
'SELECT "Version" FROM "SiteTree" WHERE "ID" = ?',
|
||||
array($this->CopyContentFromID)
|
||||
)->value();
|
||||
$liveSourceVersion = DB::prepared_query(
|
||||
'SELECT "Version" FROM "SiteTree_Live" WHERE "ID" = ?',
|
||||
array($this->CopyContentFromID)
|
||||
)->value();
|
||||
|
||||
// We're going to create a new VP record in SiteTree_versions because the published
|
||||
// version might not exist, unless we're publishing the latest version
|
||||
if($stageSourceVersion != $liveSourceVersion) {
|
||||
$this->extension_instances['Versioned']->migratingVersion = null;
|
||||
}
|
||||
} else {
|
||||
// On regular write, copy from draft source. This is only executed when the source page changes.
|
||||
$performCopyFrom = $this->isChanged('CopyContentFromID', 2) && $this->CopyContentFromID != 0;
|
||||
}
|
||||
|
||||
if($performCopyFrom && $this instanceof VirtualPage) {
|
||||
// This flush is needed because the get_one cache doesn't respect site version :-(
|
||||
singleton('SiteTree')->flushCache();
|
||||
// @todo Update get_one to support parameterised queries
|
||||
$source = DataObject::get_by_id("SiteTree", $this->CopyContentFromID);
|
||||
// Leave the updating of image tracking until after write, in case its a new record
|
||||
$this->copyFrom($source, false);
|
||||
}
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
|
||||
public function onAfterWrite() {
|
||||
parent::onAfterWrite();
|
||||
|
||||
// Don't do this stuff when we're publishing
|
||||
if(!$this->extension_instances['Versioned']->migratingVersion) {
|
||||
if(
|
||||
$this->isChanged('CopyContentFromID')
|
||||
&& $this->CopyContentFromID != 0
|
||||
&& $this instanceof VirtualPage
|
||||
) {
|
||||
$this->updateImageTracking();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if page type has changed to a non-virtual page.
|
||||
// Caution: Relies on the fact that the current instance is still of the old page type.
|
||||
if($this->isChanged('ClassName', 2)) {
|
||||
$changed = $this->getChangedFields();
|
||||
$classBefore = $changed['ClassName']['before'];
|
||||
$classAfter = $changed['ClassName']['after'];
|
||||
if($classBefore != $classAfter) {
|
||||
// Remove all database rows for the old page type to avoid inconsistent data retrieval.
|
||||
// TODO This should apply to all page type changes, not only on VirtualPage - but needs
|
||||
// more comprehensive testing as its a destructive operation
|
||||
$removedTables = array_diff(ClassInfo::dataClassesFor($classBefore), ClassInfo::dataClassesFor($classAfter));
|
||||
if($removedTables) foreach($removedTables as $removedTable) {
|
||||
// Note: *_versions records are left intact
|
||||
foreach(array('', 'Live') as $stage) {
|
||||
if($stage) $removedTable = "{$removedTable}_{$stage}";
|
||||
DB::prepared_query("DELETE FROM \"$removedTable\" WHERE \"ID\" = ?", array($this->ID));
|
||||
}
|
||||
}
|
||||
|
||||
// Also publish the change immediately to avoid inconsistent behaviour between
|
||||
// a non-virtual draft and a virtual live record (e.g. republishing the original record
|
||||
// shouldn't republish the - now unrelated - changes on the ex-VirtualPage draft).
|
||||
// Copies all stage fields to live as well.
|
||||
// @todo Update get_one to support parameterised queries
|
||||
$source = DataObject::get_by_id("SiteTree", $this->CopyContentFromID);
|
||||
$this->copyFrom($source);
|
||||
$this->publish('Stage', 'Live');
|
||||
|
||||
// Change reference on instance (as well as removing the underlying database tables)
|
||||
$this->CopyContentFromID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function validate() {
|
||||
$result = parent::validate();
|
||||
|
||||
// "Can be root" validation
|
||||
$orig = $this->CopyContentFrom();
|
||||
if(!$orig->stat('can_be_root') && !$this->ParentID) {
|
||||
if($orig && $orig->exists() && !$orig->stat('can_be_root') && !$this->ParentID) {
|
||||
$result->error(
|
||||
_t(
|
||||
'VirtualPage.PageTypNotAllowedOnRoot',
|
||||
@ -370,30 +305,6 @@ class VirtualPage extends Page {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we have an up-to-date version of everything.
|
||||
*
|
||||
* @param DataObject $source
|
||||
* @param bool $updateImageTracking
|
||||
*/
|
||||
public function copyFrom($source, $updateImageTracking = true) {
|
||||
if($source) {
|
||||
foreach($this->getVirtualFields() as $virtualField) {
|
||||
$this->$virtualField = $source->$virtualField;
|
||||
}
|
||||
|
||||
// We also want to copy certain, but only if we're copying the source page for the first
|
||||
// time. After this point, the user is free to customise these for the virtual page themselves.
|
||||
if($this->isChanged('CopyContentFromID', 2) && $this->CopyContentFromID != 0) {
|
||||
foreach(self::config()->initially_copied_fields as $fieldName) {
|
||||
$this->$fieldName = $source->$fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
if($updateImageTracking) $this->updateImageTracking();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateImageTracking() {
|
||||
// Doesn't work on unsaved records
|
||||
if(!$this->ID) return;
|
||||
@ -425,11 +336,41 @@ class VirtualPage extends Page {
|
||||
return $this->$funcName();
|
||||
} else if(parent::hasField($field) || ($field === 'ID' && !$this->exists())) {
|
||||
return $this->getField($field);
|
||||
} else {
|
||||
return $this->copyContentFrom()->$field;
|
||||
} elseif(($copy = $this->CopyContentFrom()) && $copy->exists()) {
|
||||
return $copy->$field;
|
||||
}
|
||||
}
|
||||
|
||||
public function getField($field) {
|
||||
if($this->isFieldVirtualised($field)) {
|
||||
return $this->CopyContentFrom()->getField($field);
|
||||
}
|
||||
return parent::getField($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given field is virtualised
|
||||
*
|
||||
* @param string $field
|
||||
* @return bool
|
||||
*/
|
||||
public function isFieldVirtualised($field) {
|
||||
// Don't defer if field is non-virtualised
|
||||
$ignore = $this->getNonVirtualisedFields();
|
||||
if(in_array($field, $ignore)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't defer if no virtual page
|
||||
$copied = $this->CopyContentFrom();
|
||||
if(!$copied || !$copied->exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if copied object has this field
|
||||
return $copied->hasField($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass unrecognized method calls on to the original data object
|
||||
*
|
||||
@ -452,9 +393,11 @@ class VirtualPage extends Page {
|
||||
public function hasField($field) {
|
||||
if(parent::hasField($field)) {
|
||||
return true;
|
||||
}
|
||||
$copy = $this->CopyContentFrom();
|
||||
return $copy && $copy->exists() && $copy->hasField($field);
|
||||
}
|
||||
return $this->CopyContentFrom()->hasField($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite to also check for method on the original data object
|
||||
*
|
||||
@ -464,8 +407,13 @@ class VirtualPage extends Page {
|
||||
public function hasMethod($method) {
|
||||
if(parent::hasMethod($method)) {
|
||||
return true;
|
||||
}
|
||||
return $this->CopyContentFrom()->hasMethod($method);
|
||||
}
|
||||
// Don't call property setters on copied page
|
||||
if(stripos($method, 'set') === 0) {
|
||||
return false;
|
||||
}
|
||||
$copy = $this->CopyContentFrom();
|
||||
return $copy && $copy->exists() && $copy->hasMethod($method);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,9 +424,11 @@ class VirtualPage extends Page {
|
||||
* @return string
|
||||
*/
|
||||
public function castingHelper($field) {
|
||||
return $this
|
||||
->CopyContentFrom()
|
||||
->castingHelper($field);
|
||||
$copy = $this->CopyContentFrom();
|
||||
if($copy && $copy->exists() && ($helper = $copy->castingHelper($field))) {
|
||||
return $helper;
|
||||
}
|
||||
return parent::castingHelper($field);
|
||||
}
|
||||
|
||||
}
|
||||
@ -493,15 +443,6 @@ class VirtualPage_Controller extends Page_Controller {
|
||||
'loadcontentall' => 'ADMIN',
|
||||
);
|
||||
|
||||
/**
|
||||
* Reloads the content if the version is different ;-)
|
||||
*/
|
||||
public function reloadContent() {
|
||||
$this->failover->copyFrom($this->failover->CopyContentFrom());
|
||||
$this->failover->write();
|
||||
return;
|
||||
}
|
||||
|
||||
public function getViewer($action) {
|
||||
$originalClass = get_class($this->CopyContentFrom());
|
||||
if ($originalClass == 'SiteTree') $name = 'Page_Controller';
|
||||
@ -517,26 +458,10 @@ class VirtualPage_Controller extends Page_Controller {
|
||||
* We can't load the content without an ID or record to copy it from.
|
||||
*/
|
||||
public function init(){
|
||||
if(isset($this->record) && $this->record->ID){
|
||||
if($this->record->VersionID != $this->failover->CopyContentFrom()->Version){
|
||||
$this->reloadContent();
|
||||
$this->VersionID = $this->failover->CopyContentFrom()->VersionID;
|
||||
}
|
||||
}
|
||||
parent::init();
|
||||
$this->__call('init', array());
|
||||
}
|
||||
|
||||
public function loadcontentall() {
|
||||
$pages = DataObject::get("VirtualPage");
|
||||
foreach($pages as $page) {
|
||||
$page->copyFrom($page->CopyContentFrom());
|
||||
$page->write();
|
||||
$page->publish("Stage", "Live");
|
||||
echo "<li>Published $page->URLSegment";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Also check the original object's original controller for the method
|
||||
*
|
||||
|
@ -10,22 +10,22 @@ class AssetAdminTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
AssetStoreTest_SpyStore::activate('AssetAdminTest');
|
||||
|
||||
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
|
||||
|
||||
// Create a test folders for each of the fixture references
|
||||
$folderIDs = $this->allFixtureIDs('Folder');
|
||||
foreach($folderIDs as $folderID) {
|
||||
$folder = DataObject::get_by_id('Folder', $folderID);
|
||||
if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename");
|
||||
foreach(File::get()->filter('ClassName', 'Folder') as $folder) {
|
||||
/** @var Folder $folder */
|
||||
$folder->doPublish();
|
||||
}
|
||||
|
||||
// Create a test files for each of the fixture references
|
||||
$fileIDs = $this->allFixtureIDs('File');
|
||||
foreach($fileIDs as $fileID) {
|
||||
$file = DataObject::get_by_id('File', $fileID);
|
||||
$fh = fopen(BASE_PATH."/$file->Filename", "w");
|
||||
fwrite($fh, str_repeat('x',1000000));
|
||||
fclose($fh);
|
||||
$content = str_repeat('x',1000000);
|
||||
foreach(File::get()->exclude('ClassName', 'Folder') as $file) {
|
||||
/** @var File $file */
|
||||
$file->setFromString($content, $file->generateFilename());
|
||||
$file->doPublish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@ Folder:
|
||||
File:
|
||||
file1:
|
||||
Title: File1
|
||||
Filename: assets/AssetAdminTest/file1.txt
|
||||
Name: file1.txt
|
||||
ParentID: =>Folder.folder1
|
||||
file2:
|
||||
Title: File2
|
||||
Filename: assets/AssetAdminTest/file2.txt
|
||||
Name: file2.txt
|
||||
ParentID: =>Folder.folder1
|
||||
|
@ -11,6 +11,8 @@ class CMSBatchActionsTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
// published page
|
||||
$published = $this->objFromFixture('SiteTree', 'published');
|
||||
$published->doPublish();
|
||||
|
@ -9,7 +9,7 @@ class ErrorPageFileExtensionTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->versionedMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
AssetStoreTest_SpyStore::activate('ErrorPageFileExtensionTest');
|
||||
$file = new File();
|
||||
$file->setFromString('dummy', 'dummy.txt');
|
||||
|
@ -9,7 +9,7 @@ class FileLinkTrackingTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
|
||||
AssetStoreTest_SpyStore::activate('FileLinkTrackingTest');
|
||||
$this->logInWithPermission('ADMIN');
|
||||
@ -112,18 +112,20 @@ class FileLinkTrackingTest extends SapphireTest {
|
||||
$file->write();
|
||||
|
||||
// Verify that the draft virtual pages have the correct content
|
||||
$svp = Versioned::get_by_stage('VirtualPage', Versioned::DRAFT)->byID($svp->ID);
|
||||
$this->assertContains(
|
||||
'<img src="/assets/55b443b601/renamed-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($svp->ID))->value()
|
||||
$svp->Content
|
||||
);
|
||||
|
||||
// Publishing both file and page will update the live record
|
||||
$file->doPublish();
|
||||
$page->doPublish();
|
||||
|
||||
$svp = Versioned::get_by_stage('VirtualPage', Versioned::LIVE)->byID($svp->ID);
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($svp->ID))->value()
|
||||
$svp->Content
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ class SiteTreeBacklinksTest extends SapphireTest {
|
||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||
|
||||
// assert hyperlink to page 1's new url exists
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||
$this->assertContains(Director::baseURL().'new-url-segment/', $links, 'Assert hyperlink to page 1\'s new url exists on page 3');
|
||||
}
|
||||
@ -136,7 +136,7 @@ class SiteTreeBacklinksTest extends SapphireTest {
|
||||
|
||||
// assert hyperlink to page 1's current publish url exists
|
||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3');
|
||||
|
||||
@ -179,7 +179,7 @@ class SiteTreeBacklinksTest extends SapphireTest {
|
||||
|
||||
// assert page 3 on published site contains old page 1 url
|
||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3');
|
||||
|
||||
@ -223,7 +223,7 @@ class SiteTreeBacklinksTest extends SapphireTest {
|
||||
|
||||
// confirm that published link hasn't
|
||||
$page2Live = Versioned::get_one_by_stage("Page", "Live", "\"SiteTree\".\"ID\" = $page2->ID");
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$this->assertEquals('<p><a href="'.Director::baseURL().'page1/">Testing page 1 link</a></p>', $page2Live->obj('ExtraContent')->forTemplate());
|
||||
|
||||
// publish page1 and confirm that the link on the published page2 has now been updated
|
||||
|
@ -11,7 +11,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
AssetStoreTest_SpyStore::activate('SiteTreeBrokenLinksTest');
|
||||
$this->logInWithPermission('ADMIN');
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$s->write();
|
||||
|
||||
$oldMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
|
||||
$checkSiteTree = DataObject::get_one("SiteTree", array(
|
||||
'"SiteTree"."URLSegment"' => 'get-one-test-page'
|
||||
@ -264,12 +264,12 @@ class SiteTreeTest extends SapphireTest {
|
||||
|
||||
// Check that if we restore while on the live site that the content still gets pushed to
|
||||
// stage
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$deletedPage = Versioned::get_latest_version('SiteTree', $page2ID);
|
||||
$deletedPage->doRestoreToStage();
|
||||
$this->assertFalse((bool)Versioned::get_one_by_stage("Page", "Live", "\"SiteTree\".\"ID\" = " . $page2ID));
|
||||
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
$requeriedPage = DataObject::get_by_id("Page", $page2ID);
|
||||
$this->assertEquals('Products', $requeriedPage->Title);
|
||||
$this->assertEquals('Page', $requeriedPage->class);
|
||||
@ -383,12 +383,12 @@ class SiteTreeTest extends SapphireTest {
|
||||
|
||||
$parentPage->doUnpublish();
|
||||
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', true);
|
||||
}
|
||||
|
||||
@ -406,11 +406,11 @@ class SiteTreeTest extends SapphireTest {
|
||||
$parentPage = $this->objFromFixture('Page', 'about');
|
||||
$parentPage->doUnpublish();
|
||||
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', true);
|
||||
}
|
||||
|
||||
@ -427,11 +427,11 @@ class SiteTreeTest extends SapphireTest {
|
||||
$parentPage = $this->objFromFixture('Page', 'about');
|
||||
$parentPage->doUnpublish();
|
||||
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageStaff->ID));
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1115,7 +1115,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$member->Groups()->add($group);
|
||||
|
||||
// both pages are viewable in stage
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
$this->assertFalse($about->isOrphaned());
|
||||
@ -1127,16 +1127,16 @@ class SiteTreeTest extends SapphireTest {
|
||||
$staff->publish('Stage', 'Live');
|
||||
$this->assertFalse($staff->isOrphaned());
|
||||
$this->assertTrue($staff->canView($member));
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$staff = $this->objFromFixture('Page', 'staff'); // Live copy of page
|
||||
$this->assertTrue($staff->isOrphaned()); // because parent isn't published
|
||||
$this->assertFalse($staff->canView($member));
|
||||
|
||||
// Publishing the parent page should restore visibility
|
||||
Versioned::reading_stage('Stage');
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
$about->publish('Stage', 'Live');
|
||||
Versioned::reading_stage('Live');
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
$this->assertFalse($staff->isOrphaned());
|
||||
$this->assertTrue($staff->canView($member));
|
||||
|
@ -30,28 +30,9 @@ class VirtualPageTest extends FunctionalTest {
|
||||
// Ensure we always have permission to save/publish
|
||||
$this->logInWithPermission("ADMIN");
|
||||
|
||||
$this->origInitiallyCopiedFields = VirtualPage::config()->initially_copied_fields;
|
||||
Config::inst()->remove('VirtualPage', 'initially_copied_fields');
|
||||
VirtualPage::config()->initially_copied_fields = array_merge(
|
||||
$this->origInitiallyCopiedFields,
|
||||
array('MyInitiallyCopiedField')
|
||||
);
|
||||
|
||||
$this->origNonVirtualField = VirtualPage::config()->non_virtual_fields;
|
||||
Config::inst()->remove('VirtualPage', 'non_virtual_fields');
|
||||
VirtualPage::config()->non_virtual_fields = array_merge(
|
||||
$this->origNonVirtualField,
|
||||
array('MyNonVirtualField', 'MySharedNonVirtualField')
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
|
||||
Config::inst()->remove('VirtualPage', 'initially_copied_fields');
|
||||
Config::inst()->remove('VirtualPage', 'non_virtual_fields');
|
||||
VirtualPage::config()->initially_copied_fields = $this->origInitiallyCopiedFields;
|
||||
VirtualPage::config()->non_virtual_fields = $this->origNonVirtualField;
|
||||
// Add extra fields
|
||||
Config::inst()->update('VirtualPage', 'initially_copied_fields', array('MyInitiallyCopiedField'));
|
||||
Config::inst()->update('VirtualPage', 'non_virtual_fields', array('MyNonVirtualField', 'MySharedNonVirtualField'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +79,7 @@ class VirtualPageTest extends FunctionalTest {
|
||||
|
||||
$master->doPublish();
|
||||
|
||||
Versioned::reading_stage("Live");
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$vp1 = DataObject::get_by_id("VirtualPage", $this->idFromFixture('VirtualPage', 'vp1'));
|
||||
$vp2 = DataObject::get_by_id("VirtualPage", $this->idFromFixture('VirtualPage', 'vp2'));
|
||||
|
||||
@ -111,7 +92,7 @@ class VirtualPageTest extends FunctionalTest {
|
||||
$this->assertEquals("New menutitle", $vp2->MenuTitle);
|
||||
$this->assertEquals("<p>New content</p>", $vp1->Content);
|
||||
$this->assertEquals("<p>New content</p>", $vp2->Content);
|
||||
Versioned::reading_stage("Stage");
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,23 +126,36 @@ class VirtualPageTest extends FunctionalTest {
|
||||
$p->write();
|
||||
$p->doPublish();
|
||||
|
||||
// Don't publish this change - published page will still say 'published content'
|
||||
$p->Content = "draft content";
|
||||
$p->write();
|
||||
|
||||
// Virtual page has this content
|
||||
$vp = new VirtualPage();
|
||||
$vp->CopyContentFromID = $p->ID;
|
||||
$vp->write();
|
||||
|
||||
$vp->doPublish();
|
||||
|
||||
// Don't publish this change - published page will still say 'published content'
|
||||
$p->Content = "draft content";
|
||||
$p->write();
|
||||
|
||||
// The draft content of the virtual page should say 'draft content'
|
||||
$this->assertEquals('draft content',
|
||||
DB::query('SELECT "Content" from "SiteTree" WHERE "ID" = ' . $vp->ID)->value());
|
||||
/** @var VirtualPage $vpDraft */
|
||||
$vpDraft = Versioned::get_by_stage("VirtualPage", Versioned::DRAFT)->byID($vp->ID);
|
||||
$this->assertEquals('draft content', $vpDraft->CopyContentFrom()->Content);
|
||||
$this->assertEquals('draft content', $vpDraft->Content);
|
||||
|
||||
// The published content of the virtual page should say 'published content'
|
||||
$this->assertEquals('published content',
|
||||
DB::query('SELECT "Content" from "SiteTree_Live" WHERE "ID" = ' . $vp->ID)->value());
|
||||
/** @var VirtualPage $vpLive */
|
||||
$vpLive = Versioned::get_by_stage("VirtualPage", Versioned::LIVE)->byID($vp->ID);
|
||||
$this->assertEquals('published content', $vpLive->CopyContentFrom()->Content);
|
||||
$this->assertEquals('published content', $vpLive->Content);
|
||||
|
||||
// Publishing the virtualpage should, however, trigger publishing of the live page
|
||||
$vpDraft->doPublish();
|
||||
|
||||
// Everything is published live
|
||||
$vpLive = Versioned::get_by_stage("VirtualPage", Versioned::LIVE)->byID($vp->ID);
|
||||
$this->assertEquals('draft content', $vpLive->CopyContentFrom()->Content);
|
||||
$this->assertEquals('draft content', $vpLive->Content);
|
||||
}
|
||||
|
||||
public function testCantPublishVirtualPagesBeforeTheirSource() {
|
||||
@ -191,22 +185,36 @@ class VirtualPageTest extends FunctionalTest {
|
||||
$p->Content = "test content";
|
||||
$p->write();
|
||||
$p->doPublish();
|
||||
$pID = $p->ID;
|
||||
|
||||
$vp = new VirtualPage();
|
||||
$vp->CopyContentFromID = $p->ID;
|
||||
$vp->write();
|
||||
|
||||
// Delete the source page
|
||||
$this->assertTrue($vp->canPublish());
|
||||
$this->assertTrue($p->doUnpublish());
|
||||
$this->assertTrue($vp->doPublish());
|
||||
|
||||
// Delete the source page semi-manually, without triggering
|
||||
// the cascade publish back to the virtual page.
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
$livePage = Versioned::get_by_stage('SiteTree', Versioned::LIVE)->byID($pID);
|
||||
$livePage->delete();
|
||||
Versioned::set_stage(Versioned::DRAFT);
|
||||
|
||||
// Confirm that we can unpublish, but not publish
|
||||
$this->assertFalse($p->IsPublished(), 'Copied page has orphaned the virtual page on live');
|
||||
$this->assertTrue($vp->isPublished(), 'Virtual page remains on live');
|
||||
$this->assertTrue($vp->canUnpublish());
|
||||
$this->assertFalse($vp->canPublish());
|
||||
|
||||
// Confirm that the action really works
|
||||
$this->assertTrue($vp->doUnpublish());
|
||||
$this->assertNull(DB::query("SELECT \"ID\" FROM \"SiteTree_Live\" WHERE \"ID\" = $vp->ID")->value());
|
||||
$this->assertEquals(
|
||||
0,
|
||||
DB::prepared_query(
|
||||
"SELECT count(*) FROM \"SiteTree_Live\" WHERE \"ID\" = ?",
|
||||
array($vp->ID)
|
||||
)->value()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCanEdit() {
|
||||
@ -259,7 +267,6 @@ class VirtualPageTest extends FunctionalTest {
|
||||
|
||||
// VP is still orange after we publish
|
||||
$p->doPublish();
|
||||
$this->fixVersionNumberCache($vp);
|
||||
$this->assertTrue($vp->getIsAddedToStage());
|
||||
|
||||
// A new VP created after P's initial construction
|
||||
@ -272,77 +279,28 @@ class VirtualPageTest extends FunctionalTest {
|
||||
$p->Content = "new content";
|
||||
$p->write();
|
||||
$p->doPublish();
|
||||
$this->fixVersionNumberCache($vp2);
|
||||
$this->assertTrue($vp2->getIsAddedToStage());
|
||||
|
||||
// VP is now published
|
||||
$vp->doPublish();
|
||||
|
||||
$this->fixVersionNumberCache($vp);
|
||||
$this->assertTrue($vp->getExistsOnLive());
|
||||
$this->assertFalse($vp->getIsModifiedOnStage());
|
||||
|
||||
// P edited, VP and P both go green
|
||||
// P edited, P goes green. Change set interface should indicate to the user that the owned page has
|
||||
// modifications, although the virtual page record itself will not appear as having pending changes.
|
||||
$p->Content = "third content";
|
||||
$p->write();
|
||||
|
||||
$this->fixVersionNumberCache($vp, $p);
|
||||
$this->assertTrue($p->getIsModifiedOnStage());
|
||||
$this->assertTrue($vp->getIsModifiedOnStage());
|
||||
$this->assertFalse($vp->getIsModifiedOnStage());
|
||||
|
||||
// Publish, VP goes black
|
||||
$p->doPublish();
|
||||
$this->fixVersionNumberCache($vp);
|
||||
$this->assertTrue($vp->getExistsOnLive());
|
||||
$this->assertFalse($vp->getIsModifiedOnStage());
|
||||
}
|
||||
|
||||
public function testVirtualPagesCreateVersionRecords() {
|
||||
$source = $this->objFromFixture('Page', 'master');
|
||||
$source->Title = "T0";
|
||||
$source->write();
|
||||
$source->doPublish();
|
||||
|
||||
// Creating a new VP to ensure that Version #s are out of alignment
|
||||
$vp = new VirtualPage();
|
||||
$vp->CopyContentFromID = $source->ID;
|
||||
$vp->write();
|
||||
|
||||
$source->Title = "T1";
|
||||
$source->write();
|
||||
$source->Title = "T2";
|
||||
$source->write();
|
||||
|
||||
$this->assertEquals($vp->ID, DB::query("SELECT \"RecordID\" FROM \"SiteTree_versions\"
|
||||
WHERE \"RecordID\" = $vp->ID AND \"Title\" = 'T1'")->value());
|
||||
$this->assertEquals($vp->ID, DB::query("SELECT \"RecordID\" FROM \"SiteTree_versions\"
|
||||
WHERE \"RecordID\" = $vp->ID AND \"Title\" = 'T2'")->value());
|
||||
$this->assertEquals($vp->ID, DB::query("SELECT \"RecordID\" FROM \"SiteTree_versions\"
|
||||
WHERE \"RecordID\" = $vp->ID AND \"Version\" = $vp->Version")->value());
|
||||
|
||||
$vp->doPublish();
|
||||
|
||||
// Check that the published content is copied from the published page, with a legal
|
||||
// version
|
||||
$liveVersion = DB::query("SELECT \"Version\" FROM \"SiteTree_Live\" WHERE \"ID\" = $vp->ID")->value();
|
||||
|
||||
$this->assertEquals("T0", DB::query("SELECT \"Title\" FROM \"SiteTree_Live\"
|
||||
WHERE \"ID\" = $vp->ID")->value());
|
||||
|
||||
// SiteTree_Live.Version should reference a legal entry in SiteTree_versions for the
|
||||
// virtual page
|
||||
$this->assertEquals("T0", DB::query("SELECT \"Title\" FROM \"SiteTree_versions\"
|
||||
WHERE \"RecordID\" = $vp->ID AND \"Version\" = $liveVersion")->value());
|
||||
}
|
||||
|
||||
public function fixVersionNumberCache($page) {
|
||||
$pages = func_get_args();
|
||||
foreach($pages as $p) {
|
||||
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Stage', array($p->ID));
|
||||
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($p->ID));
|
||||
}
|
||||
}
|
||||
|
||||
public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage() {
|
||||
// Create page and virutal page
|
||||
$p = new Page();
|
||||
@ -459,7 +417,6 @@ class VirtualPageTest extends FunctionalTest {
|
||||
$virtual->CopyContentFromID = $original->ID;
|
||||
$virtual->write();
|
||||
|
||||
$virtual->copyFrom($original);
|
||||
// Using getField() to avoid side effects from an overloaded __get()
|
||||
$this->assertEquals(
|
||||
'original',
|
||||
@ -478,7 +435,6 @@ class VirtualPageTest extends FunctionalTest {
|
||||
|
||||
$original->MyInitiallyCopiedField = 'changed';
|
||||
$original->write();
|
||||
$virtual->copyFrom($original);
|
||||
$this->assertEquals(
|
||||
'original',
|
||||
$virtual->MyInitiallyCopiedField,
|
||||
@ -486,53 +442,6 @@ class VirtualPageTest extends FunctionalTest {
|
||||
);
|
||||
}
|
||||
|
||||
public function testWriteWithoutVersion() {
|
||||
$original = new SiteTree();
|
||||
$original->write();
|
||||
// Create a second version (different behaviour),
|
||||
// as SiteTree->onAfterWrite() checks for Version == 1
|
||||
$original->Title = 'prepare';
|
||||
$original->write();
|
||||
$originalVersion = $original->Version;
|
||||
|
||||
$virtual = new VirtualPage();
|
||||
$virtual->CopyContentFromID = $original->ID;
|
||||
$virtual->write();
|
||||
// Create a second version, see above.
|
||||
$virtual->Title = 'prepare';
|
||||
$virtual->write();
|
||||
$virtualVersion = $virtual->Version;
|
||||
|
||||
$virtual->Title = 'changed 1';
|
||||
$virtual->writeWithoutVersion();
|
||||
$this->assertEquals(
|
||||
$virtual->Version,
|
||||
$virtualVersion,
|
||||
'writeWithoutVersion() on VirtualPage doesnt increment version'
|
||||
);
|
||||
|
||||
$original->Title = 'changed 2';
|
||||
$original->writeWithoutVersion();
|
||||
|
||||
DataObject::flush_and_destroy_cache();
|
||||
$virtual = DataObject::get_by_id('VirtualPage', $virtual->ID, false);
|
||||
$this->assertEquals(
|
||||
$virtual->Version,
|
||||
$virtualVersion,
|
||||
'writeWithoutVersion() on original page doesnt increment version on related VirtualPage'
|
||||
);
|
||||
|
||||
$original->Title = 'changed 3';
|
||||
$original->write();
|
||||
DataObject::flush_and_destroy_cache();
|
||||
$virtual = DataObject::get_by_id('VirtualPage', $virtual->ID, false);
|
||||
$this->assertGreaterThan(
|
||||
$virtualVersion,
|
||||
$virtual->Version,
|
||||
'write() on original page does increment version on related VirtualPage'
|
||||
);
|
||||
}
|
||||
|
||||
public function testCanBeRoot() {
|
||||
$page = new SiteTree();
|
||||
$page->ParentID = 0;
|
||||
@ -562,50 +471,16 @@ class VirtualPageTest extends FunctionalTest {
|
||||
if(!$isDetected) $this->fail('Fails validation with $can_be_root=false');
|
||||
}
|
||||
|
||||
public function testPageTypeChangeDoesntKeepOrphanedVirtualPageRecord() {
|
||||
$page = new SiteTree();
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$virtual = new VirtualPageTest_VirtualPageSub();
|
||||
$virtual->CopyContentFromID = $page->ID;
|
||||
$virtual->write();
|
||||
$virtual->publish('Stage', 'Live');
|
||||
|
||||
$nonVirtual = $virtual;
|
||||
$nonVirtual->ClassName = 'VirtualPageTest_ClassA';
|
||||
$nonVirtual->write(); // not publishing
|
||||
|
||||
$this->assertNotNull(
|
||||
DB::query(sprintf('SELECT "ID" FROM "SiteTree" WHERE "ID" = %d', $nonVirtual->ID))->value(),
|
||||
"Shared base database table entry exists after type change"
|
||||
);
|
||||
$this->assertNull(
|
||||
DB::query(sprintf('SELECT "ID" FROM "VirtualPage" WHERE "ID" = %d', $nonVirtual->ID))->value(),
|
||||
"Base database table entry no longer exists after type change"
|
||||
);
|
||||
$this->assertNull(
|
||||
DB::query(sprintf('SELECT "ID" FROM "VirtualPageTest_VirtualPageSub" WHERE "ID" = %d', $nonVirtual->ID))->value(),
|
||||
"Sub database table entry no longer exists after type change"
|
||||
);
|
||||
$this->assertNull(
|
||||
DB::query(sprintf('SELECT "ID" FROM "VirtualPage_Live" WHERE "ID" = %d', $nonVirtual->ID))->value(),
|
||||
"Base live database table entry no longer exists after type change"
|
||||
);
|
||||
$this->assertNull(
|
||||
DB::query(sprintf('SELECT "ID" FROM "VirtualPageTest_VirtualPageSub_Live" WHERE "ID" = %d', $nonVirtual->ID))->value(),
|
||||
"Sub live database table entry no longer exists after type change"
|
||||
);
|
||||
}
|
||||
|
||||
public function testPageTypeChangePropagatesToLive() {
|
||||
$page = new SiteTree();
|
||||
$page->Title = 'published title';
|
||||
$page->MySharedNonVirtualField = 'original';
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$virtual = new VirtualPageTest_VirtualPageSub();
|
||||
$virtual->CopyContentFromID = $page->ID;
|
||||
$virtual->MySharedNonVirtualField = 'virtual published field';
|
||||
$virtual->write();
|
||||
$virtual->publish('Stage', 'Live');
|
||||
|
||||
@ -614,28 +489,38 @@ class VirtualPageTest extends FunctionalTest {
|
||||
// but we want to test that it gets copied on class name change instead
|
||||
$page->write();
|
||||
|
||||
|
||||
$nonVirtual = $virtual;
|
||||
$nonVirtual->ClassName = 'VirtualPageTest_ClassA';
|
||||
$nonVirtual->MySharedNonVirtualField = 'changed on new type';
|
||||
$nonVirtual->write(); // not publishing the page type change here
|
||||
|
||||
$this->assertEquals('original', $nonVirtual->Title,
|
||||
// Stage record is changed to the new type and no longer acts as a virtual page
|
||||
$nonVirtualStage = Versioned::get_one_by_stage('SiteTree', 'Stage', '"SiteTree"."ID" = ' . $nonVirtual->ID, false);
|
||||
$this->assertNotNull($nonVirtualStage);
|
||||
$this->assertEquals('VirtualPageTest_ClassA', $nonVirtualStage->ClassName);
|
||||
$this->assertEquals('changed on new type', $nonVirtualStage->MySharedNonVirtualField);
|
||||
$this->assertEquals('original', $nonVirtualStage->Title,
|
||||
'Copies virtual fields from original draft into new instance on type change '
|
||||
);
|
||||
|
||||
$nonVirtualLive = Versioned::get_one_by_stage('SiteTree', 'Live', '"SiteTree_Live"."ID" = ' . $nonVirtual->ID);
|
||||
$this->assertNotNull($nonVirtualLive);
|
||||
$this->assertEquals('VirtualPageTest_ClassA', $nonVirtualLive->ClassName);
|
||||
$this->assertEquals('changed on new type', $nonVirtualLive->MySharedNonVirtualField);
|
||||
// Virtual page on live keeps working as it should
|
||||
$virtualLive = Versioned::get_one_by_stage('SiteTree', 'Live', '"SiteTree_Live"."ID" = ' . $virtual->ID, false);
|
||||
$this->assertNotNull($virtualLive);
|
||||
$this->assertEquals('VirtualPageTest_VirtualPageSub', $virtualLive->ClassName);
|
||||
$this->assertEquals('virtual published field', $virtualLive->MySharedNonVirtualField);
|
||||
$this->assertEquals('published title', $virtualLive->Title);
|
||||
|
||||
// Change live page
|
||||
$page->Title = 'title changed on original';
|
||||
$page->MySharedNonVirtualField = 'changed only on original';
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$nonVirtualLive = Versioned::get_one_by_stage('SiteTree', 'Live', '"SiteTree_Live"."ID" = ' . $nonVirtual->ID, false);
|
||||
$this->assertEquals('changed on new type', $nonVirtualLive->MySharedNonVirtualField,
|
||||
'No field copying from previous original after page type changed'
|
||||
);
|
||||
// Virtual page only notices changes to virtualised fields (Title)
|
||||
$virtualLive = Versioned::get_one_by_stage('SiteTree', 'Live', '"SiteTree_Live"."ID" = ' . $virtual->ID, false);
|
||||
$this->assertEquals('virtual published field', $virtualLive->MySharedNonVirtualField);
|
||||
$this->assertEquals('title changed on original', $virtualLive->Title);
|
||||
}
|
||||
|
||||
public function testVirtualPageFindsCorrectCasting() {
|
||||
|
Loading…
Reference in New Issue
Block a user