mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 04:30:15 +01:00
MDL-53048 core_form: passwordunmask must exist on page for validation
The initial approach I took was to provide a noscript area containing the standard input element, and to move it to a hidden input element as part of the passwordunmask setup. This allowed behat tests to pass both with and without JS enabled (i.e. with Goutte and real browsers), and also ensured that the real input did not appear on screen. However, the standard formslib validation occurs before other page JS is run and hooks into the elements on page during its setup. Since noscript elements are not accessible to scripts as standard elements, the form validation would fail for _client_ side validation and work for server-side validation. This change creates creates the hidden input element in the template instead, as well as the password element in the noscript tag. Doing this means that when JS is disabled, the form has two elements of the same name - one hidden, and the second a visible password field. Since the latter element will always override the former one on form submission, and the noscript variant is last, the noscript variant wins when JS is disabled and tests continue to pass. When JS is enabled, the noscript variant is not a valid part of the DOM (the browser sees it as a hidden text field essentially). The password is not automatically entered into the hidden field, so the autocomplete prevention continues to work as expected.
This commit is contained in:
parent
f34d743dd4
commit
3778a9226f
2
lib/form/amd/build/passwordunmask.min.js
vendored
2
lib/form/amd/build/passwordunmask.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/templates"],function(a,b){var c=function(b){this.wrapperSelector='[data-passwordunmask="wrapper"][data-passwordunmaskid="'+b+'"]',this.wrapper=a(this.wrapperSelector),this.editorSpace=this.wrapper.find('[data-passwordunmask="editor"]'),this.editLink=this.wrapper.find('a[data-passwordunmask="edit"]'),this.editInstructions=this.wrapper.find('[data-passwordunmask="instructions"]'),this.displayValue=this.wrapper.find('[data-passwordunmask="displayvalue"]');var c=a(this.wrapper.find("noscript").text());this.inputField=c.filter("input"),this.inputField.attr("type","hidden"),this.editorSpace.append(this.inputField),this.wrapper.find("noscript").remove(),this.editInstructions.attr("id")||this.editInstructions.attr("id",b+"_instructions"),this.editInstructions.hide(),this.setDisplayValue(),this.addListeners()};return c.prototype.addListeners=function(){return this.wrapper.on("click keypress",'[data-passwordunmask="edit"]',a.proxy(function(b){"keypress"===b.type&&13!==b.keyCode||(b.stopImmediatePropagation(),b.preventDefault(),"hidden"!==this.inputField.attr("type")?"click"===b.type||a(b.relatedTarget).is(":input")?this.turnEditingOff(!1):this.turnEditingOff(!0):this.turnEditingOn())},this)),this.wrapper.on("click keypress",'[data-passwordunmask="unmask"]',a.proxy(function(a){"keypress"===a.type&&13!==a.keyCode||(a.stopImmediatePropagation(),a.preventDefault(),this.wrapper.data("unmasked",!this.wrapper.data("unmasked")),this.setDisplayValue())},this)),this.wrapper.on("keydown","input",a.proxy(function(a){"keydown"===a.type&&13!==a.keyCode||(a.stopImmediatePropagation(),a.preventDefault(),this.turnEditingOff(!0))},this)),this},c.prototype.checkFocusOut=function(b){this.isEditing()&&window.setTimeout(a.proxy(function(){var c=b.relatedTarget||document.activeElement;this.wrapper.has(a(c)).length||this.turnEditingOff(!a(c).is(":input,a"))},this),100)},c.prototype.passwordVisible=function(){return!!this.wrapper.data("unmasked")},c.prototype.isEditing=function(){return"hidden"!==this.inputField.attr("type")},c.prototype.turnEditingOn=function(){return this.passwordVisible()?this.inputField.attr("type","text"):this.inputField.attr("type","password"),this.editInstructions.length&&(this.inputField.attr("aria-describedby",this.editInstructions.attr("id")),this.editInstructions.show()),this.wrapper.attr("data-passwordunmask-visible",1),this.editLink.hide(),this.inputField.focus().select(),a("body").on("focusout",this.wrapperSelector,a.proxy(this.checkFocusOut,this)),this},c.prototype.turnEditingOff=function(b){return a("body").off("focusout",this.wrapperSelector,this.checkFocusOut),this.inputField.attr("type","hidden").attr("aria-describedby",null),this.editInstructions.hide(),this.wrapper.removeAttr("data-passwordunmask-visible"),this.editLink.show(),this.setDisplayValue(),b&&this.editLink.focus(),this},c.prototype.getDisplayValue=function(){return this.inputField.val()},c.prototype.setDisplayValue=function(){this.isEditing()&&(this.wrapper.data("unmasked")?this.inputField.attr("type","text"):this.inputField.attr("type","password"));var c=this.getDisplayValue();return c&&this.wrapper.data("unmasked")?this.displayValue.text(c):(c||(c=""),b.render("core_form/element-passwordunmask-fill",{element:{frozen:this.inputField.is("[readonly]"),value:c,valuechars:c.split("")}}).done(a.proxy(function(a,c){this.displayValue.html(a),b.runTemplateJS(c)},this))),this},c});
|
||||
define(["jquery","core/templates"],function(a,b){var c=function(b){this.wrapperSelector='[data-passwordunmask="wrapper"][data-passwordunmaskid="'+b+'"]',this.wrapper=a(this.wrapperSelector),this.editorSpace=this.wrapper.find('[data-passwordunmask="editor"]'),this.editLink=this.wrapper.find('a[data-passwordunmask="edit"]'),this.editInstructions=this.wrapper.find('[data-passwordunmask="instructions"]'),this.displayValue=this.wrapper.find('[data-passwordunmask="displayvalue"]'),this.inputField=this.editorSpace.find(document.getElementById(b)),this.inputField.attr("type","hidden"),this.inputField.removeClass("hiddenifjs"),this.editInstructions.attr("id")||this.editInstructions.attr("id",b+"_instructions"),this.editInstructions.hide(),this.setDisplayValue(),this.addListeners()};return c.prototype.addListeners=function(){return this.wrapper.on("click keypress",'[data-passwordunmask="edit"]',a.proxy(function(b){"keypress"===b.type&&13!==b.keyCode||(b.stopImmediatePropagation(),b.preventDefault(),"hidden"!==this.inputField.attr("type")?"click"===b.type||a(b.relatedTarget).is(":input")?this.turnEditingOff(!1):this.turnEditingOff(!0):this.turnEditingOn())},this)),this.wrapper.on("click keypress",'[data-passwordunmask="unmask"]',a.proxy(function(a){"keypress"===a.type&&13!==a.keyCode||(a.stopImmediatePropagation(),a.preventDefault(),this.wrapper.data("unmasked",!this.wrapper.data("unmasked")),this.setDisplayValue())},this)),this.wrapper.on("keydown","input",a.proxy(function(a){"keydown"===a.type&&13!==a.keyCode||(a.stopImmediatePropagation(),a.preventDefault(),this.turnEditingOff(!0))},this)),this},c.prototype.checkFocusOut=function(b){this.isEditing()&&window.setTimeout(a.proxy(function(){var c=b.relatedTarget||document.activeElement;this.wrapper.has(a(c)).length||this.turnEditingOff(!a(c).is(":input,a"))},this),100)},c.prototype.passwordVisible=function(){return!!this.wrapper.data("unmasked")},c.prototype.isEditing=function(){return"hidden"!==this.inputField.attr("type")},c.prototype.turnEditingOn=function(){return this.passwordVisible()?this.inputField.attr("type","text"):this.inputField.attr("type","password"),this.editInstructions.length&&(this.inputField.attr("aria-describedby",this.editInstructions.attr("id")),this.editInstructions.show()),this.wrapper.attr("data-passwordunmask-visible",1),this.editLink.hide(),this.inputField.focus().select(),a("body").on("focusout",this.wrapperSelector,a.proxy(this.checkFocusOut,this)),this},c.prototype.turnEditingOff=function(b){return a("body").off("focusout",this.wrapperSelector,this.checkFocusOut),this.inputField.attr("type","hidden").attr("aria-describedby",null),this.editInstructions.hide(),this.wrapper.removeAttr("data-passwordunmask-visible"),this.editLink.show(),this.setDisplayValue(),b&&this.editLink.focus(),this},c.prototype.getDisplayValue=function(){return this.inputField.val()},c.prototype.setDisplayValue=function(){this.isEditing()&&(this.wrapper.data("unmasked")?this.inputField.attr("type","text"):this.inputField.attr("type","password"));var c=this.getDisplayValue();return c&&this.wrapper.data("unmasked")?this.displayValue.text(c):(c||(c=""),b.render("core_form/element-passwordunmask-fill",{element:{frozen:this.inputField.is("[readonly]"),value:c,valuechars:c.split("")}}).done(a.proxy(function(a,c){this.displayValue.html(a),b.runTemplateJS(c)},this))),this},c});
|
@ -39,13 +39,9 @@ define(['jquery', 'core/templates'], function($, Template) {
|
||||
this.editInstructions = this.wrapper.find('[data-passwordunmask="instructions"]');
|
||||
this.displayValue = this.wrapper.find('[data-passwordunmask="displayvalue"]');
|
||||
|
||||
// Move and convert the input field to the editor, then remove the noscript.
|
||||
// We only want a single input field.
|
||||
var noscriptContent = $(this.wrapper.find('noscript').text());
|
||||
this.inputField = noscriptContent.filter('input');
|
||||
this.inputField = this.editorSpace.find(document.getElementById(elementid));
|
||||
this.inputField.attr('type', 'hidden');
|
||||
this.editorSpace.append(this.inputField);
|
||||
this.wrapper.find('noscript').remove();
|
||||
this.inputField.removeClass('hiddenifjs');
|
||||
|
||||
if (!this.editInstructions.attr('id')) {
|
||||
this.editInstructions.attr('id', elementid + '_instructions');
|
||||
|
@ -48,6 +48,39 @@
|
||||
{{< core_form/element-template }}
|
||||
{{$ element }}
|
||||
<div data-passwordunmask="wrapper" data-passwordunmaskid="{{ element.id }}">
|
||||
<span class="visibleifjs">
|
||||
<span data-passwordunmask="editor">
|
||||
<!-- The input in the noscript will be moved here as part of the page load -->
|
||||
<span data-passwordunmask="instructions" class="editinstructions">
|
||||
{{^ element.frozen }}
|
||||
{{# str }} passwordunmaskinstructions, form {{/ str }}
|
||||
{{/ element.frozen }}
|
||||
</span>
|
||||
<input type="hidden"
|
||||
{{# element.frozen }}readonly{{/ element.frozen }}
|
||||
{{^ element.hardfrozen}} name="{{ element.name }}"{{/ element.hardfrozen }}
|
||||
id="{{ element.id }}"
|
||||
value="{{element.value }}"
|
||||
size="{{ element.size }}"
|
||||
{{# error }}
|
||||
autofocus aria-describedby="id_error_{{ element.name }}"
|
||||
{{/ error }}
|
||||
{{{ attributes }}}
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
{{^ element.frozen }}
|
||||
<a href="#" data-passwordunmask="edit" title="{{ edithint }}">
|
||||
{{/ element.frozen }}
|
||||
<span data-passwordunmask="displayvalue">{{> core_form/element-passwordunmask-fill }}</span>
|
||||
{{^ element.frozen }}
|
||||
{{# pix }} t/passwordunmask-edit, core, {{ edithint }}{{/ pix }}
|
||||
</a>
|
||||
{{/ element.frozen }}
|
||||
<a href="#" data-passwordunmask="unmask" title="{{ unmaskhint }}">
|
||||
{{# pix }} t/passwordunmask-reveal, core, {{ edithint }}{{/ pix }}
|
||||
</a>
|
||||
</span>
|
||||
<noscript>
|
||||
<!-- Backwards compatability for Behat -->
|
||||
<input type="password"
|
||||
@ -62,27 +95,6 @@
|
||||
{{{ attributes }}}
|
||||
>
|
||||
</noscript>
|
||||
<span class="visibleifjs">
|
||||
<span data-passwordunmask="editor">
|
||||
<!-- The input in the noscript will be moved here as part of the page load -->
|
||||
<span data-passwordunmask="instructions" class="editinstructions">
|
||||
{{^ element.frozen }}
|
||||
{{# str }} passwordunmaskinstructions, form {{/ str }}
|
||||
{{/ element.frozen }}
|
||||
</span>
|
||||
</span>
|
||||
{{^ element.frozen }}
|
||||
<a href="#" data-passwordunmask="edit" title="{{ edithint }}">
|
||||
{{/ element.frozen }}
|
||||
<span data-passwordunmask="displayvalue">{{> core_form/element-passwordunmask-fill }}</span>
|
||||
{{^ element.frozen }}
|
||||
{{# pix }} t/passwordunmask-edit, core, {{ edithint }}{{/ pix }}
|
||||
</a>
|
||||
{{/ element.frozen }}
|
||||
<a href="#" data-passwordunmask="unmask" title="{{ unmaskhint }}">
|
||||
{{# pix }} t/passwordunmask-reveal, core, {{ edithint }}{{/ pix }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
{{/ element }}
|
||||
{{/ core_form/element-template }}
|
||||
|
@ -48,24 +48,20 @@
|
||||
{{< core_form/element-template }}
|
||||
{{$ element }}
|
||||
<span data-passwordunmask="wrapper" data-passwordunmaskid="{{ element.id }}">
|
||||
<noscript>
|
||||
<!-- Backwards compatability for Behat -->
|
||||
<input type="password"
|
||||
{{# element.frozen }}readonly{{/ element.frozen }}
|
||||
{{^ element.hardfrozen}} name="{{ element.name }}"{{/ element.hardfrozen }}
|
||||
id="{{ element.id }}"
|
||||
value="{{ element.value }}"
|
||||
class="form-control d-inline-block {{# error }}form-control-danger{{/ error }}"
|
||||
size="{{ element.size }}"
|
||||
{{# error }}
|
||||
autofocus aria-describedby="id_error_{{ element.name }}"
|
||||
{{/ error }}
|
||||
{{{ attributes }}}
|
||||
>
|
||||
</noscript>
|
||||
<span class="visibleifjs">
|
||||
<span data-passwordunmask="editor">
|
||||
<!-- The input in the noscript will be moved here as part of the page load -->
|
||||
<input type="hidden"
|
||||
{{# element.frozen }}readonly{{/ element.frozen }}
|
||||
{{^ element.hardfrozen}} name="{{ element.name }}"{{/ element.hardfrozen }}
|
||||
id="{{ element.id }}"
|
||||
value="{{ element.value }}"
|
||||
class="form-control d-inline-block {{# error }}form-control-danger{{/ error }}"
|
||||
size="{{ element.size }}"
|
||||
{{# error }}
|
||||
autofocus aria-describedby="id_error_{{ element.name }}"
|
||||
{{/ error }}
|
||||
{{{ attributes }}}
|
||||
>
|
||||
</span>
|
||||
{{^ element.frozen }}
|
||||
<a href="#" data-passwordunmask="edit" title="{{ edithint }}">
|
||||
@ -84,6 +80,21 @@
|
||||
{{/ element.frozen }}
|
||||
</span>
|
||||
</span>
|
||||
<noscript>
|
||||
<!-- Backwards compatability for Behat -->
|
||||
<input type="password"
|
||||
{{# element.frozen }}readonly{{/ element.frozen }}
|
||||
{{^ element.hardfrozen}} name="{{ element.name }}"{{/ element.hardfrozen }}
|
||||
id="{{ element.id }}"
|
||||
value="{{ element.value }}"
|
||||
class="form-control d-inline-block {{# error }}form-control-danger{{/ error }}"
|
||||
size="{{ element.size }}"
|
||||
{{# error }}
|
||||
autofocus aria-describedby="id_error_{{ element.name }}"
|
||||
{{/ error }}
|
||||
{{{ attributes }}}
|
||||
>
|
||||
</noscript>
|
||||
</span>
|
||||
{{/ element }}
|
||||
{{/ core_form/element-template }}
|
||||
|
Loading…
x
Reference in New Issue
Block a user