From 650d44dd5783e093e43595e8278481cdb447d945 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Sat, 21 Nov 2009 03:17:41 +0000 Subject: [PATCH] ENHANCEMENT Reimplemented ModelAdmin history feature as separate javascript file based on jQuery.concrete. It removes any dependencies to PHP code or existing markup, which means it can be disabled if not required. git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92769 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- code/ModelAdmin.php | 1 + css/ModelAdmin.css | 17 ++++ javascript/ModelAdmin.History.js | 156 +++++++++++++++++++++++++++++++ javascript/ModelAdmin.js | 7 +- 4 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 javascript/ModelAdmin.History.js diff --git a/code/ModelAdmin.php b/code/ModelAdmin.php index bb383006..1a327813 100755 --- a/code/ModelAdmin.php +++ b/code/ModelAdmin.php @@ -149,6 +149,7 @@ abstract class ModelAdmin extends LeftAndMain { Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/ui.tabs.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery/jquery_improvements.js'); Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.js'); + Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.History.js'); } /** diff --git a/css/ModelAdmin.css b/css/ModelAdmin.css index a6b20fa5..6022043a 100644 --- a/css/ModelAdmin.css +++ b/css/ModelAdmin.css @@ -146,3 +146,20 @@ form .message { margin: .3em 0; padding: 0.3em; } + +body.ModelAdmin .historyNav { + background: #aaa; + padding: 0 10px; + overflow: auto; +} + body.ModelAdmin .historyNav a { + display: block; + color: white; + margin: 3px 0; + } + body.ModelAdmin .historyNav a.back { + float: left; + } + body.ModelAdmin .historyNav a.forward { + float: right; + } \ No newline at end of file diff --git a/javascript/ModelAdmin.History.js b/javascript/ModelAdmin.History.js new file mode 100644 index 00000000..a8a1b93d --- /dev/null +++ b/javascript/ModelAdmin.History.js @@ -0,0 +1,156 @@ +(function($) { + + /** + * A simple ajax browser history implementation tailored towards + * navigating through search results and different forms loaded into + * the ModelAdmin right panels. The logic listens to search and form loading + * events, keeps track of the loaded URLs, and will display graphical back/forward + * buttons where appropriate. A search action will cause the history to be reset. + * + * Note: The logic does not replay save operations or hook into any form actions. + * + * Available Events: + * - historyAdd + * - historyStart + * - historyGoFoward + * - historyGoBack + * + * @todo Switch tab state when re-displaying search forms + * @todo Reload search parameters into forms + * + * @name ss.ModelAdmin + */ + $('.ModelAdmin').concrete('ss', function($){ + return/** @lends ss.ModelAdmin */ { + + History: [], + + Future: [], + + onmatch: function() { + var self = this; + + this._super(); + + // generate markup + this.find('#right').prepend( + '
' + + '< ' + ss.i18n._t('ModelAdmin.HISTORYBACK', 'back') + '' + + '' + ss.i18n._t('ModelAdmin.HISTORYFORWARD', 'forward') + ' >' + + '
' + ).find('.back,.forward').hide(); + + this.find('.historyNav .back').live('click', function() { + self.goBack(); + return false; + }); + + this.find('.historyNav .forward').live('click', function() { + self.goForward(); + return false; + }); + }, + + redraw: function() { + this.find('.historyNav .forward').toggle(Boolean(this.Future().length > 0)); + this.find('.historyNav .back').toggle(Boolean(this.History().length > 1)); + }, + + startHistory: function(url, data) { + this.trigger('historyStart', {url: url, data: data}); + + this.setHistory([]); + this.addHistory(url, data); + }, + + /** + * Add an item to the history, to be accessed by goBack and goForward + */ + addHistory: function(url, data) { + this.trigger('historyAdd', {url: url, data: data}); + + // Combine data into URL + if(data) { + if(url.indexOf('?') == -1) url += '?' + $.param(data); + else url += '&' + $.param(data); + } + + // Add to history + this.History().push(url); + + // Reset future + this.setFuture([]); + + this.redraw(); + }, + + goBack: function() { + if(this.History() && this.History().length) { + if(this.Future() == null) this.setFuture([]); + + var currentPage = this.History().pop(); + var previousPage = this.History()[this.History().length-1]; + + this.Future().push(currentPage); + + this.trigger('historyGoBack', {url:previousPage}); + + // load new location + $('#Form_EditForm').concrete('ss').loadForm(previousPage); + + this.redraw(); + } + }, + + goForward: function() { + if(this.Future() && this.Future().length) { + if(this.Future() == null) this.setFuture([]); + + var nextPage = this.Future().pop(); + + this.History().push(nextPage); + + this.trigger('historyGoForward', {url:nextPage}); + + // load new location + $('#Form_EditForm').concrete('ss').loadForm(nextPage); + + this.redraw(); + } + } + }; + }); + + /** + * A search action will cause the history to be reset. + */ + $('#SearchForm_holder form').concrete('ss', function($) { + return{ + onmatch: function() { + var self = this; + this.bind('beforeSubmit', function(e) { + $('.ModelAdmin').concrete('ss').startHistory( + self.attr('action'), + self.serializeArray() + ); + }); + } + }; + }); + + /** + * We have to apply this to the result table buttons instead of the + * more generic form loading. + */ + $('form[name=Form_ResultsForm] tbody td a').concrete('ss', function($) { + return{ + onmatch: function() { + var self = this; + this.bind('click', function(e) { + $('.ModelAdmin').addHistory(self.attr('href')); + }); + } + }; + }); + +})(jQuery); \ No newline at end of file diff --git a/javascript/ModelAdmin.js b/javascript/ModelAdmin.js index 5635ce57..6583bfbd 100644 --- a/javascript/ModelAdmin.js +++ b/javascript/ModelAdmin.js @@ -9,7 +9,7 @@ * @todo alias the $ function instead of literal jQuery */ (function($) { - + ////////////////////////////////////////////////////////////////// // Search form ////////////////////////////////////////////////////////////////// @@ -43,9 +43,12 @@ $('#SearchForm_holder form').concrete({ onmatch: function() { var self = this; + this.bind('submit', function(e) { // Import forms are processed without ajax if(self.is('#Form_ImportForm')) return true; + + self.trigger('beforeSubmit'); $('#Form_EditForm').concrete('ss').loadForm( self.attr('action'), @@ -129,7 +132,7 @@ this.bind('click', function() { var confirmed = confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?')); if(!confirmed) { - $(this).removeClass('loading') + $(this).removeClass('loading'); return false; } });