From ff54044206d5fb9147d84fbf7b9f2f6e269cbf39 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Wed, 8 Jun 2011 11:35:56 +1200 Subject: [PATCH] API CHANGE Forms in ajax responses from LeftAndMain and subclasses include
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. --- admin/code/LeftAndMain.php | 19 +- admin/code/ModelAdmin.php | 18 +- admin/css/layout.css | 4 +- admin/css/screen.css | 2 +- admin/javascript/LeftAndMain.AddForm.js | 5 +- admin/javascript/LeftAndMain.Content.js | 218 +++++++++++++- admin/javascript/LeftAndMain.EditForm.js | 272 +++--------------- admin/javascript/LeftAndMain.js | 2 + admin/javascript/ModelAdmin.js | 9 +- admin/scss/_style.scss | 3 +- .../templates/Includes/LeftAndMain_Content.ss | 4 +- .../Includes/LeftAndMain_EditForm.ss | 58 ++-- admin/templates/Includes/ModelAdmin.ss | 0 13 files changed, 308 insertions(+), 306 deletions(-) delete mode 100755 admin/templates/Includes/ModelAdmin.ss diff --git a/admin/code/LeftAndMain.php b/admin/code/LeftAndMain.php index 7bd961fbc..d3548e44c 100755 --- a/admin/code/LeftAndMain.php +++ b/admin/code/LeftAndMain.php @@ -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)); } diff --git a/admin/code/ModelAdmin.php b/admin/code/ModelAdmin.php index dae73578b..2ac480841 100755 --- a/admin/code/ModelAdmin.php +++ b/admin/code/ModelAdmin.php @@ -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'); } diff --git a/admin/css/layout.css b/admin/css/layout.css index de4a5a1c4..773c6cc08 100755 --- a/admin/css/layout.css +++ b/admin/css/layout.css @@ -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; } diff --git a/admin/css/screen.css b/admin/css/screen.css index 63707e33c..184bd9165 100755 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -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%); } diff --git a/admin/javascript/LeftAndMain.AddForm.js b/admin/javascript/LeftAndMain.AddForm.js index 896f0e47b..8f9176cf8 100755 --- a/admin/javascript/LeftAndMain.AddForm.js +++ b/admin/javascript/LeftAndMain.AddForm.js @@ -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; } diff --git a/admin/javascript/LeftAndMain.Content.js b/admin/javascript/LeftAndMain.Content.js index 529dedd87..896eda0af 100644 --- a/admin/javascript/LeftAndMain.Content.js +++ b/admin/javascript/LeftAndMain.Content.js @@ -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 tag is replaced, + * but the old 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 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

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'); } }); }); diff --git a/admin/javascript/LeftAndMain.EditForm.js b/admin/javascript/LeftAndMain.EditForm.js index de192dfb2..eac77c808 100755 --- a/admin/javascript/LeftAndMain.EditForm.js +++ b/admin/javascript/LeftAndMain.EditForm.js @@ -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(/(]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#'); + // + // // Rewrite iframe links (for IE) + // html = html.replace(/(]*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 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

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 tag is replaced, - * but the old 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(/(]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#'); - - // Rewrite iframe links (for IE) - html = html.replace(/(]*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; } }); diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 9576164d9..794a46bdf 100755 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -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}) } diff --git a/admin/javascript/ModelAdmin.js b/admin/javascript/ModelAdmin.js index 7f78fa66c..dcbad80ab 100755 --- a/admin/javascript/ModelAdmin.js +++ b/admin/javascript/ModelAdmin.js @@ -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; diff --git a/admin/scss/_style.scss b/admin/scss/_style.scss index c36c6411c..1665c48e0 100755 --- a/admin/scss/_style.scss +++ b/admin/scss/_style.scss @@ -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; } diff --git a/admin/templates/Includes/LeftAndMain_Content.ss b/admin/templates/Includes/LeftAndMain_Content.ss index c74f54b0a..40443e046 100755 --- a/admin/templates/Includes/LeftAndMain_Content.ss +++ b/admin/templates/Includes/LeftAndMain_Content.ss @@ -1,3 +1 @@ -

-$EditForm -
\ No newline at end of file +$EditForm \ No newline at end of file diff --git a/admin/templates/Includes/LeftAndMain_EditForm.ss b/admin/templates/Includes/LeftAndMain_EditForm.ss index 93c09d44d..16a96631a 100755 --- a/admin/templates/Includes/LeftAndMain_EditForm.ss +++ b/admin/templates/Includes/LeftAndMain_EditForm.ss @@ -1,34 +1,32 @@ -
-
-

My Page Title

- <% if Fields.hasTabset %> - <% with Fields.fieldByName('Root') %> -
-
    - <% control Tabs %> -
  • $Title
  • - <% end_control %> -
- <% end_with %> -
- <% end_if %> +<% if IncludeFormTag %> + +<% end_if %> + +
+
+

My Page Title

+ <% if Fields.hasTabset %> + <% with Fields.fieldByName('Root') %> +
+
    + <% control Tabs %> +
  • $Title
  • + <% end_control %> +
+ <% end_with %> +
+ <% end_if %> - + +
-
-
+
- <% if IncludeFormTag %> - - <% end_if %> + - - -
- <% if Message %>

$Message

<% else %> @@ -55,8 +53,6 @@ <% end_if %>
- <% if IncludeFormTag %> - - <% end_if %> - -
\ No newline at end of file +<% if IncludeFormTag %> + +<% end_if %> \ No newline at end of file diff --git a/admin/templates/Includes/ModelAdmin.ss b/admin/templates/Includes/ModelAdmin.ss deleted file mode 100755 index e69de29bb..000000000