diff --git a/code/CMSMain.php b/code/CMSMain.php
index a0c93632..0cd0e77f 100644
--- a/code/CMSMain.php
+++ b/code/CMSMain.php
@@ -46,7 +46,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
'publishall',
'publishitems',
'PublishItemsForm',
- 'restorepage',
+ 'restore',
'revert',
'rollback',
'sidereport',
@@ -56,6 +56,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
'EditForm',
'AddPageOptionsForm',
'SiteTreeAsUL',
+ 'getshowdeletedsubtree'
);
/**
@@ -171,6 +172,21 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getSiteTreeFor("SiteTree");
}
+ /**
+ * Get a subtree underneath the request param 'ID', of the tree that includes deleted pages.
+ * If ID = 0, then get the whole tree.
+ */
+ public function getshowdeletedsubtree() {
+ // Get the tree
+ $tree = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID'], "AllHistoricalChildren");
+
+ // Trim off the outer tag
+ $tree = ereg_replace('^[ \t\r\n]*
]*>','', $tree);
+ $tree = ereg_replace('
]*>[ \t\r\n]*$','', $tree);
+
+ return $tree;
+ }
+
/**
* Returns the SiteTree columns that can be filtered using the the Site Tree Search button as a DataObjectSet
*/
@@ -372,6 +388,7 @@ JS;
if($id && is_numeric($id)) {
$record = DataObject::get_one( $treeClass, "\"$treeClass\".\"ID\" = $id");
+ // 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');
@@ -380,6 +397,17 @@ JS;
$record = DataObject::get_one( $treeClass, "\"$treeClass\".\"ID\" = $id");
if($record) Versioned::reading_stage(null);
}
+
+ // Then, try getting a deleted record
+ if(!$record) {
+ $record = Versioned::get_latest_version($treeClass, $id);
+ }
+
+ // Don't open a page from a different locale
+ if($record && Translatable::is_enabled() && $record->Locale && $record->Locale != Translatable::current_locale()) {
+ $record = null;
+ }
+
return $record;
} else if(substr($id,0,3) == 'new') {
@@ -1255,25 +1283,25 @@ JS;
}
/**
- * Restore a previously deleted page.
- * Internal action which shouldn't be executed through URL-handlers.
+ * Restore a completely deleted page from the SiteTree_versions table.
*/
- function restorepage() {
- if($id = $this->urlParams['ID']) {
+ function restore() {
+ if(($id = $_REQUEST['ID']) && is_numeric($id)) {
$restoredPage = Versioned::get_latest_version("SiteTree", $id);
- $restoredPage->ID = $restoredPage->RecordID;
+ if($restoredPage) {
+ $restoredPage = $restoredPage->doRestoreToStage();
- // if no record can be found on draft stage (meaning it has been "deleted from draft" before),
- // create an empty record
- if(!DB::query("SELECT \"ID\" FROM \"SiteTree\" WHERE \"ID\" = $restoredPage->ID")->value()) {
- DB::query("INSERT INTO \"SiteTree\" (\"ID\") VALUES ($restoredPage->ID)");
+ FormResponse::get_page($id);
+ $title = Convert::raw2js($restoredPage->TreeTitle());
+ FormResponse::add("$('sitetree').setNodeTitle($id, '$title');");
+ FormResponse::status_message(sprintf(_t('CMSMain.RESTORED',"Restored '%s' successfully",PR_MEDIUM,'Param %s is a title'),$title),'good');
+ return FormResponse::respond();
+
+ } else {
+ return new HTTPResponse("SiteTree #$id not found", 400);
}
-
- $restoredPage->forceChange();
- $restoredPage->writeWithoutVersion();
-
- } else {
- echo _t('CMSMain.VISITRESTORE',"visit restorepage/(ID)",PR_LOW,'restorepage/(ID) should not be translated (is an URL)');
+ } else {
+ return new HTTPResponse("Please pass an ID in the form content", 400);
}
}
diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php
index 76bd3796..572b1e98 100644
--- a/code/LeftAndMain.php
+++ b/code/LeftAndMain.php
@@ -451,13 +451,24 @@ class LeftAndMain extends Controller {
}
public function getRecord($id, $className = null) {
- if(!$className) $className = $this->stat('tree_class');
- return DataObject::get_by_id($className, $rootID);
+ if($id && is_numeric($id)) {
+ if(!$className) $className = $this->stat('tree_class');
+ return DataObject::get_by_id($className, $id);
+ }
}
- function getSiteTreeFor($className, $rootID = null) {
+ /**
+ * Get a site tree displaying the nodes under the given objects
+ * @param $className The class of the root object
+ * @param $rootID The ID of the root object. If this is null then a complete tree will be
+ * shown
+ * @param $childrenMethod The method to call to get the children of the tree. For example,
+ * Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
+ */
+ function getSiteTreeFor($className, $rootID = null,
+ $childrenMethod = "AllChildrenIncludingDeleted") {
$obj = $rootID ? $this->getRecord($rootID) : singleton($className);
- $obj->markPartialTree(30, $this);
+ $obj->markPartialTree(30, $this, $childrenMethod);
if($p = $this->currentPage()) $obj->markToExpose($p);
// getChildrenAsUL is a flexible and complex way of traversing the tree
@@ -467,7 +478,7 @@ class LeftAndMain extends Controller {
($child->TreeTitle()) .
""
'
- ,$this, true);
+ ,$this, true, $childrenMethod);
// Wrap the root if needs be.
@@ -485,9 +496,19 @@ class LeftAndMain extends Controller {
return $siteTree;
}
+ /**
+ * Get a subtree underneath the request param 'ID'.
+ * If ID = 0, then get the whole tree.
+ */
public function getsubtree() {
- $results = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID']);
- return substr(trim($results), 4,-5);
+ // Get the tree
+ $tree = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID']);
+
+ // Trim off the outer tag
+ $tree = ereg_replace('^[ \t\r\n]*
<% _t('TOREORG','To reorganise your site, drag the pages around as desired.') %>
-
+
diff --git a/tests/CMSMainTest.php b/tests/CMSMainTest.php
index 98950311..918e09fe 100644
--- a/tests/CMSMainTest.php
+++ b/tests/CMSMainTest.php
@@ -4,7 +4,6 @@
* @subpackage tests
*/
class CMSMainTest extends FunctionalTest {
-
static $fixture_file = 'cms/tests/CMSMainTest.yml';
protected $autoFollowRedirection = false;
@@ -88,4 +87,48 @@ class CMSMainTest extends FunctionalTest {
$this->assertTrue($page->getCMSFields(null) instanceof FieldSet);
}
}
-}
\ No newline at end of file
+
+ /**
+ * Test that a draft-deleted page can still be opened in the CMS
+ */
+ function testDraftDeletedPageCanBeOpenedInCMS() {
+ // Set up a page that is delete from live
+ $page = $this->objFromFixture('Page','page1');
+ $pageID = $page->ID;
+ $page->doPublish();
+ $page->delete();
+
+ $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin'));
+ $response = $this->get('admin/getitem?ID=' . $pageID . '&ajax=1');
+
+ // Check that the 'delete from live' button exists as a simple way of checking that the correct page is returned.
+ $this->assertRegExp('/]+type="submit"[^>]+name="action_deletefromlive"/i', $response->getBody());
+ }
+
+ /**
+ * Test CMSMain::getRecord()
+ */
+ function testGetRecord() {
+ // Set up a page that is delete from live
+ $page1 = $this->objFromFixture('Page','page1');
+ $page1ID = $page1->ID;
+ $page1->doPublish();
+ $page1->delete();
+
+ $cmsMain = new CMSMain();
+
+ // Bad calls
+ $this->assertNull($cmsMain->getRecord('0'));
+ $this->assertNull($cmsMain->getRecord('asdf'));
+
+ // Pages that are on draft and aren't on draft should both work
+ $this->assertType('Page', $cmsMain->getRecord($page1ID));
+ $this->assertType('Page', $cmsMain->getRecord($this->idFromFixture('Page','page2')));
+
+ // This functionality isn't actually used any more.
+ $newPage = $cmsMain->getRecord('new-Page-5');
+ $this->assertType('Page', $newPage);
+ $this->assertEquals('5', $newPage->ParentID);
+
+ }
+}