Skip to content

Commit

Permalink
MDL-53048 form: Rewrite passwordunmask
Browse files Browse the repository at this point in the history
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
  • Loading branch information
andrewnicols committed Oct 24, 2016
1 parent ac157b0 commit ca25005
Show file tree
Hide file tree
Showing 27 changed files with 791 additions and 228 deletions.
63 changes: 29 additions & 34 deletions admin/templates/setting_configpasswordunmask.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
1 change: 1 addition & 0 deletions lang/en/deprecated.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ downloadoptions,core_table
downloadtsv,core_table
downloadxhtml,core_table
invalidpersistent,core_competency
revealpassword,core_form
9 changes: 8 additions & 1 deletion lang/en/form.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@
$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';
Expand All @@ -68,3 +72,6 @@
$string['timing'] = 'Timing';
$string['unmaskpassword'] = 'Unmask';
$string['year'] = 'Year';

// Deprecated since 3.2.
$string['revealpassword'] = 'Reveal';
10 changes: 4 additions & 6 deletions lib/adminlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -2406,13 +2406,11 @@ protected function add_to_config_log($name, $oldvalue, $value) {
}

/**
* 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;
Expand Down
4 changes: 4 additions & 0 deletions lib/behat/behat_field_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ protected static function get_field_node_type(NodeElement $fieldnode, Session $s
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')) {

Expand Down
5 changes: 5 additions & 0 deletions lib/behat/classes/partial_named_selector.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ public function __construct() {
'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
],
];
Expand Down
64 changes: 64 additions & 0 deletions lib/behat/form_field/behat_form_passwordunmask.php
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions lib/form/amd/build/passwordunmask.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ca25005

Please sign in to comment.