mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-09-30 05:09:06 +02:00
API CHANGE Forms in ajax responses from LeftAndMain and subclasses include <form> tags, and replace the form itself on the client (rather than just everything inside the form). Form submissions and loading is handled by its container ('.cms-content') to avoid problems with a DOM element replacing itself upon form submission.
This commit is contained in:
parent
0e0a59df99
commit
ff54044206
@ -383,11 +383,14 @@ class LeftAndMain extends Controller {
|
|||||||
if($request->param('ID')) $this->setCurrentPageID($request->param('ID'));
|
if($request->param('ID')) $this->setCurrentPageID($request->param('ID'));
|
||||||
|
|
||||||
if($this->isAjax()) {
|
if($this->isAjax()) {
|
||||||
SSViewer::setOption('rewriteHashlinks', false);
|
if($request->getVar('cms-view-form')) {
|
||||||
$form = $this->getEditForm($request->param('ID'));
|
$form = $this->getEditForm();
|
||||||
$content = $form->formHtmlContent();
|
$content = $form->forTemplate();
|
||||||
|
} else {
|
||||||
|
// Rendering is handled by template, which will call EditForm() eventually
|
||||||
|
$content = $this->renderWith($this->getTemplatesWithSuffix('_Content'));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Rendering is handled by template, which will call EditForm() eventually
|
|
||||||
$content = $this->renderWith($this->getViewer('show'));
|
$content = $this->renderWith($this->getViewer('show'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,7 +639,7 @@ class LeftAndMain extends Controller {
|
|||||||
// write process might've changed the record, so we reload before returning
|
// write process might've changed the record, so we reload before returning
|
||||||
$form = $this->getEditForm($record->ID);
|
$form = $this->getEditForm($record->ID);
|
||||||
|
|
||||||
return $form->formHtmlContent();
|
return $form->forTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($data, $form) {
|
public function delete($data, $form) {
|
||||||
@ -648,7 +651,7 @@ class LeftAndMain extends Controller {
|
|||||||
$record->delete();
|
$record->delete();
|
||||||
|
|
||||||
if($this->isAjax()) {
|
if($this->isAjax()) {
|
||||||
return $this->EmptyForm()->formHtmlContent();
|
return $this->EmptyForm()->forTemplate();
|
||||||
} else {
|
} else {
|
||||||
$this->redirectBack();
|
$this->redirectBack();
|
||||||
}
|
}
|
||||||
@ -954,7 +957,7 @@ class LeftAndMain extends Controller {
|
|||||||
return $record->ID;
|
return $record->ID;
|
||||||
} else if($this->isAjax()) {
|
} else if($this->isAjax()) {
|
||||||
$form = $this->getEditForm($record->ID);
|
$form = $this->getEditForm($record->ID);
|
||||||
return $form->formHtmlContent();
|
return $form->forTemplate();
|
||||||
} else {
|
} else {
|
||||||
return $this->redirect(Controller::join_links($this->Link('show'), $record->ID));
|
return $this->redirect(Controller::join_links($this->Link('show'), $record->ID));
|
||||||
}
|
}
|
||||||
|
@ -664,7 +664,7 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
$msg = _t('ModelAdmin.NORESULTS',"Your search didn't return any matching items");
|
$msg = _t('ModelAdmin.NORESULTS',"Your search didn't return any matching items");
|
||||||
}
|
}
|
||||||
return new SS_HTTPResponse(
|
return new SS_HTTPResponse(
|
||||||
$resultsForm->formHtmlContent(),
|
$resultsForm->forTemplate(),
|
||||||
200,
|
200,
|
||||||
$msg
|
$msg
|
||||||
);
|
);
|
||||||
@ -794,7 +794,7 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
*/
|
*/
|
||||||
function add($request) {
|
function add($request) {
|
||||||
return new SS_HTTPResponse(
|
return new SS_HTTPResponse(
|
||||||
$this->AddForm()->formHtmlContent(),
|
$this->AddForm()->forTemplate(),
|
||||||
200,
|
200,
|
||||||
sprintf(
|
sprintf(
|
||||||
_t('ModelAdmin.ADDFORM', "Fill out this form to add a %s to the database."),
|
_t('ModelAdmin.ADDFORM', "Fill out this form to add a %s to the database."),
|
||||||
@ -845,6 +845,7 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
|
|
||||||
$form = new Form($this, "AddForm", $fields, $actions, $validator);
|
$form = new Form($this, "AddForm", $fields, $actions, $validator);
|
||||||
$form->loadDataFrom($newRecord);
|
$form->loadDataFrom($newRecord);
|
||||||
|
$form->addExtraClass('cms-edit-form');
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
@ -862,7 +863,7 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
$class = $this->parentController->getRecordControllerClass($this->getModelClass());
|
$class = $this->parentController->getRecordControllerClass($this->getModelClass());
|
||||||
$recordController = new $class($this, $request, $model->ID);
|
$recordController = new $class($this, $request, $model->ID);
|
||||||
return new SS_HTTPResponse(
|
return new SS_HTTPResponse(
|
||||||
$recordController->EditForm()->formHtmlContent(),
|
$recordController->EditForm()->forTemplate(),
|
||||||
200,
|
200,
|
||||||
sprintf(
|
sprintf(
|
||||||
_t('ModelAdmin.LOADEDFOREDITING', "Loaded '%s' for editing."),
|
_t('ModelAdmin.LOADEDFOREDITING', "Loaded '%s' for editing."),
|
||||||
@ -912,7 +913,7 @@ class ModelAdmin_RecordController extends Controller {
|
|||||||
function edit($request) {
|
function edit($request) {
|
||||||
if ($this->currentRecord) {
|
if ($this->currentRecord) {
|
||||||
if($this->isAjax()) {
|
if($this->isAjax()) {
|
||||||
$this->response->setBody($this->EditForm()->formHtmlContent());
|
$this->response->setBody($this->EditForm()->forTemplate());
|
||||||
$this->response->setStatusCode(
|
$this->response->setStatusCode(
|
||||||
200,
|
200,
|
||||||
sprintf(
|
sprintf(
|
||||||
@ -924,10 +925,10 @@ class ModelAdmin_RecordController extends Controller {
|
|||||||
} else {
|
} else {
|
||||||
// This is really quite ugly; to fix will require a change in the way that customise() works. :-(
|
// This is really quite ugly; to fix will require a change in the way that customise() works. :-(
|
||||||
return $this->parentController->parentController->customise(array(
|
return $this->parentController->parentController->customise(array(
|
||||||
'Right' => $this->parentController->parentController->customise(array(
|
'Content' => $this->parentController->parentController->customise(array(
|
||||||
'EditForm' => $this->EditForm()
|
'EditForm' => $this->EditForm()
|
||||||
))->renderWith(array("{$this->class}_right",'LeftAndMain_right'))
|
))->renderWith(array("{$this->class}_Content",'ModelAdmin_Content', 'LeftAndMain_Content'))
|
||||||
))->renderWith(array('ModelAdmin','LeftAndMain'));
|
))->renderWith(array('ModelAdmin', 'LeftAndMain'));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return _t('ModelAdmin.ITEMNOTFOUND', "I can't find that item");
|
return _t('ModelAdmin.ITEMNOTFOUND', "I can't find that item");
|
||||||
@ -963,6 +964,7 @@ class ModelAdmin_RecordController extends Controller {
|
|||||||
|
|
||||||
$form = new Form($this, "EditForm", $fields, $actions, $validator);
|
$form = new Form($this, "EditForm", $fields, $actions, $validator);
|
||||||
$form->loadDataFrom($this->currentRecord);
|
$form->loadDataFrom($this->currentRecord);
|
||||||
|
$form->addExtraClass('cms-edit-form');
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
@ -1017,7 +1019,7 @@ class ModelAdmin_RecordController extends Controller {
|
|||||||
function view($request) {
|
function view($request) {
|
||||||
if($this->currentRecord) {
|
if($this->currentRecord) {
|
||||||
$form = $this->ViewForm();
|
$form = $this->ViewForm();
|
||||||
return $form->formHtmlContent();
|
return $form->forTemplate();
|
||||||
} else {
|
} else {
|
||||||
return _t('ModelAdmin.ITEMNOTFOUND');
|
return _t('ModelAdmin.ITEMNOTFOUND');
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ html, body { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden;
|
|||||||
.cms-menu .cms-panel-content { width: 250px; }
|
.cms-menu .cms-panel-content { width: 250px; }
|
||||||
.cms-menu.collapsed { width: 40px; }
|
.cms-menu.collapsed { width: 40px; }
|
||||||
|
|
||||||
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form { display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: middle; *vertical-align: auto; }
|
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form, .cms-edit-form { display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: middle; *vertical-align: auto; }
|
||||||
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form { *display: inline; }
|
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form, .cms-edit-form { *display: inline; }
|
||||||
|
|
||||||
.cms-content-tools { width: 230px; overflow: auto; }
|
.cms-content-tools { width: 230px; overflow: auto; }
|
||||||
.cms-content-tools .cms-panel-header, .cms-content-tools .cms-panel-content { padding: 10px; }
|
.cms-content-tools .cms-panel-header, .cms-content-tools .cms-panel-content { padding: 10px; }
|
||||||
|
@ -144,7 +144,7 @@ li.jstree-closed > ul { display: none; }
|
|||||||
|
|
||||||
.cms-menu-list li { background-color: #b0bfc6; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #b0bfc6), color-stop(100%, #758f9b)); background-image: -moz-linear-gradient(top, #b0bfc6 0%, #758f9b 100%); background-image: linear-gradient(top, #b0bfc6 0%, #758f9b 100%); border-bottom: 1px solid #aaaaaa; }
|
.cms-menu-list li { background-color: #b0bfc6; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #b0bfc6), color-stop(100%, #758f9b)); background-image: -moz-linear-gradient(top, #b0bfc6 0%, #758f9b 100%); background-image: linear-gradient(top, #b0bfc6 0%, #758f9b 100%); border-bottom: 1px solid #aaaaaa; }
|
||||||
.cms-menu-list li a { display: block; height: 32px; vertical-align: middle; font-size: 14px; text-shadow: #aaaaaa 1px 1px 1px; color: #333333; padding: 5px; }
|
.cms-menu-list li a { display: block; height: 32px; vertical-align: middle; font-size: 14px; text-shadow: #aaaaaa 1px 1px 1px; color: #333333; padding: 5px; }
|
||||||
.cms-menu-list li a .icon { display: block; float: left; margin-right: 5px; background: url('../images/icons-32.png?1305837920') no-repeat; width: 32px; height: 32px; overflow: hidden; background-position: 0px 0px; }
|
.cms-menu-list li a .icon { display: block; float: left; margin-right: 5px; background: url('../images/icons-32.png?1306441269') no-repeat; width: 32px; height: 32px; overflow: hidden; background-position: 0px 0px; }
|
||||||
.cms-menu-list li a:hover .icon { background-position: -32px 0px; }
|
.cms-menu-list li a:hover .icon { background-position: -32px 0px; }
|
||||||
.cms-menu-list li a .text { display: block; padding-top: 10px; }
|
.cms-menu-list li a .text { display: block; padding-top: 10px; }
|
||||||
.cms-menu-list li.current { background-color: #338dc1; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #338dc1), color-stop(100%, #1e5270)); background-image: -moz-linear-gradient(top, #338dc1 0%, #1e5270 100%); background-image: linear-gradient(top, #338dc1 0%, #1e5270 100%); }
|
.cms-menu-list li.current { background-color: #338dc1; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #338dc1), color-stop(100%, #1e5270)); background-image: -moz-linear-gradient(top, #338dc1 0%, #1e5270 100%); background-image: linear-gradient(top, #338dc1 0%, #1e5270 100%); }
|
||||||
|
@ -91,8 +91,9 @@
|
|||||||
data.push({name:button.attr('name'),value:button.val()});
|
data.push({name:button.attr('name'),value:button.val()});
|
||||||
|
|
||||||
// TODO Should be set by hiddenfield already
|
// TODO Should be set by hiddenfield already
|
||||||
jQuery('.cms-edit-form').entwine('ss').loadForm(
|
jQuery('.cms-content').entwine('ss').loadForm(
|
||||||
this.attr('action'),
|
this.attr('action'),
|
||||||
|
null,
|
||||||
function() {
|
function() {
|
||||||
// Tree updates are triggered by Form_EditForm load events
|
// Tree updates are triggered by Form_EditForm load events
|
||||||
button.removeClass('loading');
|
button.removeClass('loading');
|
||||||
@ -129,7 +130,7 @@
|
|||||||
dropdown.find('option').remove();
|
dropdown.find('option').remove();
|
||||||
|
|
||||||
//Use tree hints to find allowed children for this node
|
//Use tree hints to find allowed children for this node
|
||||||
if (className && typeof siteTreeHints !== 'undefined') {
|
if (className && siteTreeHints) {
|
||||||
disallowed = siteTreeHints[className].disallowedChildren;
|
disallowed = siteTreeHints[className].disallowedChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,36 +4,228 @@
|
|||||||
|
|
||||||
$('.LeftAndMain .cms-content').entwine({
|
$('.LeftAndMain .cms-content').entwine({
|
||||||
|
|
||||||
redraw: function() {
|
onmatch: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Listen to tree selection events
|
||||||
|
$('.cms-tree').bind('select_node.jstree', function(e, data) {
|
||||||
|
var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val()
|
||||||
|
|
||||||
|
// Don't allow checking disabled nodes
|
||||||
|
if($(node).hasClass('disabled')) return false;
|
||||||
|
|
||||||
|
// Don't allow reloading of currently selected node,
|
||||||
|
// mainly to avoid doing an ajax request on initial page load
|
||||||
|
if($(node).data('id') == loadedNodeID) return;
|
||||||
|
|
||||||
|
var url = $(node).find('a:first').attr('href');
|
||||||
|
if(url && url != '#') {
|
||||||
|
var xmlhttp = self.loadForm(
|
||||||
|
url,
|
||||||
|
null,
|
||||||
|
function(response) {}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.removeForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeLoad: function(url) {
|
||||||
|
this.addClass('loading');
|
||||||
|
this.cleanup();
|
||||||
},
|
},
|
||||||
|
|
||||||
cleanup: function() {
|
cleanup: function() {
|
||||||
this.empty();
|
this.empty();
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPanel: function(url, callback, ajaxOptions) {
|
/**
|
||||||
|
* Function: loadForm
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (String) url - ..
|
||||||
|
* (Function) callback - (Optional) Called after the form content as been loaded
|
||||||
|
* (Object) ajaxOptions - Object literal merged into the jQuery.ajax() call (Optional)
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* (XMLHTTPRequest)
|
||||||
|
*/
|
||||||
|
loadForm: function(url, form, callback, ajaxOptions) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
if(!form || !form.length) var form = $('.cms-content-form form:first');
|
||||||
|
|
||||||
this.trigger('load', {url: url, args: arguments});
|
// Alert when unsaved changes are present
|
||||||
|
if(form._checkChangeTracker(true) == false) return false;
|
||||||
|
|
||||||
this.cleanup();
|
// hide existing form - shown again through _loadResponse()
|
||||||
|
form.addClass('loading');
|
||||||
|
|
||||||
// TODO Add browser history support
|
this.trigger('loadform', {form: form, url: url});
|
||||||
return $.ajax($.extend({
|
|
||||||
|
form.cleanup();
|
||||||
|
|
||||||
|
return jQuery.ajax(jQuery.extend({
|
||||||
url: url,
|
url: url,
|
||||||
|
// Ensure that form view is loaded (rather than whole "Content" template)
|
||||||
|
data: {'cms-view-form': 1},
|
||||||
complete: function(xmlhttp, status) {
|
complete: function(xmlhttp, status) {
|
||||||
self.loadPanel_onSuccess(xmlhttp.responseText, status, xmlhttp);
|
self.loadForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp);
|
||||||
self.removeClass('loading');
|
|
||||||
|
|
||||||
if(callback) callback.apply(self, arguments);
|
if(callback) callback.apply(self, arguments);
|
||||||
},
|
},
|
||||||
dataType: 'html'
|
dataType: 'html'
|
||||||
}, ajaxOptions));
|
}, ajaxOptions));
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPanel_onSuccess: function(html, status, xmlhttp) {
|
loadForm_responseHandler: function(oldForm, html, status, xmlhttp) {
|
||||||
this.html(html);
|
oldForm.replaceWith(html); // triggers onmatch() on form
|
||||||
this.redraw();
|
|
||||||
|
// set status message based on response
|
||||||
|
var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function: ajaxSubmit
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* {DOMElement} button - The pressed button (optional)
|
||||||
|
* {Function} callback - Called in complete() handler of jQuery.ajax()
|
||||||
|
* {Object} ajaxOptions - Object literal to merge into $.ajax() call
|
||||||
|
* {boolean} loadResponse - Render response through _loadResponse() (Default: true)
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* (boolean)
|
||||||
|
*/
|
||||||
|
submitForm: function(form, button, callback, ajaxOptions, loadResponse) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// look for save button
|
||||||
|
if(!button) button = this.find('.Actions :submit[name=action_save]');
|
||||||
|
// default to first button if none given - simulates browser behaviour
|
||||||
|
if(!button) button = this.find('.Actions :submit:first');
|
||||||
|
|
||||||
|
this.trigger('submitform', {form: form, button: button});
|
||||||
|
|
||||||
|
// set button to "submitting" state
|
||||||
|
$(button).addClass('loading');
|
||||||
|
|
||||||
|
// @todo TinyMCE coupling
|
||||||
|
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
|
||||||
|
|
||||||
|
// validate if required
|
||||||
|
if(!form.validate()) {
|
||||||
|
// TODO Automatically switch to the tab/position of the first error
|
||||||
|
statusMessage("Validation failed.", "bad");
|
||||||
|
|
||||||
|
$(button).removeClass('loading');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save tab selections in order to reconstruct them later
|
||||||
|
var selectedTabs = [];
|
||||||
|
form.find('.ss-tabset').each(function(i, el) {
|
||||||
|
if($(el).attr('id')) selectedTabs.push({id:$(el).attr('id'), selected:$(el).tabs('option', 'selected')});
|
||||||
|
});
|
||||||
|
|
||||||
|
// get all data from the form
|
||||||
|
var formData = form.serializeArray();
|
||||||
|
// add button action
|
||||||
|
formData.push({name: $(button).attr('name'), value:'1'});
|
||||||
|
jQuery.ajax(jQuery.extend({
|
||||||
|
url: form.attr('action'),
|
||||||
|
data: formData,
|
||||||
|
type: 'POST',
|
||||||
|
complete: function(xmlhttp, status) {
|
||||||
|
$(button).removeClass('loading');
|
||||||
|
|
||||||
|
// TODO This should be using the plugin API
|
||||||
|
form.removeClass('changed');
|
||||||
|
|
||||||
|
if(callback) callback(xmlhttp, status);
|
||||||
|
|
||||||
|
// pass along original form data to enable old/new comparisons
|
||||||
|
if(loadResponse !== false) {
|
||||||
|
self.submitForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp, formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-init tabs (in case the form tag itself is a tabset)
|
||||||
|
if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset');
|
||||||
|
|
||||||
|
// re-select previously saved tabs
|
||||||
|
$.each(selectedTabs, function(i, selectedTab) {
|
||||||
|
form.find('#' + selectedTab.id).tabs('select', selectedTab.selected);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dataType: 'html'
|
||||||
|
}, ajaxOptions));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function: _loadResponse
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* {String} data - Either HTML for straight insertion, or eval'ed JavaScript.
|
||||||
|
* If passed as HTML, it is assumed that everying inside the <form> tag is replaced,
|
||||||
|
* but the old <form> tag itself stays intact.
|
||||||
|
* {String} status
|
||||||
|
* {XMLHTTPRequest} xmlhttp - ..
|
||||||
|
* {Array} origData - The original submitted data, useful to do comparisons of changed
|
||||||
|
* values in new form output, e.g. to detect a URLSegment being changed on the serverside.
|
||||||
|
* Array in jQuery serializeArray() notation.
|
||||||
|
*/
|
||||||
|
submitForm_responseHandler: function(oldForm, data, status, xmlhttp, origData) {
|
||||||
|
if(status == 'success') {
|
||||||
|
var form = this.replaceForm(oldForm, data);
|
||||||
|
|
||||||
|
Behaviour.apply(); // refreshes ComplexTableField
|
||||||
|
|
||||||
|
this.trigger('loadnewpage', {form: form, origData: origData, xmlhttp: xmlhttp});
|
||||||
|
}
|
||||||
|
|
||||||
|
// set status message based on response
|
||||||
|
var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {jQuery} New form element
|
||||||
|
*/
|
||||||
|
replaceForm: function(form, html) {
|
||||||
|
form.cleanup();
|
||||||
|
if(html) {
|
||||||
|
var parent = form.parent(), id = form.attr('id');
|
||||||
|
form.replaceWith(html);
|
||||||
|
// Try to get the new form by ID (assuming they're identical), otherwise fall back to the first form in the parent
|
||||||
|
return id ? $('#' + id) : parent.children('form:first');
|
||||||
|
} else {
|
||||||
|
this.removeForm(form);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function: removeForm
|
||||||
|
*
|
||||||
|
* Remove everying inside the <form> tag
|
||||||
|
* with a custom HTML fragment. Useful e.g. for deleting a page in the CMS.
|
||||||
|
* Checks for unsaved changes before removing the form
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* {String} placeholderHtml - Short note why the form has been removed, displayed in <p> tags.
|
||||||
|
* Falls back to the default RemoveText() option (Optional)
|
||||||
|
*/
|
||||||
|
removeForm: function(form, placeholderHtml) {
|
||||||
|
if(!placeholderHtml) placeholderHtml = this.getPlaceholderHtml();
|
||||||
|
// Alert when unsaved changes are present
|
||||||
|
if(form._checkChangeTracker(true) == false) return;
|
||||||
|
this.trigger('removeform');
|
||||||
|
this.html(placeholderHtml);
|
||||||
|
// TODO This should be using the plugin API
|
||||||
|
this.removeClass('changed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -41,30 +41,44 @@
|
|||||||
|
|
||||||
this._setupChangeTracker();
|
this._setupChangeTracker();
|
||||||
|
|
||||||
$('.cms-tree').bind('select_node.jstree', function(e, data) {
|
|
||||||
var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val()
|
|
||||||
|
|
||||||
// Don't allow checking disabled nodes
|
|
||||||
if($(node).hasClass('disabled')) return false;
|
|
||||||
|
|
||||||
// Don't allow reloading of currently selected node,
|
|
||||||
// mainly to avoid doing an ajax request on initial page load
|
|
||||||
if($(node).data('id') == loadedNodeID) return;
|
|
||||||
|
|
||||||
var url = $(node).find('a:first').attr('href');
|
|
||||||
if(url && url != '#') {
|
|
||||||
var xmlhttp = self.loadForm(
|
|
||||||
url,
|
|
||||||
function(response) {}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
self.removeForm();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Can't bind this through jQuery
|
// Can't bind this through jQuery
|
||||||
window.onbeforeunload = function(e) {return self._checkChangeTracker(false);};
|
window.onbeforeunload = function(e) {return self._checkChangeTracker(false);};
|
||||||
|
|
||||||
|
// focus input on first form element
|
||||||
|
this.find(':input:visible:first').focus();
|
||||||
|
|
||||||
|
// Optionally get the form attributes from embedded fields, see Form->formHtmlContent()
|
||||||
|
for(var overrideAttr in {'action':true,'method':true,'enctype':true,'name':true}) {
|
||||||
|
var el = this.find(':input[name='+ '_form_' + overrideAttr + ']');
|
||||||
|
if(el) {
|
||||||
|
this.attr(overrideAttr, el.val());
|
||||||
|
el.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// // Rewrite # links
|
||||||
|
// html = html.replace(/(<a[^>]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#');
|
||||||
|
//
|
||||||
|
// // Rewrite iframe links (for IE)
|
||||||
|
// html = html.replace(/(<iframe[^>]*src=")([^"]+)("[^>]*>)/g, '$1' + $('base').attr('href') + '$2$3');
|
||||||
|
|
||||||
|
// Show validation errors if necessary
|
||||||
|
if(this.hasClass('validationerror')) {
|
||||||
|
// TODO validation shouldnt need a special case
|
||||||
|
statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
|
onunmatch: function() {
|
||||||
|
// Prepare iframes for removal, otherwise we get loading bugs
|
||||||
|
this.find('iframe').each(function() {
|
||||||
|
this.contentWindow.location.href = 'about:blank';
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
|
||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -114,86 +128,7 @@
|
|||||||
* Suppress submission unless it is handled through ajaxSubmit().
|
* Suppress submission unless it is handled through ajaxSubmit().
|
||||||
*/
|
*/
|
||||||
onsubmit: function(e) {
|
onsubmit: function(e) {
|
||||||
this.ajaxSubmit();
|
this.parents('.cms-content').submitForm(this);
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: ajaxSubmit
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* {DOMElement} button - The pressed button (optional)
|
|
||||||
* {Function} callback - Called in complete() handler of jQuery.ajax()
|
|
||||||
* {Object} ajaxOptions - Object literal to merge into $.ajax() call
|
|
||||||
* {boolean} loadResponse - Render response through _loadResponse() (Default: true)
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* (boolean)
|
|
||||||
*/
|
|
||||||
ajaxSubmit: function(button, callback, ajaxOptions, loadResponse) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// look for save button
|
|
||||||
if(!button) button = this.find('.Actions :submit[name=action_save]');
|
|
||||||
// default to first button if none given - simulates browser behaviour
|
|
||||||
if(!button) button = this.find('.Actions :submit:first');
|
|
||||||
|
|
||||||
this.trigger('ajaxsubmit', {button: button});
|
|
||||||
|
|
||||||
// set button to "submitting" state
|
|
||||||
$(button).addClass('loading');
|
|
||||||
|
|
||||||
// @todo TinyMCE coupling
|
|
||||||
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
|
|
||||||
|
|
||||||
// validate if required
|
|
||||||
if(!this.validate()) {
|
|
||||||
// TODO Automatically switch to the tab/position of the first error
|
|
||||||
statusMessage("Validation failed.", "bad");
|
|
||||||
|
|
||||||
$(button).removeClass('loading');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// save tab selections in order to reconstruct them later
|
|
||||||
var selectedTabs = [];
|
|
||||||
this.find('.ss-tabset').each(function(i, el) {
|
|
||||||
if($(el).attr('id')) selectedTabs.push({id:$(el).attr('id'), selected:$(el).tabs('option', 'selected')});
|
|
||||||
});
|
|
||||||
|
|
||||||
// get all data from the form
|
|
||||||
var formData = this.serializeArray();
|
|
||||||
// add button action
|
|
||||||
formData.push({name: $(button).attr('name'), value:'1'});
|
|
||||||
jQuery.ajax(jQuery.extend({
|
|
||||||
url: this.attr('action'),
|
|
||||||
data: formData,
|
|
||||||
type: 'POST',
|
|
||||||
complete: function(xmlhttp, status) {
|
|
||||||
$(button).removeClass('loading');
|
|
||||||
|
|
||||||
// TODO This should be using the plugin API
|
|
||||||
self.removeClass('changed');
|
|
||||||
|
|
||||||
if(callback) callback(xmlhttp, status);
|
|
||||||
|
|
||||||
// pass along original form data to enable old/new comparisons
|
|
||||||
if(loadResponse !== false) {
|
|
||||||
self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-init tabs (in case the form tag itself is a tabset)
|
|
||||||
if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset');
|
|
||||||
|
|
||||||
// re-select previously saved tabs
|
|
||||||
$.each(selectedTabs, function(i, selectedTab) {
|
|
||||||
self.find('#' + selectedTab.id).tabs('select', selectedTab.selected);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
dataType: 'html'
|
|
||||||
}, ajaxOptions));
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -217,67 +152,6 @@
|
|||||||
return isValid;
|
return isValid;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: loadForm
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (String) url - ..
|
|
||||||
* (Function) callback - (Optional) Called after the form content as been loaded
|
|
||||||
* (Object) ajaxOptions - Object literal merged into the jQuery.ajax() call (Optional)
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* (XMLHTTPRequest)
|
|
||||||
*/
|
|
||||||
loadForm: function(url, callback, ajaxOptions) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Alert when unsaved changes are present
|
|
||||||
if(this._checkChangeTracker(true) == false) return false;
|
|
||||||
|
|
||||||
// hide existing form - shown again through _loadResponse()
|
|
||||||
this.addClass('loading');
|
|
||||||
|
|
||||||
this.trigger('load', {url: url});
|
|
||||||
|
|
||||||
this.cleanup();
|
|
||||||
|
|
||||||
return jQuery.ajax(jQuery.extend({
|
|
||||||
url: url,
|
|
||||||
complete: function(xmlhttp, status) {
|
|
||||||
// TODO This should be using the plugin API
|
|
||||||
self.removeClass('changed');
|
|
||||||
|
|
||||||
self._loadResponse(xmlhttp.responseText, status, xmlhttp);
|
|
||||||
|
|
||||||
self.removeClass('loading');
|
|
||||||
|
|
||||||
if(callback) callback.apply(self, arguments);
|
|
||||||
},
|
|
||||||
dataType: 'html'
|
|
||||||
}, ajaxOptions));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: removeForm
|
|
||||||
*
|
|
||||||
* Remove everying inside the <form> tag
|
|
||||||
* with a custom HTML fragment. Useful e.g. for deleting a page in the CMS.
|
|
||||||
* Checks for unsaved changes before removing the form
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* {String} placeholderHtml - Short note why the form has been removed, displayed in <p> tags.
|
|
||||||
* Falls back to the default RemoveText() option (Optional)
|
|
||||||
*/
|
|
||||||
removeForm: function(placeholderHtml) {
|
|
||||||
if(!placeholderHtml) placeholderHtml = this.getPlaceholderHtml();
|
|
||||||
// Alert when unsaved changes are present
|
|
||||||
if(this._checkChangeTracker(true) == false) return;
|
|
||||||
this.trigger('removeform');
|
|
||||||
this.html(placeholderHtml);
|
|
||||||
// TODO This should be using the plugin API
|
|
||||||
this.removeClass('changed');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: cleanup
|
* Function: cleanup
|
||||||
*
|
*
|
||||||
@ -287,79 +161,9 @@
|
|||||||
cleanup: function() {
|
cleanup: function() {
|
||||||
if((typeof tinymce != 'undefined') && tinymce.editors) {
|
if((typeof tinymce != 'undefined') && tinymce.editors) {
|
||||||
$(tinymce.editors).each(function() {
|
$(tinymce.editors).each(function() {
|
||||||
if(typeof(this.remove) == 'function') {
|
if(typeof(this.remove) == 'function') this.remove();
|
||||||
this.remove();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: _loadResponse
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* {String} data - Either HTML for straight insertion, or eval'ed JavaScript.
|
|
||||||
* If passed as HTML, it is assumed that everying inside the <form> tag is replaced,
|
|
||||||
* but the old <form> tag itself stays intact.
|
|
||||||
* {String} status
|
|
||||||
* {XMLHTTPRequest} xmlhttp - ..
|
|
||||||
* {Array} origData - The original submitted data, useful to do comparisons of changed
|
|
||||||
* values in new form output, e.g. to detect a URLSegment being changed on the serverside.
|
|
||||||
* Array in jQuery serializeArray() notation.
|
|
||||||
*/
|
|
||||||
_loadResponse: function(data, status, xmlhttp, origData) {
|
|
||||||
if(status == 'success') {
|
|
||||||
this.cleanup();
|
|
||||||
|
|
||||||
var html = data;
|
|
||||||
|
|
||||||
// Rewrite # links
|
|
||||||
html = html.replace(/(<a[^>]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#');
|
|
||||||
|
|
||||||
// Rewrite iframe links (for IE)
|
|
||||||
html = html.replace(/(<iframe[^>]*src=")([^"]+)("[^>]*>)/g, '$1' + $('base').attr('href') + '$2$3');
|
|
||||||
|
|
||||||
// Prepare iframes for removal, otherwise we get loading bugs
|
|
||||||
this.find('iframe').each(function() {
|
|
||||||
this.contentWindow.location.href = 'about:blank';
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// update form content
|
|
||||||
if(html) {
|
|
||||||
this.html(html);
|
|
||||||
} else {
|
|
||||||
this.removeForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the form itself is a tabset, force re-rendering
|
|
||||||
if(this.hasClass('ss-tabset')) this.tabs('destroy').tabs();
|
|
||||||
|
|
||||||
this._setupChangeTracker();
|
|
||||||
|
|
||||||
// Optionally get the form attributes from embedded fields, see Form->formHtmlContent()
|
|
||||||
for(var overrideAttr in {'action':true,'method':true,'enctype':true,'name':true}) {
|
|
||||||
var el = this.find(':input[name='+ '_form_' + overrideAttr + ']');
|
|
||||||
if(el) {
|
|
||||||
this.attr(overrideAttr, el.val());
|
|
||||||
el.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behaviour.apply(); // refreshes ComplexTableField
|
|
||||||
|
|
||||||
// focus input on first form element
|
|
||||||
this.find(':input:visible:first').focus();
|
|
||||||
|
|
||||||
this.trigger('loadnewpage', {data: data, origData: origData, xmlhttp: xmlhttp});
|
|
||||||
}
|
|
||||||
|
|
||||||
// set status message based on response
|
|
||||||
var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
|
|
||||||
if(this.hasClass('validationerror')) {
|
|
||||||
// TODO validation shouldnt need a special case
|
|
||||||
statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -376,7 +180,7 @@
|
|||||||
* Function: onclick
|
* Function: onclick
|
||||||
*/
|
*/
|
||||||
onclick: function(e) {
|
onclick: function(e) {
|
||||||
jQuery('.cms-edit-form').entwine('ss').ajaxSubmit(this);
|
$('.cms-content').submitForm(this.parents('form'), this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -73,6 +73,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
redraw: function() {
|
redraw: function() {
|
||||||
|
// Not all edit forms are layouted
|
||||||
|
var editForm = this.find('.cms-edit-form[data-layout]').layout();
|
||||||
this.find('.cms-content').layout();
|
this.find('.cms-content').layout();
|
||||||
this.find('.cms-container').layout({resize: false})
|
this.find('.cms-container').layout({resize: false})
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,9 @@
|
|||||||
var btn = $(this[0].clickedButton);
|
var btn = $(this[0].clickedButton);
|
||||||
btn.addClass('loading');
|
btn.addClass('loading');
|
||||||
|
|
||||||
$('.cms-edit-form').loadForm(
|
$('.cms-content').loadForm(
|
||||||
this.attr('action'),
|
this.attr('action'),
|
||||||
|
null,
|
||||||
function() {
|
function() {
|
||||||
btn.removeClass('loading');
|
btn.removeClass('loading');
|
||||||
},
|
},
|
||||||
@ -135,7 +136,8 @@
|
|||||||
onclick: function(e) {
|
onclick: function(e) {
|
||||||
var firstLink = this.find('a[href]');
|
var firstLink = this.find('a[href]');
|
||||||
if(!firstLink) return;
|
if(!firstLink) return;
|
||||||
$('.cms-edit-form').loadForm(firstLink.attr('href'));
|
|
||||||
|
window.History.pushState({selector: '.cms-edit-form'}, '', firstLink.attr('href'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -153,8 +155,9 @@
|
|||||||
className = $('select option:selected', this).val();
|
className = $('select option:selected', this).val();
|
||||||
requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
|
requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
|
||||||
var $button = $(':submit', this);
|
var $button = $(':submit', this);
|
||||||
$('.cms-edit-form').loadForm(
|
$('.cms-content').loadForm(
|
||||||
requestPath,
|
requestPath,
|
||||||
|
null,
|
||||||
function() {
|
function() {
|
||||||
$button.removeClass('loading');
|
$button.removeClass('loading');
|
||||||
$button = null;
|
$button = null;
|
||||||
|
@ -51,7 +51,8 @@ body .ui-widget {
|
|||||||
.cms-content,
|
.cms-content,
|
||||||
.cms-content-header,
|
.cms-content-header,
|
||||||
.cms-content-tools,
|
.cms-content-tools,
|
||||||
.cms-content-form {
|
.cms-content-form,
|
||||||
|
.cms-edit-form {
|
||||||
@include inline-block;
|
@include inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1 @@
|
|||||||
<div class="cms-content center ss-tabset" data-layout="{type: 'border'}">
|
|
||||||
$EditForm
|
$EditForm
|
||||||
</div>
|
|
@ -1,33 +1,31 @@
|
|||||||
<div class="cms-content-header north">
|
<% if IncludeFormTag %>
|
||||||
<div>
|
<form $FormAttributes data-layout="{type: 'border'}">
|
||||||
<h2> My Page Title</h2>
|
<% end_if %>
|
||||||
<% if Fields.hasTabset %>
|
|
||||||
<% with Fields.fieldByName('Root') %>
|
|
||||||
<div class="cms-content-header-tabs">
|
|
||||||
<ul>
|
|
||||||
<% control Tabs %>
|
|
||||||
<li><a href="#$id">$Title</a></li>
|
|
||||||
<% end_control %>
|
|
||||||
</ul>
|
|
||||||
<% end_with %>
|
|
||||||
</div>
|
|
||||||
<% end_if %>
|
|
||||||
|
|
||||||
<!-- <div class="cms-content-search">...</div> -->
|
<div class="cms-content-header north">
|
||||||
|
<div>
|
||||||
|
<h2> My Page Title</h2>
|
||||||
|
<% if Fields.hasTabset %>
|
||||||
|
<% with Fields.fieldByName('Root') %>
|
||||||
|
<div class="cms-content-header-tabs">
|
||||||
|
<ul>
|
||||||
|
<% control Tabs %>
|
||||||
|
<li><a href="#$id">$Title</a></li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
<% end_with %>
|
||||||
|
</div>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<!-- <div class="cms-content-search">...</div> -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cms-content-form center">
|
<div class="cms-content-form center">
|
||||||
|
|
||||||
<% if IncludeFormTag %>
|
<!-- <div class="cms-content-tools west">
|
||||||
<form $FormAttributes>
|
$Left
|
||||||
<% end_if %>
|
</div> -->
|
||||||
|
|
||||||
<!-- <div class="cms-content-tools west">
|
|
||||||
$Left
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="center">
|
|
||||||
|
|
||||||
<% if Message %>
|
<% if Message %>
|
||||||
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
|
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
|
||||||
@ -55,8 +53,6 @@
|
|||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if IncludeFormTag %>
|
<% if IncludeFormTag %>
|
||||||
</form>
|
</form>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user