Skip to content

Commit

Permalink
FEATURE Changed CMSMain and LeftAndMain form submissions to return ra…
Browse files Browse the repository at this point in the history
…w HTML instead of using FormResponse logic and evaluated javascript. This allows a more customizeable UI layer that is decoupled from the serverside logic. Any state changes should be propagated through the form itself.

ENHANCEMENT Using new 'X-STATUS' HTTP response header for CMS form responses, as it is more robust for submitting variable length strings than the original 'Status' header. The status is evaluated in LeftAndMain.EditForm.js
API CHANGE Removed CMSMain->tellBrowserAboutPublicationChange(), LeftAndMain->returnItemToUser(), LeftAndMain->getActionUpdateJS(), LeftAndMain->addTreeNodeJS(), LeftAndMain->deleteTreeNodeJS(). Use javascript to respond to state changes
API CHANGE Removed CMSForm and CMSRightForm javascript classes, superseded by LeftAndMain.EditForm.js
ENHANCEMENT Removed custom change detection in LeftAndMain->save(), this should be handled by DataObject->write()
ENHANCEMENT Removed switch in LeftAndMain->save() which doesnt process saving if the record hasn't been altered, to simplify the saving logic
ENHANCEMENT Removed custom add/remove tree node logic in LeftAndMain->save() which was retrieving state from DataObjectLog. This was never actively used, and should be handled by custom clientside logic.

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92674 467b73ca-7a2a-4603-9d3b-597d59a354a9
  • Loading branch information
chillu committed Nov 21, 2009
1 parent a397804 commit c2d24f9
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 672 deletions.
1 change: 0 additions & 1 deletion code/AssetAdmin.php
Expand Up @@ -578,7 +578,6 @@ public function deletefolder() {
if(is_numeric($id)) { if(is_numeric($id)) {
$record = DataObject::get_by_id($this->stat('tree_class'), $id); $record = DataObject::get_by_id($this->stat('tree_class'), $id);
if($record) { if($record) {
$script .= $this->deleteTreeNodeJS($record);
$record->delete(); $record->delete();
$record->destroy(); $record->destroy();
} }
Expand Down
242 changes: 125 additions & 117 deletions code/CMSMain.php
Expand Up @@ -369,6 +369,10 @@ public function getEditForm($id) {
} }
} }


$form = new Form($this, "EditForm", $fields, $actions);
$form->loadDataFrom($record);
$form->disableDefaultAction();

// Add a default or custom validator. // Add a default or custom validator.
// @todo Currently the default Validator.js implementation // @todo Currently the default Validator.js implementation
// adds javascript to the document body, meaning it won't // adds javascript to the document body, meaning it won't
Expand All @@ -387,15 +391,6 @@ public function getEditForm($id) {
} else { } else {
$form->unsetValidator(); $form->unsetValidator();
} }

// The clientside (mainly LeftAndMain*.js) rely on ajax responses
// which can be evaluated as javascript, hence we need
// to override any global changes to the validation handler.
$validator->setJavascriptValidationHandler('prototype');

$form = new Form($this, "EditForm", $fields, $actions, $validator);
$form->loadDataFrom($record);
$form->disableDefaultAction();


if(!$record->canEdit() || $record->IsDeletedFromStage) { if(!$record->canEdit() || $record->IsDeletedFromStage) {
$readonlyFields = $form->Fields()->makeReadonly(); $readonlyFields = $form->Fields()->makeReadonly();
Expand All @@ -421,27 +416,29 @@ public function getEditForm($id) {
// Data saving handlers // Data saving handlers




public function addpage() { public function addpage($data, $form) {
$className = isset($_REQUEST['PageType']) ? $_REQUEST['PageType'] : "Page"; $className = isset($data['PageType']) ? $data['PageType'] : "Page";
$parent = isset($_REQUEST['ParentID']) ? $_REQUEST['ParentID'] : 0; $parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
$suffix = isset($_REQUEST['Suffix']) ? "-" . $_REQUEST['Suffix'] : null; $suffix = isset($data['Suffix']) ? "-" . $data['Suffix'] : null;


if(!$parent && isset($_REQUEST['Parent'])) { if(!$parentID && isset($data['Parent'])) {
$page = SiteTree::get_by_link($_REQUEST['Parent']); $page = SiteTree:: get_by_link(Convert::raw2sql($data['Parent']));
if($page) $parent = $page->ID; if($page) $parentID = $page->ID;
} }


if(is_numeric($parent)) $parentObj = DataObject::get_by_id("SiteTree", $parent); if(is_numeric($parentID)) $parentObj = DataObject::get_by_id("SiteTree", $parentID);
if(!$parentObj || !$parentObj->ID) $parent = 0; if(!$parentObj || !$parentObj->ID) $parentID = 0;


if($parentObj && !$parentObj->canAddChildren()) return Security::permissionFailure($this); if($parentObj && !$parentObj->canAddChildren()) return Security::permissionFailure($this);
if(!singleton($className)->canCreate()) return Security::permissionFailure($this); if(!singleton($className)->canCreate()) return Security::permissionFailure($this);


$p = $this->getNewItem("new-$className-$parent".$suffix, false); $p = $this->getNewItem("new-$className-$parent".$suffix, false);
$p->Locale = $_REQUEST['Locale']; $p->Locale = $data['Locale'];
$p->write(); $p->write();


return $this->returnItemToUser($p); $form = $this->getEditForm($p->ID);

return $form->formHtmlContent();
} }


/** /**
Expand Down Expand Up @@ -491,10 +488,9 @@ public function getNewItem($id, $setID = true) {
* *
* @see delete() * @see delete()
*/ */
public function deletefromlive($urlParams, $form) { public function deletefromlive($data, $form) {
$id = $_REQUEST['ID'];
Versioned::reading_stage('Live'); Versioned::reading_stage('Live');
$record = DataObject::get_by_id("SiteTree", $id); $record = DataObject::get_by_id("SiteTree", $data['ID']);
if($record && !$record->canDelete()) return Security::permissionFailure($this); if($record && !$record->canDelete()) return Security::permissionFailure($this);


$descRemoved = ''; $descRemoved = '';
Expand Down Expand Up @@ -525,10 +521,17 @@ public function deletefromlive($urlParams, $form) {
$descRemoved = ''; $descRemoved = '';
} }


FormResponse::add($this->deleteTreeNodeJS($record)); $this->response->addHeader(
FormResponse::status_message(sprintf(_t('CMSMain.REMOVED', 'Deleted \'%s\'%s from live site'), $record->Title, $descRemoved), 'good'); 'X-Status',
sprintf(
_t('CMSMain.REMOVED', 'Deleted \'%s\'%s from live site'),
$record->Title,
$descRemoved
)
);


return FormResponse::respond(); // nothing to return
return '';
} }


/** /**
Expand All @@ -547,22 +550,37 @@ public function performPublish($record) {
* *
* @uses SiteTree->doRevertToLive() * @uses SiteTree->doRevertToLive()
*/ */
public function revert($urlParams, $form) { public function revert($data, $form) {
$id = (int)$_REQUEST['ID']; if(!isset($data['ID'])) return new HTTPResponse("Please pass an ID in the form content", 400);
$record = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree_Live\".\"ID\" = '{$id}'");
$restoredPage = Versioned::get_latest_version("SiteTree", $data['ID']);
if(!$restoredPage) return new HTTPResponse("SiteTree #$id not found", 400);

$record = Versioned::get_one_by_stage(
'SiteTree',
'Live',
sprintf("\"SiteTree_Live\".\"ID\" = '%d'", (int)$data['ID'])
);


// a user can restore a page without publication rights, as it just adds a new draft state // 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") // (this action should just be available when page has been "deleted from draft")
if(isset($record) && $record && !$record->canEdit()) return Security::permissionFailure($this); if(isset($record) && $record && !$record->canEdit()) {
return Security::permissionFailure($this);
}


$record->doRevertToLive(); $record->doRevertToLive();


$title = Convert::raw2js($record->Title); $this->response->addHeader(
FormResponse::get_page($id); 'X-Status',
FormResponse::add("$('sitetree').setNodeTitle($id, '$title');"); sprintf(
FormResponse::status_message(sprintf(_t('CMSMain.RESTORED',"Restored '%s' successfully",PR_MEDIUM,'Param %s is a title'),$title),'good'); _t('CMSMain.RESTORED',"Restored '%s' successfully",PR_MEDIUM,'Param %s is a title'),

$record->Title
return FormResponse::respond(); )
);

$form = $this->getEditForm($record->ID);

return $form->formHtmlContent();
} }


/** /**
Expand All @@ -580,20 +598,23 @@ public function delete($data, $form) {
$recordID = $record->ID; $recordID = $record->ID;
$record->delete(); $record->delete();


$this->response->addHeader(
'X-Status',
sprintf(
_t('CMSMain.REMOVEDPAGEFROMDRAFT',"Removed '%s' from the draft site"),
$record->Title
)
);

if(Director::is_ajax()) { if(Director::is_ajax()) {
// need a valid ID value even if the record doesn't have one in the database // need a valid ID value even if the record doesn't have one in the database
// (its still present in the live tables) // (its still present in the live tables)
$liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree_Live\".\"ID\" = $recordID"); $liveRecord = Versioned::get_one_by_stage(
// if the page has never been published to live, we need to act the same way as in deletefromlive() 'SiteTree',
if($liveRecord) { 'Live',
// the form is readonly now, so we need to refresh the representation "\"SiteTree_Live\".\"ID\" = $recordID"
FormResponse::get_page($recordID); );
return $this->tellBrowserAboutPublicationChange($liveRecord, sprintf(_t('CMSMain.REMOVEDPAGEFROMDRAFT',"Removed '%s' from the draft site"),$record->Title)); return ($liveRecord) ? $form->formHtmlContent() : "";
} else {
FormResponse::add($this->deleteTreeNodeJS($record));
FormResponse::status_message(sprintf(_t('CMSMain.REMOVEDPAGEFROMDRAFT',"Removed '%s' from the draft site"),$record->Title), 'good');
return FormResponse::respond();
}
} else { } else {
Director::redirectBack(); Director::redirectBack();
} }
Expand Down Expand Up @@ -674,65 +695,49 @@ function versions() {
/** /**
* Roll a page back to a previous version * Roll a page back to a previous version
*/ */
function rollback() { function rollback($data, $form) {
if(isset($_REQUEST['Version']) && (bool)$_REQUEST['Version']) { if(isset($data['Version']) && (bool)$data['Version']) {
$record = $this->performRollback($_REQUEST['ID'], $_REQUEST['Version']); $record = $this->performRollback($data['ID'], $data['Version']);
echo sprintf(_t('CMSMain.ROLLEDBACKVERSION',"Rolled back to version #%d. New version number is #%d"),$_REQUEST['Version'],$record->Version); $message = sprintf(
_t('CMSMain.ROLLEDBACKVERSION',"Rolled back to version #%d. New version number is #%d"),
$data['Version'],
$record->Version
);
} else { } else {
$record = $this->performRollback($_REQUEST['ID'], "Live"); $record = $this->performRollback($data['ID'], "Live");
echo sprintf(_t('CMSMain.ROLLEDBACKPUB',"Rolled back to published version. New version number is #%d"),$record->Version); $message = sprintf(
_t('CMSMain.ROLLEDBACKPUB',"Rolled back to published version. New version number is #%d"),
$record->Version
);
} }

$this->response->addHeader('X-Status', $message);

$form = $this->getEditForm($record->ID);

return $form->formHtmlContent();
} }


function publish($urlParams, $form) { function publish($data, $form) {
$urlParams['publish'] = '1'; $data['publish'] = '1';


return $this->save($urlParams, $form); return $this->save($data, $form);
} }


function unpublish() { function unpublish($data, $form) {
$SQL_id = Convert::raw2sql($_REQUEST['ID']); $page = DataObject::get_by_id("SiteTree", $data['ID']);

$page = DataObject::get_by_id("SiteTree", $SQL_id);
if($page && !$page->canPublish()) return Security::permissionFailure($this); if($page && !$page->canPublish()) return Security::permissionFailure($this);


$page->doUnpublish(); $page->doUnpublish();


return $this->tellBrowserAboutPublicationChange($page, sprintf(_t('CMSMain.REMOVEDPAGE',"Removed '%s' from the published site"),$page->Title)); $this->response->addHeader(
} 'X-Status',

sprintf(_t('CMSMain.REMOVEDPAGE',"Removed '%s' from the published site"),$page->Title)
/** );
* Return a few pieces of information about a change to a page
* - Send the new status message
* - Update the action buttons
* - Update the treenote
* - Send a status message
*/
function tellBrowserAboutPublicationChange($page, $statusMessage) {
$JS_title = Convert::raw2js($page->TreeTitle());

$JS_stageURL = $page->IsDeletedFromStage ? '' : Convert::raw2js($page->AbsoluteLink());
$liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree\".\"ID\" = $page->ID");

$JS_liveURL = $liveRecord ? Convert::raw2js($liveRecord->AbsoluteLink()) : '';

FormResponse::add($this->getActionUpdateJS($page));
FormResponse::update_status($page->Status);


if($JS_stageURL || $JS_liveURL) { $form->loadDataFrom($page);
FormResponse::add("\$('sitetree').setNodeTitle($page->ID, '$JS_title');");
} else {
FormResponse::add("var node = $('sitetree').getTreeNodeByIdx('$page->ID');");
FormResponse::add("if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);");
FormResponse::add("$('Form_EditForm').reloadIfSetTo($page->ID);");
}


if($statusMessage) FormResponse::status_message($statusMessage, 'good'); return $form->formHtmlContent();
FormResponse::add("$('Form_EditForm').elements.StageURLSegment.value = '$JS_stageURL';");
FormResponse::add("$('Form_EditForm').elements.LiveURLSegment.value = '$JS_liveURL';");
FormResponse::add("$('Form_EditForm').notify('PagePublished', $('Form_EditForm').elements.ID.value);");

return FormResponse::respond();
} }


function performRollback($id, $version) { function performRollback($id, $version) {
Expand Down Expand Up @@ -1085,6 +1090,7 @@ function SearchTreeForm() {
) )
) )
); );
$form->unsetValidator();


return $form; return $form;
} }
Expand Down Expand Up @@ -1167,24 +1173,26 @@ function publishall() {
/** /**
* Restore a completely deleted page from the SiteTree_versions table. * Restore a completely deleted page from the SiteTree_versions table.
*/ */
function restore() { function restore($data, $form) {
if(($id = $_REQUEST['ID']) && is_numeric($id)) { if(!isset($data['ID']) || !is_numeric($data['ID'])) {
$restoredPage = Versioned::get_latest_version("SiteTree", $id); return new HTTPResponse("Please pass an ID in the form content", 400);
if($restoredPage) {
$restoredPage = $restoredPage->doRestoreToStage();

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 SS_HTTPResponse("SiteTree #$id not found", 400);
}
} else {
return new SS_HTTPResponse("Please pass an ID in the form content", 400);
} }

$restoredPage = Versioned::get_latest_version("SiteTree", $id);
if(!$restoredPage) return new HTTPResponse("SiteTree #$id not found", 400);

$restoredPage = $restoredPage->doRestoreToStage();

$this->response->addHeader(
'X-Status',
sprintf(
_t('CMSMain.RESTORED',"Restored '%s' successfully",PR_MEDIUM,'Param %s is a title'),
$restoredPage->TreeTitle
)
);

$form = $this->getEditForm($id);
return $form->formHtmlContent();
} }


function duplicate() { function duplicate() {
Expand All @@ -1201,8 +1209,9 @@ function duplicate() {
$newPage->ParentID = $_GET['parentID']; $newPage->ParentID = $_GET['parentID'];
$newPage->write(); $newPage->write();
} }


return $this->returnItemToUser($newPage); $form = $this->getEditForm($newPage->ID);
return $form->formHtmlContent();
} else { } else {
user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING); user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING);
} }
Expand All @@ -1217,14 +1226,13 @@ function duplicatewithchildren() {


$newPage = $page->duplicateWithChildren(); $newPage = $page->duplicateWithChildren();


return $this->returnItemToUser($newPage); $form = $this->getEditForm($newPage->ID);
return $form->formHtmlContent();
} else { } else {
user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING); user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING);
} }
} }




/** /**
* Create a new translation from an existing item, switch to this language and reload the tree. * Create a new translation from an existing item, switch to this language and reload the tree.
*/ */
Expand Down

0 comments on commit c2d24f9

Please sign in to comment.