MDL-53048 form: Rewrite passwordunmask
This version: * should work with the Behat Goutte driver * should not suffer from password autofill anxiety * should allow unmasking (and masking) of a password * should allow editing of passwords in either masked, or unmasked form AMOS BEGIN MOV [revealpassword,core_form],[passwordunmaskrevealhint,core_form] AMOS END
This commit is contained in:
@@ -34,40 +34,35 @@
|
||||
}
|
||||
}}
|
||||
<div class="form-password">
|
||||
<input type="password" name="{{name}}" size="{{size}}" id="{{id}}" value="{{value}}">
|
||||
<div class="unmask" id="{{id}}unmaskdiv"></div>
|
||||
<span data-passwordunmask="wrapper" data-passwordunmaskid="{{ id }}">
|
||||
<noscript>
|
||||
<!-- Backwards compatability for Behat -->
|
||||
<input type="password"
|
||||
name="{{ name }}"
|
||||
id="{{ id }}"
|
||||
value="{{ value }}"
|
||||
size="{{ size }}"
|
||||
>
|
||||
</noscript>
|
||||
<span class="visibleifjs">
|
||||
<span data-passwordunmask="editor">
|
||||
<!-- The input in the noscript will be moved here as part of the page load -->
|
||||
</span>
|
||||
<a href="#" data-passwordunmask="edit" title="{{ edithint }}">
|
||||
<span data-passwordunmask="displayvalue">{{> core_form/element-passwordunmask-fill }}</span>
|
||||
{{# pix }} t/passwordunmask-edit, core, {{# str }} passwordunmaskedithint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
<a href="#" data-passwordunmask="unmask" title="{{ unmaskhint }}">
|
||||
{{# pix }} t/passwordunmask-reveal, core, {{# str }} passwordunmaskrevealhint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
<span data-passwordunmask="instructions" class="form-text text-muted" style="display: none;">
|
||||
{{# str }} passwordunmaskinstructions, form {{/ str }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{{#js}}
|
||||
(function() {
|
||||
var id = '{{id}}';
|
||||
var unmaskid = id + 'unmask';
|
||||
var unmaskdivid = id + 'unmaskdiv';
|
||||
var unmaskstr = {{#quote}}{{#str}}unmaskpassword, form{{/str}}{{/quote}};
|
||||
var is_ie = (navigator.userAgent.toLowerCase().indexOf("msie") != -1);
|
||||
|
||||
document.getElementById(id).setAttribute("autocomplete", "off");
|
||||
|
||||
var unmaskdiv = document.getElementById(unmaskdivid);
|
||||
|
||||
var unmaskchb = document.createElement("input");
|
||||
unmaskchb.setAttribute("type", "checkbox");
|
||||
unmaskchb.setAttribute("id", unmaskid);
|
||||
unmaskchb.onchange = function() {unmaskPassword(id);};
|
||||
unmaskdiv.appendChild(unmaskchb);
|
||||
|
||||
var unmasklbl = document.createElement("label");
|
||||
unmasklbl.innerHTML = unmaskstr;
|
||||
if (is_ie) {
|
||||
unmasklbl.setAttribute("htmlFor", unmaskid);
|
||||
} else {
|
||||
unmasklbl.setAttribute("for", unmaskid);
|
||||
}
|
||||
unmaskdiv.appendChild(unmasklbl);
|
||||
|
||||
if (is_ie) {
|
||||
// Ugly hack to work around the famous onchange IE bug.
|
||||
unmaskchb.onclick = function() {this.blur();};
|
||||
unmaskdiv.onclick = function() {this.blur();};
|
||||
}
|
||||
})()
|
||||
require(['core_form/passwordunmask'], function(PasswordUnmask) {
|
||||
new PasswordUnmask("{{ id }}");
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -34,3 +34,4 @@ downloadoptions,core_table
|
||||
downloadtsv,core_table
|
||||
downloadxhtml,core_table
|
||||
invalidpersistent,core_competency
|
||||
revealpassword,core_form
|
||||
|
||||
+8
-1
@@ -51,10 +51,14 @@ $string['nomethodforaddinghelpbutton'] = 'There is no method for adding a help b
|
||||
$string['nonexistentformelements'] = 'Trying to add help buttons to non-existent form elements : {$a}';
|
||||
$string['noselection'] = 'No selection';
|
||||
$string['nosuggestions'] = 'No suggestions';
|
||||
$string['novalue'] = 'Nothing entered';
|
||||
$string['novalueclicktoset'] = 'Click to enter text';
|
||||
$string['optional'] = 'Optional';
|
||||
$string['othersettings'] = 'Other settings';
|
||||
$string['passwordunmaskedithint'] = 'Edit password';
|
||||
$string['passwordunmaskrevealhint'] = 'Reveal';
|
||||
$string['passwordunmaskinstructions'] = 'Press enter to save changes';
|
||||
$string['requiredelement'] = 'Required field';
|
||||
$string['revealpassword'] = 'Reveal';
|
||||
$string['security'] = 'Security';
|
||||
$string['selectallornone'] = 'Select all/none';
|
||||
$string['selected'] = 'Selected';
|
||||
@@ -68,3 +72,6 @@ $string['timeunit'] = 'Time unit';
|
||||
$string['timing'] = 'Timing';
|
||||
$string['unmaskpassword'] = 'Unmask';
|
||||
$string['year'] = 'Year';
|
||||
|
||||
// Deprecated since 3.2.
|
||||
$string['revealpassword'] = 'Reveal';
|
||||
|
||||
+4
-6
@@ -2406,13 +2406,11 @@ class admin_setting_configpasswordunmask extends admin_setting_configtext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns XHTML for the field
|
||||
* Writes Javascript into the HTML below right before the last div
|
||||
* Returns HTML for the field.
|
||||
*
|
||||
* @todo Make javascript available through newer methods if possible
|
||||
* @param string $data Value for the field
|
||||
* @param string $query Passed as final argument for format_admin_setting
|
||||
* @return string XHTML field
|
||||
* @param string $data Value for the field
|
||||
* @param string $query Passed as final argument for format_admin_setting
|
||||
* @return string Rendered HTML
|
||||
*/
|
||||
public function output_html($data, $query='') {
|
||||
global $OUTPUT;
|
||||
|
||||
@@ -228,6 +228,10 @@ class behat_field_manager {
|
||||
return $type;
|
||||
}
|
||||
|
||||
if (!empty($fieldnode->find('xpath', '/ancestor::*[@data-passwordunmaskid]'))) {
|
||||
return 'passwordunmask';
|
||||
}
|
||||
|
||||
// We look for a parent node with 'felement' class.
|
||||
if ($class = $fieldnode->getParent()->getAttribute('class')) {
|
||||
|
||||
|
||||
@@ -186,6 +186,11 @@ XPATH
|
||||
'filemanager' => <<<XPATH
|
||||
.//*[@data-fieldtype = 'filemanager' or @data-fieldtype = 'filepicker']
|
||||
/descendant::input[@id = //label[contains(normalize-space(string(.)), %locator%)]/@for]
|
||||
XPATH
|
||||
,
|
||||
'passwordunmask' => <<<XPATH
|
||||
.//*[@data-passwordunmask='wrapper']
|
||||
/descendant::input[@id = //label[contains(normalize-space(string(.)), %locator%)]/@for]
|
||||
XPATH
|
||||
],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Silly behat_form_select extension.
|
||||
*
|
||||
* @package core_form
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
||||
|
||||
require_once(__DIR__ . '/behat_form_text.php');
|
||||
|
||||
/**
|
||||
* Allows interaction with passwordunmask form fields.
|
||||
*
|
||||
* Plain behat_form_select extension as it is the same
|
||||
* kind of field.
|
||||
*
|
||||
* @package core_form
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class behat_form_passwordunmask extends behat_form_text {
|
||||
/**
|
||||
* Sets the value to a field.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function set_value($value) {
|
||||
if ($this->running_javascript()) {
|
||||
$id = $this->field->getAttribute('id');
|
||||
$js = <<<JS
|
||||
require(["jquery"], function($) {
|
||||
var wrapper = $(document.getElementById("{$id}")).closest('[data-passwordunmask="wrapper"]');
|
||||
wrapper.find('[data-passwordunmask="edit"]').trigger("click");
|
||||
});
|
||||
JS;
|
||||
$this->session->executeScript($js);
|
||||
}
|
||||
|
||||
$this->field->setValue($value);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +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});
|
||||
@@ -0,0 +1,286 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Password Unmask functionality.
|
||||
*
|
||||
* @module core_form/passwordunmask
|
||||
* @package core_form
|
||||
* @class passwordunmask
|
||||
* @copyright 2016 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.2
|
||||
*/
|
||||
define(['jquery', 'core/templates'], function($, Template) {
|
||||
|
||||
/**
|
||||
* Constructor for PasswordUnmask.
|
||||
*
|
||||
* @param {String} elementid The element to apply the PasswordUnmask to
|
||||
*/
|
||||
var PasswordUnmask = function(elementid) {
|
||||
// Setup variables.
|
||||
this.wrapperSelector = '[data-passwordunmask="wrapper"][data-passwordunmaskid="' + elementid + '"]';
|
||||
this.wrapper = $(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"]');
|
||||
|
||||
// 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.attr('type', 'hidden');
|
||||
this.editorSpace.append(this.inputField);
|
||||
this.wrapper.find('noscript').remove();
|
||||
|
||||
if (!this.editInstructions.attr('id')) {
|
||||
this.editInstructions.attr('id', elementid + '_instructions');
|
||||
}
|
||||
this.editInstructions.hide();
|
||||
|
||||
this.setDisplayValue();
|
||||
|
||||
// Add the listeners.
|
||||
this.addListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the event listeners required for PasswordUnmask.
|
||||
*
|
||||
* @method addListeners
|
||||
* @return {PasswordUnmask}
|
||||
* @chainable
|
||||
*/
|
||||
PasswordUnmask.prototype.addListeners = function() {
|
||||
this.wrapper.on('click keypress', '[data-passwordunmask="edit"]', $.proxy(function(e) {
|
||||
if (e.type === 'keypress' && e.keyCode !== 13) {
|
||||
return;
|
||||
}
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (this.inputField.attr('type') !== 'hidden') {
|
||||
// Only focus on the edit link if the event was not a click, and the new target is not an input field.
|
||||
if (e.type !== 'click' && !$(e.relatedTarget).is(':input')) {
|
||||
this.turnEditingOff(true);
|
||||
} else {
|
||||
this.turnEditingOff(false);
|
||||
}
|
||||
} else {
|
||||
this.turnEditingOn();
|
||||
}
|
||||
}, this));
|
||||
|
||||
this.wrapper.on('click keypress', '[data-passwordunmask="unmask"]', $.proxy(function(e) {
|
||||
if (e.type === 'keypress' && e.keyCode !== 13) {
|
||||
return;
|
||||
}
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Toggle the data attribute.
|
||||
this.wrapper.data('unmasked', !this.wrapper.data('unmasked'));
|
||||
|
||||
this.setDisplayValue();
|
||||
}, this));
|
||||
|
||||
this.wrapper.on('keydown', 'input', $.proxy(function(e) {
|
||||
if (e.type === 'keydown' && e.keyCode !== 13) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
this.turnEditingOff(true);
|
||||
}, this));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether focus was lost from the PasswordUnmask and turn editing off if required.
|
||||
*
|
||||
* @method checkFocusOut
|
||||
* @param {EventFacade} e The EventFacade generating the suspsected Focus Out
|
||||
*/
|
||||
PasswordUnmask.prototype.checkFocusOut = function(e) {
|
||||
if (!this.isEditing()) {
|
||||
// Ignore - not editing.
|
||||
return;
|
||||
}
|
||||
|
||||
window.setTimeout($.proxy(function() {
|
||||
// Firefox does not have the focusout event. Instead jQuery falls back to the 'blur' event.
|
||||
// The blur event does not have a relatedTarget, so instead we use a timeout and the new activeElement.
|
||||
var relatedTarget = e.relatedTarget || document.activeElement;
|
||||
if (this.wrapper.has($(relatedTarget)).length) {
|
||||
// Ignore, some part of the element is still active.
|
||||
return;
|
||||
}
|
||||
|
||||
// Only focus on the edit link if the new related target is not an input field or anchor.
|
||||
this.turnEditingOff(!$(relatedTarget).is(':input,a'));
|
||||
}, this), 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the password is currently visible (unmasked).
|
||||
*
|
||||
* @method passwordVisible
|
||||
* @return {Boolean} True if the password is unmasked
|
||||
*/
|
||||
PasswordUnmask.prototype.passwordVisible = function() {
|
||||
return !!this.wrapper.data('unmasked');
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the user is currently editing the field.
|
||||
*
|
||||
* @method isEditing
|
||||
* @return {Boolean} True if edit mode is enabled
|
||||
*/
|
||||
PasswordUnmask.prototype.isEditing = function() {
|
||||
return this.inputField.attr('type') !== 'hidden';
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the editing functionality.
|
||||
*
|
||||
* @method turnEditingOn
|
||||
* @return {PasswordUnmask}
|
||||
* @chainable
|
||||
*/
|
||||
PasswordUnmask.prototype.turnEditingOn = function() {
|
||||
if (this.passwordVisible()) {
|
||||
this.inputField.attr('type', 'text');
|
||||
} else {
|
||||
this.inputField.attr('type', 'password');
|
||||
}
|
||||
|
||||
if (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();
|
||||
|
||||
// Note, this cannot be added as a delegated listener on init because Firefox does not support the FocusOut
|
||||
// event (https://bugzilla.mozilla.org/show_bug.cgi?id=687787) and the blur event does not identify the
|
||||
// relatedTarget.
|
||||
// The act of focusing the this.inputField means that in Firefox the focusout will be triggered on blur of the edit
|
||||
// link anchor.
|
||||
$('body').on('focusout', this.wrapperSelector, $.proxy(this.checkFocusOut, this));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the editing functionality, optionally focusing on the edit link.
|
||||
*
|
||||
* @method turnEditingOff
|
||||
* @param {Boolean} focusOnEditLink Whether to focus on the edit link after disabling the editor
|
||||
* @return {PasswordUnmask}
|
||||
* @chainable
|
||||
*/
|
||||
PasswordUnmask.prototype.turnEditingOff = function(focusOnEditLink) {
|
||||
$('body').off('focusout', this.wrapperSelector, this.checkFocusOut);
|
||||
this.inputField
|
||||
// Hide the field again.
|
||||
.attr('type', 'hidden')
|
||||
|
||||
// Ensure that the aria-describedby is removed.
|
||||
.attr('aria-describedby', null);
|
||||
|
||||
this.editInstructions.hide();
|
||||
|
||||
// Remove the visible attr.
|
||||
this.wrapper.removeAttr('data-passwordunmask-visible');
|
||||
|
||||
this.editLink.show();
|
||||
this.setDisplayValue();
|
||||
|
||||
if (focusOnEditLink) {
|
||||
this.editLink.focus();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently value.
|
||||
*
|
||||
* @method getDisplayValue
|
||||
* @return {String}
|
||||
*/
|
||||
PasswordUnmask.prototype.getDisplayValue = function() {
|
||||
return this.inputField.val();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the currently value in the display, taking into account the current settings.
|
||||
*
|
||||
* @method setDisplayValue
|
||||
* @return {PasswordUnmask}
|
||||
* @chainable
|
||||
*/
|
||||
PasswordUnmask.prototype.setDisplayValue = function() {
|
||||
if (this.isEditing()) {
|
||||
if (this.wrapper.data('unmasked')) {
|
||||
this.inputField.attr('type', 'text');
|
||||
} else {
|
||||
this.inputField.attr('type', 'password');
|
||||
}
|
||||
}
|
||||
|
||||
// Update the display value.
|
||||
// Note: This must always be updated.
|
||||
// The unmask value can be changed whilst editing and the editing can then be disabled.
|
||||
var value = this.getDisplayValue();
|
||||
if (value && this.wrapper.data('unmasked')) {
|
||||
// There is a value, and we will show it.
|
||||
this.displayValue.text(value);
|
||||
} else {
|
||||
if (!value) {
|
||||
value = "";
|
||||
}
|
||||
// There is a value, but it will be disguised.
|
||||
// We use the passwordunmask-fill to allow modification of the fill and to ensure that the display does not
|
||||
// change as the page loads the JS.
|
||||
Template.render('core_form/element-passwordunmask-fill', {
|
||||
element: {
|
||||
frozen: this.inputField.is('[readonly]'),
|
||||
value: value,
|
||||
valuechars: value.split(''),
|
||||
},
|
||||
}).done($.proxy(function(html, js) {
|
||||
this.displayValue.html(html);
|
||||
|
||||
Template.runTemplateJS(js);
|
||||
}, this));
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
return PasswordUnmask;
|
||||
});
|
||||
@@ -52,7 +52,6 @@ class MoodleQuickForm_passwordunmask extends MoodleQuickForm_password {
|
||||
* or an associative array
|
||||
*/
|
||||
public function __construct($elementName=null, $elementLabel=null, $attributes=null) {
|
||||
global $CFG;
|
||||
// no standard mform in moodle should allow autocomplete of passwords
|
||||
if (empty($attributes)) {
|
||||
$attributes = array('autocomplete'=>'off');
|
||||
@@ -63,6 +62,7 @@ class MoodleQuickForm_passwordunmask extends MoodleQuickForm_password {
|
||||
$attributes .= ' autocomplete="off" ';
|
||||
}
|
||||
}
|
||||
$this->_persistantFreeze = true;
|
||||
|
||||
parent::__construct($elementName, $elementLabel, $attributes);
|
||||
$this->setType('passwordunmask');
|
||||
@@ -79,25 +79,15 @@ class MoodleQuickForm_passwordunmask extends MoodleQuickForm_password {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for password form element.
|
||||
* Function to export the renderer data in a format that is suitable for a mustache template.
|
||||
*
|
||||
* @return string
|
||||
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
||||
* @return stdClass|array
|
||||
*/
|
||||
function toHtml() {
|
||||
global $PAGE;
|
||||
public function export_for_template(renderer_base $output) {
|
||||
$context = parent::export_for_template($output);
|
||||
$context['valuechars'] = array_fill(0, strlen($context['value']), 'x');
|
||||
|
||||
if ($this->_flagFrozen) {
|
||||
return $this->getFrozenHtml();
|
||||
} else {
|
||||
$unmask = get_string('unmaskpassword', 'form');
|
||||
//Pass id of the element, so that unmask checkbox can be attached.
|
||||
$attributes = array('formid' => $this->getAttribute('id'),
|
||||
'checkboxlabel' => $unmask,
|
||||
'checkboxname' => $this->getAttribute('name'));
|
||||
$PAGE->requires->yui_module('moodle-form-passwordunmask', 'M.form.passwordunmask',
|
||||
array($attributes));
|
||||
return $this->_getTabs() . '<input' . $this->_getAttrString($this->_attributes) . ' />';
|
||||
}
|
||||
return $context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ trait templatable_form_element {
|
||||
|
||||
// Special wierd named property.
|
||||
$context['frozen'] = !empty($this->_flagFrozen);
|
||||
$context['hardfrozen'] = !empty($this->_flagFrozen) && empty($this->_persistantFreeze);
|
||||
|
||||
// Other attributes.
|
||||
$otherattributes = [];
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_form/element-passwordunmask-fill
|
||||
|
||||
The fill for a passwordunmask form element.
|
||||
|
||||
The purpose of this template is to render the fill for a passwordunmask element.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* element
|
||||
* valuechars (optional)
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"element": {
|
||||
"valuechars": [
|
||||
"E",
|
||||
"x",
|
||||
"a",
|
||||
"m",
|
||||
"p",
|
||||
"l",
|
||||
"e"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
<span>
|
||||
{{# element.valuechars }}•{{/ element.valuechars }}
|
||||
{{^ element.valuechars }}<em>{{!
|
||||
}}{{^ element.frozen }}{{# str }} novalueclicktoset, form {{/ str }}{{/ element.frozen }}{{!
|
||||
}}{{# element.frozen }}{{# str }} novalue, form {{/ str }}{{/ element.frozen }}{{!
|
||||
}}</em>{{/ element.valuechars }}
|
||||
</span>
|
||||
@@ -0,0 +1,93 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_form/element-passwordunmask
|
||||
|
||||
Moodle passwordunmask form element template.
|
||||
|
||||
The purpose of this template is to render a passwordunmask form element.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* element
|
||||
* id
|
||||
* name
|
||||
* value
|
||||
* size
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"element": {
|
||||
"id": "example_password_unmask",
|
||||
"name": "example",
|
||||
"value": "Password1!",
|
||||
"size": 40
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
{{< core_form/element-template }}
|
||||
{{$ element }}
|
||||
<div 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 }}"
|
||||
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 -->
|
||||
<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 }}
|
||||
{{# js }}
|
||||
require(['core_form/passwordunmask'], function(PasswordUnmask) {
|
||||
new PasswordUnmask("{{ element.id }}");
|
||||
});
|
||||
{{/ js }}
|
||||
@@ -0,0 +1,64 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_form/element-template
|
||||
|
||||
Moodle form element wrapper template.
|
||||
|
||||
The purpose of this template is to wrap a form element.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* label
|
||||
* helpbutton
|
||||
* error
|
||||
* element
|
||||
* id
|
||||
* name
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"label": "Password",
|
||||
"error": "No password set",
|
||||
"element": {
|
||||
"id": "example_password_unmask",
|
||||
"name": "example"
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
<div id="fitem_{{ element.id }}" class="fitem fitem_{{ element.type }}">
|
||||
<div class="fitemtitle">
|
||||
<label for="{{element.id}}">{{{ label }}}</label>
|
||||
{{{ helpbutton }}}
|
||||
</div>
|
||||
<div class="felement {{# error }} error{{/ error }} f{{ element.type }}">
|
||||
{{# error }}
|
||||
<span class="error" tabindex="0" id="id_error_{{ element.name }}">
|
||||
{{{ error }}}
|
||||
</span>
|
||||
{{/ error }}
|
||||
{{$ element }}
|
||||
<!-- Element goes here -->
|
||||
{{/ element }}
|
||||
</div>
|
||||
</div>
|
||||
+4
-40
@@ -1,46 +1,10 @@
|
||||
YUI.add('moodle-form-passwordunmask', function (Y, NAME) {
|
||||
|
||||
var PASSWORDUNMASK = function() {
|
||||
PASSWORDUNMASK.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
|
||||
Y.extend(PASSWORDUNMASK, Y.Base, {
|
||||
// Initialize checkbox if id is passed.
|
||||
initializer: function(params) {
|
||||
if (params && params.formid) {
|
||||
this.add_checkbox(params.formid, params.checkboxlabel, params.checkboxname);
|
||||
}
|
||||
},
|
||||
|
||||
// Create checkbox for unmasking password.
|
||||
add_checkbox: function(elementid, checkboxlabel, checkboxname) {
|
||||
var node = Y.one('#' + elementid);
|
||||
|
||||
// Retaining unmask div from previous implementation.
|
||||
var unmaskdiv = Y.Node.create('<div id="' + elementid + 'unmaskdiv" class="unmask"></div>');
|
||||
|
||||
// Add checkbox for unmasking to unmaskdiv.
|
||||
var unmaskchb = Y.Node.create('<input id="' + elementid + 'unmask" type="checkbox" name="' +
|
||||
checkboxname + 'unmask">');
|
||||
unmaskdiv.appendChild(unmaskchb);
|
||||
// Attach event using static javascript function for unmasking password.
|
||||
unmaskchb.on('click', function() {
|
||||
window.unmaskPassword(elementid);
|
||||
});
|
||||
|
||||
// Add label for checkbox to unmaskdiv.
|
||||
var unmasklabel = Y.Node.create('<label for="' + elementid + 'unmask">' + checkboxlabel + '</label>');
|
||||
unmaskdiv.appendChild(unmasklabel);
|
||||
|
||||
// Insert unmask div in the same div as password input.
|
||||
node.get('parentNode').insert(unmaskdiv, node.get('lastNode'));
|
||||
}
|
||||
});
|
||||
|
||||
M.form = M.form || {};
|
||||
M.form.passwordunmask = function(params) {
|
||||
return new PASSWORDUNMASK(params);
|
||||
M.form.passwordunmask = function() {
|
||||
Y.log("The moodle-form-passwordunmask module has been deprecated. " +
|
||||
"Please use the core_forum/passwordunmask amd module instead.", 'moodle-form-passwordunmask', 'warn');
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["node", "base"]});
|
||||
}, '@VERSION@', {"requires": []});
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-form-passwordunmask",function(e,t){var n=function(){n.superclass.constructor.apply(this,arguments)};e.extend(n,e.Base,{initializer:function(e){e&&e.formid&&this.add_checkbox(e.formid,e.checkboxlabel,e.checkboxname)},add_checkbox:function(t,n,r){var i=e.one("#"+t),s=e.Node.create('<div id="'+t+'unmaskdiv" class="unmask"></div>'),o=e.Node.create('<input id="'+t+'unmask" type="checkbox" name="'+r+'unmask">');s.appendChild(o),o.on("click",function(){window.unmaskPassword(t)});var u=e.Node.create('<label for="'+t+'unmask">'+n+"</label>");s.appendChild(u),i.get("parentNode").insert(s,i.get("lastNode"))}}),M.form=M.form||{},M.form.passwordunmask=function(e){return new n(e)}},"@VERSION@",{requires:["node","base"]});
|
||||
YUI.add("moodle-form-passwordunmask",function(e,t){M.form=M.form||{},M.form.passwordunmask=function(){}},"@VERSION@",{requires:[]});
|
||||
|
||||
+2
-40
@@ -1,46 +1,8 @@
|
||||
YUI.add('moodle-form-passwordunmask', function (Y, NAME) {
|
||||
|
||||
var PASSWORDUNMASK = function() {
|
||||
PASSWORDUNMASK.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
|
||||
Y.extend(PASSWORDUNMASK, Y.Base, {
|
||||
// Initialize checkbox if id is passed.
|
||||
initializer: function(params) {
|
||||
if (params && params.formid) {
|
||||
this.add_checkbox(params.formid, params.checkboxlabel, params.checkboxname);
|
||||
}
|
||||
},
|
||||
|
||||
// Create checkbox for unmasking password.
|
||||
add_checkbox: function(elementid, checkboxlabel, checkboxname) {
|
||||
var node = Y.one('#' + elementid);
|
||||
|
||||
// Retaining unmask div from previous implementation.
|
||||
var unmaskdiv = Y.Node.create('<div id="' + elementid + 'unmaskdiv" class="unmask"></div>');
|
||||
|
||||
// Add checkbox for unmasking to unmaskdiv.
|
||||
var unmaskchb = Y.Node.create('<input id="' + elementid + 'unmask" type="checkbox" name="' +
|
||||
checkboxname + 'unmask">');
|
||||
unmaskdiv.appendChild(unmaskchb);
|
||||
// Attach event using static javascript function for unmasking password.
|
||||
unmaskchb.on('click', function() {
|
||||
window.unmaskPassword(elementid);
|
||||
});
|
||||
|
||||
// Add label for checkbox to unmaskdiv.
|
||||
var unmasklabel = Y.Node.create('<label for="' + elementid + 'unmask">' + checkboxlabel + '</label>');
|
||||
unmaskdiv.appendChild(unmasklabel);
|
||||
|
||||
// Insert unmask div in the same div as password input.
|
||||
node.get('parentNode').insert(unmaskdiv, node.get('lastNode'));
|
||||
}
|
||||
});
|
||||
|
||||
M.form = M.form || {};
|
||||
M.form.passwordunmask = function(params) {
|
||||
return new PASSWORDUNMASK(params);
|
||||
M.form.passwordunmask = function() {
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["node", "base"]});
|
||||
}, '@VERSION@', {"requires": []});
|
||||
|
||||
+3
-39
@@ -1,41 +1,5 @@
|
||||
var PASSWORDUNMASK = function() {
|
||||
PASSWORDUNMASK.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
|
||||
Y.extend(PASSWORDUNMASK, Y.Base, {
|
||||
// Initialize checkbox if id is passed.
|
||||
initializer: function(params) {
|
||||
if (params && params.formid) {
|
||||
this.add_checkbox(params.formid, params.checkboxlabel, params.checkboxname);
|
||||
}
|
||||
},
|
||||
|
||||
// Create checkbox for unmasking password.
|
||||
add_checkbox: function(elementid, checkboxlabel, checkboxname) {
|
||||
var node = Y.one('#' + elementid);
|
||||
|
||||
// Retaining unmask div from previous implementation.
|
||||
var unmaskdiv = Y.Node.create('<div id="' + elementid + 'unmaskdiv" class="unmask"></div>');
|
||||
|
||||
// Add checkbox for unmasking to unmaskdiv.
|
||||
var unmaskchb = Y.Node.create('<input id="' + elementid + 'unmask" type="checkbox" name="' +
|
||||
checkboxname + 'unmask">');
|
||||
unmaskdiv.appendChild(unmaskchb);
|
||||
// Attach event using static javascript function for unmasking password.
|
||||
unmaskchb.on('click', function() {
|
||||
window.unmaskPassword(elementid);
|
||||
});
|
||||
|
||||
// Add label for checkbox to unmaskdiv.
|
||||
var unmasklabel = Y.Node.create('<label for="' + elementid + 'unmask">' + checkboxlabel + '</label>');
|
||||
unmaskdiv.appendChild(unmasklabel);
|
||||
|
||||
// Insert unmask div in the same div as password input.
|
||||
node.get('parentNode').insert(unmaskdiv, node.get('lastNode'));
|
||||
}
|
||||
});
|
||||
|
||||
M.form = M.form || {};
|
||||
M.form.passwordunmask = function(params) {
|
||||
return new PASSWORDUNMASK(params);
|
||||
M.form.passwordunmask = function() {
|
||||
Y.log("The moodle-form-passwordunmask module has been deprecated. " +
|
||||
"Please use the core_forum/passwordunmask amd module instead.", 'moodle-form-passwordunmask', 'warn');
|
||||
};
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"moodle-form-passwordunmask": {
|
||||
"requires": [
|
||||
"node",
|
||||
"base"
|
||||
]
|
||||
"requires": []
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 172 B |
@@ -0,0 +1,3 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M5.2 11H12v1H4.2l1-1zm1.4-8.4L0 9.1V12h2.8l6.6-6.6-2.8-2.8zm4.8.8l-1 1-2.8-2.8 1-1c.8-.8 2.1-.8 2.8 0 .8.8.8 2 0 2.8z" fill="#999"/></svg>
|
||||
|
After Width: | Height: | Size: 442 B |
Binary file not shown.
|
After Width: | Height: | Size: 225 B |
@@ -0,0 +1,3 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M11.4 9.4L9.9 7.9c-.1-.1-.2-.1-.3-.2.4-.8.7-1.6.7-2.5C10.3 2.3 8 0 5.2 0 2.3 0 0 2.3 0 5.2s2.3 5.2 5.2 5.2c.9 0 1.7-.2 2.4-.6.1 0 .1.1.2.2l1.5 1.5c.6.6 1.5.6 2.1 0s.6-1.5 0-2.1zM5.2 7.3C4 7.3 3 6.4 3 5.2 3 4 4 3 5.2 3c1.2 0 2.2 1 2.2 2.2-.1 1.2-1 2.1-2.2 2.1z" fill="#999"/></svg>
|
||||
|
After Width: | Height: | Size: 584 B |
@@ -15,43 +15,55 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
Setting configpasswordunmask.
|
||||
@template core_admin/setting_configpasswordunmask
|
||||
|
||||
Admin password unmask setting template.
|
||||
|
||||
Context variables required for this template:
|
||||
* name - form element name
|
||||
* size - form element size
|
||||
* value - form element value
|
||||
* id - element id
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "test",
|
||||
"id": "test0",
|
||||
"size": "8",
|
||||
"value": "secret"
|
||||
}
|
||||
}}
|
||||
<div class="form-password">
|
||||
<input type="password" name="{{name}}" size="{{size}}" id="{{id}}" value="{{value}}" class="form-control d-inline-block">
|
||||
<div class="unmask" id="{{id}}unmaskdiv"></div>
|
||||
<span data-passwordunmask="wrapper" data-passwordunmaskid="{{ id }}">
|
||||
<noscript>
|
||||
<!-- Backwards compatability for Behat -->
|
||||
<input type="password"
|
||||
name="{{ name }}"
|
||||
id="{{ id }}"
|
||||
value="{{ value }}"
|
||||
size="{{ size }}"
|
||||
class="form-control d-inline-block"
|
||||
>
|
||||
</noscript>
|
||||
<span class="visibleifjs">
|
||||
<span data-passwordunmask="editor">
|
||||
<!-- The input in the noscript will be moved here as part of the page load -->
|
||||
</span>
|
||||
<a href="#" data-passwordunmask="edit" title="{{ edithint }}">
|
||||
<span data-passwordunmask="displayvalue">{{> core_form/element-passwordunmask-fill }}</span>
|
||||
{{# pix }} t/passwordunmask-edit, core, {{# str }} passwordunmaskedithint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
<a href="#" data-passwordunmask="unmask" title="{{ unmaskhint }}">
|
||||
{{# pix }} t/passwordunmask-reveal, core, {{# str }} passwordunmaskrevealhint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
<span data-passwordunmask="instructions" class="form-text text-muted" style="display: none;">
|
||||
{{# str }} passwordunmaskinstructions, form {{/ str }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{{#js}}
|
||||
(function() {
|
||||
var id = '{{id}}';
|
||||
var unmaskid = id + 'unmask';
|
||||
var unmaskdivid = id + 'unmaskdiv';
|
||||
var unmaskstr = {{#quote}}{{#str}}unmaskpassword, form{{/str}}{{/quote}};
|
||||
var is_ie = (navigator.userAgent.toLowerCase().indexOf("msie") != -1);
|
||||
|
||||
document.getElementById(id).setAttribute("autocomplete", "off");
|
||||
|
||||
var unmaskdiv = document.getElementById(unmaskdivid);
|
||||
|
||||
var unmaskchb = document.createElement("input");
|
||||
unmaskchb.setAttribute("type", "checkbox");
|
||||
unmaskchb.setAttribute("id", unmaskid);
|
||||
unmaskchb.onchange = function() {unmaskPassword(id);};
|
||||
unmaskdiv.appendChild(unmaskchb);
|
||||
|
||||
var unmasklbl = document.createElement("label");
|
||||
unmasklbl.innerHTML = unmaskstr;
|
||||
if (is_ie) {
|
||||
unmasklbl.setAttribute("htmlFor", unmaskid);
|
||||
} else {
|
||||
unmasklbl.setAttribute("for", unmaskid);
|
||||
}
|
||||
unmaskdiv.appendChild(unmasklbl);
|
||||
|
||||
if (is_ie) {
|
||||
// Ugly hack to work around the famous onchange IE bug.
|
||||
unmaskchb.onclick = function() {this.blur();};
|
||||
unmaskdiv.onclick = function() {this.blur();};
|
||||
}
|
||||
})()
|
||||
require(['core_form/passwordunmask'], function(PasswordUnmask) {
|
||||
new PasswordUnmask("{{ id }}");
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -1,12 +1,94 @@
|
||||
{{> core_form/element-password }}
|
||||
{{^element.frozen}}
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_form/element-passwordunmask
|
||||
|
||||
Moodle passwordunmask form element template.
|
||||
|
||||
The purpose of this template is to render a passwordunmask form element.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* element
|
||||
* id
|
||||
* name
|
||||
* value
|
||||
* size
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"element": {
|
||||
"id": "example_password_unmask",
|
||||
"name": "example",
|
||||
"value": "Password1!",
|
||||
"size": 40
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
{{< 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 -->
|
||||
</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, {{# str }} passwordunmaskedithint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
{{/ element.frozen }}
|
||||
<a href="#" data-passwordunmask="unmask" title="{{ unmaskhint }}">
|
||||
{{# pix }} t/passwordunmask-reveal, core, {{# str }} passwordunmaskrevealhint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
<span data-passwordunmask="instructions" class="form-text text-muted" style="display: none;">
|
||||
{{^ element.frozen }}
|
||||
{{# str }} passwordunmaskinstructions, form {{/ str }}
|
||||
{{/ element.frozen }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
{{/ element }}
|
||||
{{/ core_form/element-template }}
|
||||
{{#js}}
|
||||
require(['core/yui'], function(Y) {
|
||||
Y.use('moodle-form-passwordunmask', function() {
|
||||
M.form.passwordunmask({ formid: {{#quote}}{{element.id}}{{/quote}},
|
||||
checkboxlabel: {{#quote}}{{#str}}unmaskpassword, form{{/str}}{{/quote}},
|
||||
checkboxname: {{#quote}}{{element.name}}{{/quote}} });
|
||||
});
|
||||
require(['core_form/passwordunmask'], function(PasswordUnmask) {
|
||||
new PasswordUnmask("{{ element.id }}");
|
||||
});
|
||||
{{/js}}
|
||||
{{/element.frozen}}
|
||||
{{/ js }}
|
||||
|
||||
@@ -524,3 +524,9 @@ input[size] {
|
||||
textarea[data-auto-rows] {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
div[data-passwordunmask="wrapper"] {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -14349,6 +14349,11 @@ input[size] {
|
||||
textarea[data-auto-rows] {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
div[data-passwordunmask="wrapper"] {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user