mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +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
@ -377,17 +377,20 @@ class LeftAndMain extends Controller {
|
||||
if(!$title) $title = preg_replace('/Admin$/', '', $class);
|
||||
return $title;
|
||||
}
|
||||
|
||||
|
||||
public function show($request) {
|
||||
// TODO Necessary for TableListField URLs to work properly
|
||||
if($request->param('ID')) $this->setCurrentPageID($request->param('ID'));
|
||||
|
||||
if($this->isAjax()) {
|
||||
SSViewer::setOption('rewriteHashlinks', false);
|
||||
$form = $this->getEditForm($request->param('ID'));
|
||||
$content = $form->formHtmlContent();
|
||||
if($request->getVar('cms-view-form')) {
|
||||
$form = $this->getEditForm();
|
||||
$content = $form->forTemplate();
|
||||
} else {
|
||||
// Rendering is handled by template, which will call EditForm() eventually
|
||||
$content = $this->renderWith($this->getTemplatesWithSuffix('_Content'));
|
||||
}
|
||||
} else {
|
||||
// Rendering is handled by template, which will call EditForm() eventually
|
||||
$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
|
||||
$form = $this->getEditForm($record->ID);
|
||||
|
||||
return $form->formHtmlContent();
|
||||
return $form->forTemplate();
|
||||
}
|
||||
|
||||
public function delete($data, $form) {
|
||||
@ -648,7 +651,7 @@ class LeftAndMain extends Controller {
|
||||
$record->delete();
|
||||
|
||||
if($this->isAjax()) {
|
||||
return $this->EmptyForm()->formHtmlContent();
|
||||
return $this->EmptyForm()->forTemplate();
|
||||
} else {
|
||||
$this->redirectBack();
|
||||
}
|
||||
@ -954,7 +957,7 @@ class LeftAndMain extends Controller {
|
||||
return $record->ID;
|
||||
} else if($this->isAjax()) {
|
||||
$form = $this->getEditForm($record->ID);
|
||||
return $form->formHtmlContent();
|
||||
return $form->forTemplate();
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
return new SS_HTTPResponse(
|
||||
$resultsForm->formHtmlContent(),
|
||||
$resultsForm->forTemplate(),
|
||||
200,
|
||||
$msg
|
||||
);
|
||||
@ -794,7 +794,7 @@ class ModelAdmin_CollectionController extends Controller {
|
||||
*/
|
||||
function add($request) {
|
||||
return new SS_HTTPResponse(
|
||||
$this->AddForm()->formHtmlContent(),
|
||||
$this->AddForm()->forTemplate(),
|
||||
200,
|
||||
sprintf(
|
||||
_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->loadDataFrom($newRecord);
|
||||
$form->addExtraClass('cms-edit-form');
|
||||
|
||||
return $form;
|
||||
}
|
||||
@ -862,7 +863,7 @@ class ModelAdmin_CollectionController extends Controller {
|
||||
$class = $this->parentController->getRecordControllerClass($this->getModelClass());
|
||||
$recordController = new $class($this, $request, $model->ID);
|
||||
return new SS_HTTPResponse(
|
||||
$recordController->EditForm()->formHtmlContent(),
|
||||
$recordController->EditForm()->forTemplate(),
|
||||
200,
|
||||
sprintf(
|
||||
_t('ModelAdmin.LOADEDFOREDITING', "Loaded '%s' for editing."),
|
||||
@ -912,7 +913,7 @@ class ModelAdmin_RecordController extends Controller {
|
||||
function edit($request) {
|
||||
if ($this->currentRecord) {
|
||||
if($this->isAjax()) {
|
||||
$this->response->setBody($this->EditForm()->formHtmlContent());
|
||||
$this->response->setBody($this->EditForm()->forTemplate());
|
||||
$this->response->setStatusCode(
|
||||
200,
|
||||
sprintf(
|
||||
@ -924,10 +925,10 @@ class ModelAdmin_RecordController extends Controller {
|
||||
} else {
|
||||
// This is really quite ugly; to fix will require a change in the way that customise() works. :-(
|
||||
return $this->parentController->parentController->customise(array(
|
||||
'Right' => $this->parentController->parentController->customise(array(
|
||||
'Content' => $this->parentController->parentController->customise(array(
|
||||
'EditForm' => $this->EditForm()
|
||||
))->renderWith(array("{$this->class}_right",'LeftAndMain_right'))
|
||||
))->renderWith(array('ModelAdmin','LeftAndMain'));
|
||||
))->renderWith(array("{$this->class}_Content",'ModelAdmin_Content', 'LeftAndMain_Content'))
|
||||
))->renderWith(array('ModelAdmin', 'LeftAndMain'));
|
||||
}
|
||||
} else {
|
||||
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->loadDataFrom($this->currentRecord);
|
||||
$form->addExtraClass('cms-edit-form');
|
||||
|
||||
return $form;
|
||||
}
|
||||
@ -1017,7 +1019,7 @@ class ModelAdmin_RecordController extends Controller {
|
||||
function view($request) {
|
||||
if($this->currentRecord) {
|
||||
$form = $this->ViewForm();
|
||||
return $form->formHtmlContent();
|
||||
return $form->forTemplate();
|
||||
} else {
|
||||
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.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 { *display: inline; }
|
||||
.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, .cms-edit-form { *display: inline; }
|
||||
|
||||
.cms-content-tools { width: 230px; overflow: auto; }
|
||||
.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 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 .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%); }
|
||||
|
@ -91,8 +91,9 @@
|
||||
data.push({name:button.attr('name'),value:button.val()});
|
||||
|
||||
// TODO Should be set by hiddenfield already
|
||||
jQuery('.cms-edit-form').entwine('ss').loadForm(
|
||||
jQuery('.cms-content').entwine('ss').loadForm(
|
||||
this.attr('action'),
|
||||
null,
|
||||
function() {
|
||||
// Tree updates are triggered by Form_EditForm load events
|
||||
button.removeClass('loading');
|
||||
@ -129,7 +130,7 @@
|
||||
dropdown.find('option').remove();
|
||||
|
||||
//Use tree hints to find allowed children for this node
|
||||
if (className && typeof siteTreeHints !== 'undefined') {
|
||||
if (className && siteTreeHints) {
|
||||
disallowed = siteTreeHints[className].disallowedChildren;
|
||||
}
|
||||
|
||||
|
@ -4,36 +4,228 @@
|
||||
|
||||
$('.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() {
|
||||
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;
|
||||
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;
|
||||
|
||||
// hide existing form - shown again through _loadResponse()
|
||||
form.addClass('loading');
|
||||
|
||||
this.cleanup();
|
||||
this.trigger('loadform', {form: form, url: url});
|
||||
|
||||
form.cleanup();
|
||||
|
||||
// TODO Add browser history support
|
||||
return $.ajax($.extend({
|
||||
return jQuery.ajax(jQuery.extend({
|
||||
url: url,
|
||||
// Ensure that form view is loaded (rather than whole "Content" template)
|
||||
data: {'cms-view-form': 1},
|
||||
complete: function(xmlhttp, status) {
|
||||
self.loadPanel_onSuccess(xmlhttp.responseText, status, xmlhttp);
|
||||
self.removeClass('loading');
|
||||
|
||||
self.loadForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp);
|
||||
if(callback) callback.apply(self, arguments);
|
||||
},
|
||||
dataType: 'html'
|
||||
}, ajaxOptions));
|
||||
},
|
||||
|
||||
loadForm_responseHandler: function(oldForm, html, status, xmlhttp) {
|
||||
oldForm.replaceWith(html); // triggers onmatch() on form
|
||||
|
||||
// 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");
|
||||
|
||||
loadPanel_onSuccess: function(html, status, xmlhttp) {
|
||||
this.html(html);
|
||||
this.redraw();
|
||||
$(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');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -40,33 +40,47 @@
|
||||
var self = this;
|
||||
|
||||
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
|
||||
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();
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: _setupChangeTracker
|
||||
@ -114,90 +128,11 @@
|
||||
* Suppress submission unless it is handled through ajaxSubmit().
|
||||
*/
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: validate
|
||||
*
|
||||
@ -217,67 +152,6 @@
|
||||
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
|
||||
*
|
||||
@ -287,79 +161,9 @@
|
||||
cleanup: function() {
|
||||
if((typeof tinymce != 'undefined') && tinymce.editors) {
|
||||
$(tinymce.editors).each(function() {
|
||||
if(typeof(this.remove) == 'function') {
|
||||
this.remove();
|
||||
}
|
||||
if(typeof(this.remove) == 'function') 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
|
||||
*/
|
||||
onclick: function(e) {
|
||||
jQuery('.cms-edit-form').entwine('ss').ajaxSubmit(this);
|
||||
$('.cms-content').submitForm(this.parents('form'), this);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
@ -73,6 +73,8 @@
|
||||
},
|
||||
|
||||
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-container').layout({resize: false})
|
||||
}
|
||||
|
@ -71,8 +71,9 @@
|
||||
var btn = $(this[0].clickedButton);
|
||||
btn.addClass('loading');
|
||||
|
||||
$('.cms-edit-form').loadForm(
|
||||
$('.cms-content').loadForm(
|
||||
this.attr('action'),
|
||||
null,
|
||||
function() {
|
||||
btn.removeClass('loading');
|
||||
},
|
||||
@ -135,7 +136,8 @@
|
||||
onclick: function(e) {
|
||||
var firstLink = this.find('a[href]');
|
||||
if(!firstLink) return;
|
||||
$('.cms-edit-form').loadForm(firstLink.attr('href'));
|
||||
|
||||
window.History.pushState({selector: '.cms-edit-form'}, '', firstLink.attr('href'));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -153,8 +155,9 @@
|
||||
className = $('select option:selected', this).val();
|
||||
requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
|
||||
var $button = $(':submit', this);
|
||||
$('.cms-edit-form').loadForm(
|
||||
$('.cms-content').loadForm(
|
||||
requestPath,
|
||||
null,
|
||||
function() {
|
||||
$button.removeClass('loading');
|
||||
$button = null;
|
||||
|
@ -51,7 +51,8 @@ body .ui-widget {
|
||||
.cms-content,
|
||||
.cms-content-header,
|
||||
.cms-content-tools,
|
||||
.cms-content-form {
|
||||
.cms-content-form,
|
||||
.cms-edit-form {
|
||||
@include inline-block;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1 @@
|
||||
<div class="cms-content center ss-tabset" data-layout="{type: 'border'}">
|
||||
$EditForm
|
||||
</div>
|
||||
$EditForm
|
@ -1,34 +1,32 @@
|
||||
<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 %>
|
||||
<% if IncludeFormTag %>
|
||||
<form $FormAttributes data-layout="{type: 'border'}">
|
||||
<% end_if %>
|
||||
|
||||
<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 class="cms-content-search">...</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cms-content-form center">
|
||||
<div class="cms-content-form center">
|
||||
|
||||
<% if IncludeFormTag %>
|
||||
<form $FormAttributes>
|
||||
<% end_if %>
|
||||
<!-- <div class="cms-content-tools west">
|
||||
$Left
|
||||
</div> -->
|
||||
|
||||
<!-- <div class="cms-content-tools west">
|
||||
$Left
|
||||
</div> -->
|
||||
|
||||
<div class="center">
|
||||
|
||||
<% if Message %>
|
||||
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
|
||||
<% else %>
|
||||
@ -55,8 +53,6 @@
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
||||
<% if IncludeFormTag %>
|
||||
</form>
|
||||
<% end_if %>
|
||||
|
||||
</div>
|
||||
<% if IncludeFormTag %>
|
||||
</form>
|
||||
<% end_if %>
|
Loading…
Reference in New Issue
Block a user