From 2edb12ec302fc003a46e8f3ca8596e51fdd3c54f Mon Sep 17 00:00:00 2001 From: David Monllao Date: Wed, 13 Jan 2016 14:20:05 +0800 Subject: [PATCH] MDL-52826 forms: Move global functions to self-contained --- lib/formslib.php | 222 ++++++++++-------- lib/pear/HTML/QuickForm.php | 9 - .../HTML/QuickForm/Renderer/Tableless.php | 2 +- lib/pear/README_MOODLE.txt | 2 + lib/upgrade.txt | 2 + 5 files changed, 130 insertions(+), 107 deletions(-) diff --git a/lib/formslib.php b/lib/formslib.php index 51fc83cc410..27bbae763f7 100644 --- a/lib/formslib.php +++ b/lib/formslib.php @@ -1426,6 +1426,12 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless { */ var $_pageparams = ''; + /** + * Whether the form contains any client-side validation or not. + * @var bool + */ + protected $clientvalidation = false; + /** * Class constructor - same parameters as HTML_QuickForm_DHTMLRulesTableless * @@ -2026,7 +2032,7 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless { { parent::addRule($element, $message, $type, $format, $validation, $reset, $force); if ($validation == 'client') { - $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);')); + $this->clientvalidation = true; } } @@ -2055,16 +2061,14 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless { foreach ($arg1 as $rules) { foreach ($rules as $rule) { $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server'; - - if ('client' == $validation) { - $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);')); + if ($validation == 'client') { + $this->clientvalidation = true; } } } } elseif (is_string($arg1)) { - if ($validation == 'client') { - $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);')); + $this->clientvalidation = true; } } } @@ -2080,7 +2084,7 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless { */ function getValidationScript() { - if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) { + if (empty($this->_rules) || $this->clientvalidation === false) { return ''; } @@ -2164,60 +2168,62 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless { var skipClientValidation = false; -function qf_errorHandler(element, _qfMsg, escapedName) { - div = element.parentNode; +(function() { - if ((div == undefined) || (element.name == undefined)) { - //no checking can be done for undefined elements so let server handle it. - return true; - } + function qf_errorHandler(element, _qfMsg, escapedName) { + div = element.parentNode; - if (_qfMsg != \'\') { - var errorSpan = document.getElementById(\'id_error_\' + escapedName); - if (!errorSpan) { - errorSpan = document.createElement("span"); - errorSpan.id = \'id_error_\' + escapedName; - errorSpan.className = "error"; - element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild); - document.getElementById(errorSpan.id).setAttribute(\'TabIndex\', \'0\'); - document.getElementById(errorSpan.id).focus(); - } + if ((div == undefined) || (element.name == undefined)) { + //no checking can be done for undefined elements so let server handle it. + return true; + } - while (errorSpan.firstChild) { - errorSpan.removeChild(errorSpan.firstChild); - } + if (_qfMsg != \'\') { + var errorSpan = document.getElementById(\'id_error_\' + escapedName); + if (!errorSpan) { + errorSpan = document.createElement("span"); + errorSpan.id = \'id_error_\' + escapedName; + errorSpan.className = "error"; + element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild); + document.getElementById(errorSpan.id).setAttribute(\'TabIndex\', \'0\'); + document.getElementById(errorSpan.id).focus(); + } - errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3))); + while (errorSpan.firstChild) { + errorSpan.removeChild(errorSpan.firstChild); + } - if (div.className.substr(div.className.length - 6, 6) != " error" - && div.className != "error") { - div.className += " error"; - linebreak = document.createElement("br"); - linebreak.className = "error"; - linebreak.id = \'id_error_break_\' + escapedName; - errorSpan.parentNode.insertBefore(linebreak, errorSpan.nextSibling); - } + errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3))); - return false; - } else { - var errorSpan = document.getElementById(\'id_error_\' + escapedName); - if (errorSpan) { - errorSpan.parentNode.removeChild(errorSpan); - } - var linebreak = document.getElementById(\'id_error_break_\' + escapedName); - if (linebreak) { - linebreak.parentNode.removeChild(linebreak); - } + if (div.className.substr(div.className.length - 6, 6) != " error" + && div.className != "error") { + div.className += " error"; + linebreak = document.createElement("br"); + linebreak.className = "error"; + linebreak.id = \'id_error_break_\' + escapedName; + errorSpan.parentNode.insertBefore(linebreak, errorSpan.nextSibling); + } - if (div.className.substr(div.className.length - 6, 6) == " error") { - div.className = div.className.substr(0, div.className.length - 6); - } else if (div.className == "error") { - div.className = ""; - } + return false; + } else { + var errorSpan = document.getElementById(\'id_error_\' + escapedName); + if (errorSpan) { + errorSpan.parentNode.removeChild(errorSpan); + } + var linebreak = document.getElementById(\'id_error_break_\' + escapedName); + if (linebreak) { + linebreak.parentNode.removeChild(linebreak); + } - return true; - } -}'; + if (div.className.substr(div.className.length - 6, 6) == " error") { + div.className = div.className.substr(0, div.className.length - 6); + } else if (div.className == "error") { + div.className = ""; + } + + return true; + } + }'; $validateJS = ''; foreach ($test as $elementName => $jsandelement) { // Fix for bug displaying errors for elements in a group @@ -2228,64 +2234,86 @@ function qf_errorHandler(element, _qfMsg, escapedName) { '/[_\[\]-]/', create_function('$matches', 'return sprintf("_%2x",ord($matches[0]));'), $elementName); + $valFunc = 'validate_' . $this->_formName . '_' . $escapedElementName . '(ev.target, \''.$escapedElementName.'\')'; + $js .= ' -function validate_' . $this->_formName . '_' . $escapedElementName . '(element, escapedName) { - if (undefined == element) { - //required element was not found, then let form be submitted without client side validation - return true; - } - var value = \'\'; - var errFlag = new Array(); - var _qfGroups = {}; - var _qfMsg = \'\'; - var frm = element.parentNode; - if ((undefined != element.name) && (frm != undefined)) { - while (frm && frm.nodeName.toUpperCase() != "FORM") { - frm = frm.parentNode; + function validate_' . $this->_formName . '_' . $escapedElementName . '(element, escapedName) { + if (undefined == element) { + //required element was not found, then let form be submitted without client side validation + return true; } - ' . join("\n", $jsArr) . ' - return qf_errorHandler(element, _qfMsg, escapedName); - } else { - //element name should be defined else error msg will not be displayed. - return true; - } -} + var value = \'\'; + var errFlag = new Array(); + var _qfGroups = {}; + var _qfMsg = \'\'; + var frm = element.parentNode; + if ((undefined != element.name) && (frm != undefined)) { + while (frm && frm.nodeName.toUpperCase() != "FORM") { + frm = frm.parentNode; + } + ' . join("\n", $jsArr) . ' + return qf_errorHandler(element, _qfMsg, escapedName); + } else { + //element name should be defined else error msg will not be displayed. + return true; + } + } + + document.getElementById(\'id_' . $elementName . '\').addEventListener(\'blur\', function(ev) { + ' . $valFunc . ' + }); + document.getElementById(\'id_' . $elementName . '\').addEventListener(\'change\', function(ev) { + ' . $valFunc . ' + }); '; $validateJS .= ' - ret = validate_' . $this->_formName . '_' . $escapedElementName.'(frm.elements[\''.$elementName.'\'], \''.$escapedElementName.'\') && ret; - if (!ret && !first_focus) { - first_focus = true; - Y.use(\'moodle-core-event\', function() { - Y.Global.fire(M.core.globalEvents.FORM_ERROR, {formid: \'' . $this->_attributes['id'] . '\', - elementid: \'id_error_' . $escapedElementName . '\'}); - document.getElementById(\'id_error_' . $escapedElementName . '\').focus(); - }); - } + ret = validate_' . $this->_formName . '_' . $escapedElementName.'(frm.elements[\''.$elementName.'\'], \''.$escapedElementName.'\') && ret; + if (!ret && !first_focus) { + first_focus = true; + Y.use(\'moodle-core-event\', function() { + Y.Global.fire(M.core.globalEvents.FORM_ERROR, {formid: \'' . $this->_attributes['id'] . '\', + elementid: \'id_error_' . $escapedElementName . '\'}); + document.getElementById(\'id_error_' . $escapedElementName . '\').focus(); + }); + } '; // Fix for bug displaying errors for elements in a group //unset($element); //$element =& $this->getElement($elementName); //end of fix - $valFunc = 'validate_' . $this->_formName . '_' . $escapedElementName . '(this, \''.$escapedElementName.'\')'; - $onBlur = $element->getAttribute('onBlur'); - $onChange = $element->getAttribute('onChange'); - $element->updateAttributes(array('onBlur' => $onBlur . $valFunc, - 'onChange' => $onChange . $valFunc)); + //$onBlur = $element->getAttribute('onBlur'); + //$onChange = $element->getAttribute('onChange'); + //$element->updateAttributes(array('onBlur' => $onBlur . $valFunc, + //'onChange' => $onChange . $valFunc)); } // do not rely on frm function parameter, because htmlarea breaks it when overloading the onsubmit method $js .= ' -function validate_' . $this->_formName . '(frm) { - if (skipClientValidation) { - return true; - } - var ret = true; - var frm = document.getElementById(\''. $this->_attributes['id'] .'\') - var first_focus = false; -' . $validateJS . '; - return ret; -} + function validate_' . $this->_formName . '() { + if (skipClientValidation) { + return true; + } + var ret = true; + + var frm = document.getElementById(\''. $this->_attributes['id'] .'\') + var first_focus = false; + ' . $validateJS . '; + return ret; + } + + + document.getElementById(\'' . $this->_attributes['id'] . '\').addEventListener(\'submit\', function(ev) { + try { + var myValidator = validate_' . $this->_formName . '; + } catch(e) { + return true; + } + if (!myValidator()) { + ev.preventDefault(); + } + }); +})(); //]]> '; return $js; diff --git a/lib/pear/HTML/QuickForm.php b/lib/pear/HTML/QuickForm.php index bf8062ef4c7..c53ab751181 100644 --- a/lib/pear/HTML/QuickForm.php +++ b/lib/pear/HTML/QuickForm.php @@ -1061,9 +1061,6 @@ class HTML_QuickForm extends HTML_Common { if (!isset($this->_rules[$element])) { $this->_rules[$element] = array(); } - if ($validation == 'client') { - $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);')); - } $this->_rules[$element][] = array( 'type' => $type, 'format' => $format, @@ -1132,9 +1129,6 @@ class HTML_QuickForm extends HTML_Common { $this->_required[] = $elementName; $required++; } - if ('client' == $validation) { - $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);')); - } } } if ($required > 0 && count($groupObj->getElements()) == $required) { @@ -1166,9 +1160,6 @@ class HTML_QuickForm extends HTML_Common { if ($type == 'required') { $this->_required[] = $group; } - if ($validation == 'client') { - $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);')); - } } } // end func addGroupRule diff --git a/lib/pear/HTML/QuickForm/Renderer/Tableless.php b/lib/pear/HTML/QuickForm/Renderer/Tableless.php index 4ef5a337e7e..5b444ac0bad 100644 --- a/lib/pear/HTML/QuickForm/Renderer/Tableless.php +++ b/lib/pear/HTML/QuickForm/Renderer/Tableless.php @@ -267,7 +267,7 @@ class HTML_QuickForm_Renderer_Tableless extends HTML_QuickForm_Renderer_Default $this->_html = str_replace('>', '> ', $this->_html); // add a validation script if ('' != ($script = $form->getValidationScript())) { - $this->_html = $script . "\n" . $this->_html; + $this->_html = $this->_html . "\n" . $script; } } // end func finishForm diff --git a/lib/pear/README_MOODLE.txt b/lib/pear/README_MOODLE.txt index a4274404f5f..02eb5b7e1ce 100644 --- a/lib/pear/README_MOODLE.txt +++ b/lib/pear/README_MOODLE.txt @@ -28,6 +28,8 @@ MDL-20876 - replaced split() with explode() or preg_split() where appropriate MDL-40267 - Moodle core_text strlen functions used for range rule rule to be utf8 safe. MDL-46467 - $mform->hardfreeze causes labels to loose their for HTML attribute MDL-52081 - made all constructors PHP7 compatible +MDL-52826 - Remove onsubmit events pointing to the global validation functions and script + tag moved after the HTML Pear diff --git a/lib/upgrade.txt b/lib/upgrade.txt index c4e9f6f81e0..255b7647e41 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -37,6 +37,8 @@ information provided here is intended especially for developers. * The previous events API handlers are being deprecated in favour of events 2 API, debugging messages are being displayed if there are 3rd party plugins using it. Switch to events 2 API please, see https://docs.moodle.org/dev/Event_2#Event_dispatching_and_observers Note than you will need to bump the plugin version so moodle is aware that you removed the plugin's event handlers. +* mforms validation functions are not available in the global JS namespace anymore, event listeners + are assigned to fields and buttons through a self-contained JS function. === 3.0 ===