From 00e8d08dba6a86616ea028bd9216d3da62cfbcda Mon Sep 17 00:00:00 2001 From: Andrew Robert Nicols Date: Mon, 23 Jan 2012 11:36:53 +0000 Subject: [PATCH] MDL-31315 Ask before moving away from a modified form --- admin/settings.php | 10 ++++-- lang/en/moodle.php | 1 + lib/form/filemanager.js | 5 +++ lib/formslib.php | 9 ++++- lib/javascript-static.js | 68 ++++++++++++++++++++++++++++++++++++ lib/yui/formslib/formslib.js | 59 +++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 lib/yui/formslib/formslib.js diff --git a/admin/settings.php b/admin/settings.php index 3344f354b87..3f8d2c93002 100644 --- a/admin/settings.php +++ b/admin/settings.php @@ -131,6 +131,12 @@ if (empty($SITE->fullname)) { echo ''; } +$PAGE->requires->yui_module('moodle-core-formslib', + 'M.core.init_formslib', + array(array( + 'formid' => 'adminsettings' + )) +); +$PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle'); + echo $OUTPUT->footer(); - - diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 0286c524317..d1624e3b34b 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -219,6 +219,7 @@ $string['categoryname'] = 'Category name'; $string['idnumbercoursecategory'] = 'Category ID number'; $string['idnumbercoursecategory_help'] = 'The ID number of a course category is only used when matching the category against external systems and is not displayed anywhere on the site. If the category has an official code name it may be entered, otherwise the field can be left blank.'; $string['categoryupdated'] = 'The category \'{$a}\' was updated'; +$string['changesmadereallygoaway'] = 'You have made changes. Are you sure you want to navigate away and lose your changes?'; $string['city'] = 'City/town'; $string['clambroken'] = 'Your administrator has enabled virus checking for file uploads but has misconfigured something.
Your file upload was NOT successful. Your administrator has been emailed to notify them so they can fix it.
Maybe try uploading this file later.'; $string['clamdeletedfile'] = 'The file has been deleted'; diff --git a/lib/form/filemanager.js b/lib/form/filemanager.js index 18d38424723..d3d237b0533 100644 --- a/lib/form/filemanager.js +++ b/lib/form/filemanager.js @@ -144,6 +144,7 @@ M.form_filemanager.init = function(Y, options) { this.filecount++; this.check_buttons(); this.refresh(this.currentpath); + M.util.set_form_changed(); }, check_buttons: function() { var button_addfile = Y.one("#btnadd-"+this.client_id); @@ -213,6 +214,7 @@ M.form_filemanager.init = function(Y, options) { scope.mkdir_dialog.hide(); scope.refresh(filepath); Y.one('#fm-newname').set('value', ''); + M.util.set_form_changed(); } }); } @@ -559,6 +561,7 @@ M.form_filemanager.init = function(Y, options) { callback: function(id, obj, args) { scope.filecount--; scope.refresh(obj.filepath); + M.util.set_form_changed(); if (scope.filecount < scope.maxfiles && scope.maxfiles!=-1) { var button_addfile = Y.one("#btnadd-"+scope.client_id); button_addfile.setStyle('display', 'inline'); @@ -606,6 +609,7 @@ M.form_filemanager.init = function(Y, options) { alert(M.str.repository.fileexists); } else { scope.refresh(obj.filepath); + M.util.set_form_changed(); } Y.one('#fm-rename-input').set('value', ''); scope.rename_dialog.hide(); @@ -683,6 +687,7 @@ M.form_filemanager.init = function(Y, options) { } dialog.cancel(); scope.refresh(p); + M.util.set_form_changed(); } }); } diff --git a/lib/formslib.php b/lib/formslib.php index 0818fbe7515..cfe553a73f8 100644 --- a/lib/formslib.php +++ b/lib/formslib.php @@ -2255,6 +2255,7 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{ * @param object $form MoodleQuickForm */ function startForm(&$form){ + global $PAGE; $this->_reqHTML = $form->getReqHTML(); $this->_elementTemplates = str_replace('{req}', $this->_reqHTML, $this->_elementTemplates); $this->_advancedHTML = $form->getAdvancedHTML(); @@ -2267,7 +2268,13 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{ $this->_hiddenHtml .= $form->_pageparams; } - + $PAGE->requires->yui_module('moodle-core-formslib', + 'M.core.init_formslib', + array(array( + 'formid' => $form->getAttribute('id') + )) + ); + $PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle'); } /** diff --git a/lib/javascript-static.js b/lib/javascript-static.js index 07f8cfc6c40..452be340119 100644 --- a/lib/javascript-static.js +++ b/lib/javascript-static.js @@ -1752,4 +1752,72 @@ M.util.load_flowplayer = function() { fileref.onreadystatechange = embed_function; document.getElementsByTagName('head')[0].appendChild(fileref); } +}; + +/** + * Set the form changed state to true + */ +M.util.set_form_changed = function() { + M.cfg.form_changed = 1; +}; + +/** + * Set the form submitted state to true + */ +M.util.set_form_submitted = function() { + M.cfg.form_submitted = 1; } + +/** + * Attempt to determine whether the form has been modified in any way and + * is thus 'dirty' + * + * @return Integer 1 is the form is dirty; 0 if not + */ +M.util.get_form_dirty_state = function() { + // If the form was submitted, then return a non-dirty state + if (M.cfg.form_submitted) { + return 0; + } + + // If any fields have been marked dirty, return a dirty state + if (M.cfg.form_changed) { + return 1; + } + + // Handle TinyMCE editor instances + // We can't add a listener in the initializer as the editors may not have been created by that point + // so we do so here instead + if (typeof tinyMCE != 'undefined') { + for (var editor in tinyMCE.editors) { + if (tinyMCE.editors[editor].isDirty()) { + return 1; + } + } + } + + // If we reached here, then the form hasn't met any of the dirty conditions + return 0; +}; + +/** + * Return a suitable message if changes have been made to a form + */ +M.util.report_form_dirty_state = function(e) { + if (!M.util.get_form_dirty_state()) { + // the form is not dirty, so don't display any message + return; + } + + // This is the error message that we'll show to browsers which support it + var returnValue = M.util.get_string('changesmadereallygoaway', 'moodle'); + + // Most browsers are happy with the returnValue being set on the event + // But some browsers do not consistently pass the event + if (e) { + e.returnValue = returnValue; + } + + // But some require it to be returned instead + return returnValue; +}; diff --git a/lib/yui/formslib/formslib.js b/lib/yui/formslib/formslib.js new file mode 100644 index 00000000000..452b9e3dfcb --- /dev/null +++ b/lib/yui/formslib/formslib.js @@ -0,0 +1,59 @@ +YUI.add('moodle-core-formslib', + function(Y) { + // The CSS selectors we use + var CSS = { + }; + + var FORMSLIBNAME = 'core-formslib'; + + var FORMSLIB = function() { + FORMSLIB.superclass.constructor.apply(this, arguments); + } + + Y.extend(FORMSLIB, Y.Base, { + /** + * Initialize the module + */ + initializer : function(config) { + var formid = 'form#' + this.get('formid'); + + // Add change events to the form elements + Y.all(formid + ' input').on('change', M.util.set_form_changed, this); + Y.all(formid + ' textarea').on('change', M.util.set_form_changed, this); + Y.all(formid + ' select').on('change', M.util.set_form_changed, this); + + // We need any submit buttons on the form to set the submitted flag + Y.one(formid).on('submit', M.util.set_form_submitted, this); + + // YUI doesn't support onbeforeunload properly so we must use the DOM to set the onbeforeunload. As + // a result, the has_changed must stay in the DOM too + window.onbeforeunload = M.util.report_form_dirty_state; + }, + + /** + * Unset the form dirty state and also set the form submitted flag to true + */ + unset_changed : function(e) { + M.util.set_form_changed(); + } + }, + { + NAME : FORMSLIBNAME, + ATTRS : { + formid : { + 'value' : '' + } + } + } + ); + + M.core = M.core || {}; + M.core.init_formslib = function(config) { + return new FORMSLIB(config); + } + + }, + '@VERSION@', { + requires : ['base'] + } +);