MDL-79769 login: Add password toggle icon to the login screen
This commit is contained in:
@@ -135,6 +135,18 @@ if ($hassiteconfig) {
|
||||
$temp->add($setting);
|
||||
$ADMIN->add('authsettings', $temp);
|
||||
|
||||
// Toggle password visiblity icon.
|
||||
$temp->add(new admin_setting_configselect('loginpasswordtoggle',
|
||||
new lang_string('auth_loginpasswordtoggle', 'auth'),
|
||||
new lang_string('auth_loginpasswordtoggle_desc', 'auth'),
|
||||
TOGGLE_SENSITIVE_SMALL_SCREENS_ONLY,
|
||||
[
|
||||
TOGGLE_SENSITIVE_DISABLED => get_string('disabled', 'admin'),
|
||||
TOGGLE_SENSITIVE_ENABLED => get_string('enabled', 'admin'),
|
||||
TOGGLE_SENSITIVE_SMALL_SCREENS_ONLY => get_string('smallscreensonly', 'admin'),
|
||||
],
|
||||
));
|
||||
|
||||
$temp = new admin_externalpage('authtestsettings', get_string('testsettings', 'core_auth'), new moodle_url("/auth/test_settings.php"), 'moodle/site:config', true);
|
||||
$ADMIN->add('authsettings', $temp);
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ class login implements renderable, templatable {
|
||||
public $maintenance;
|
||||
/** @var string ReCaptcha element HTML. */
|
||||
public $recaptcha;
|
||||
/** @var bool Toggle the password visibility icon. */
|
||||
public $togglepassword;
|
||||
/** @var bool Toggle the password visibility icon for small screens only. */
|
||||
public $smallscreensonly;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -130,6 +134,11 @@ class login implements renderable, templatable {
|
||||
require_once($CFG->libdir . '/recaptchalib_v2.php');
|
||||
$this->recaptcha = recaptcha_get_challenge_html(RECAPTCHA_API_URL, $CFG->recaptchapublickey);
|
||||
}
|
||||
|
||||
// Toggle password visibility icon.
|
||||
$this->togglepassword = get_config('core', 'loginpasswordtoggle') == TOGGLE_SENSITIVE_ENABLED ||
|
||||
get_config('core', 'loginpasswordtoggle') == TOGGLE_SENSITIVE_SMALL_SCREENS_ONLY;
|
||||
$this->smallscreensonly = get_config('core', 'loginpasswordtoggle') == TOGGLE_SENSITIVE_SMALL_SCREENS_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,6 +184,8 @@ class login implements renderable, templatable {
|
||||
$data->maintenance = format_text($this->maintenance, FORMAT_MOODLE);
|
||||
$data->languagemenu = $this->languagemenu;
|
||||
$data->recaptcha = $this->recaptcha;
|
||||
$data->togglepassword = $this->togglepassword;
|
||||
$data->smallscreensonly = $this->smallscreensonly;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -101,3 +101,21 @@ Feature: Test if the login form provides the correct feedback
|
||||
And I press "Log in"
|
||||
And I press the tab key
|
||||
Then the focused element is "Username" "field"
|
||||
|
||||
Scenario: Display the password visibility toggle icon
|
||||
Given the following config values are set as admin:
|
||||
| loginpasswordtoggle | 1 |
|
||||
When I follow "Log in"
|
||||
Then "Toggle sensitive" "button" should be visible
|
||||
And the following config values are set as admin:
|
||||
| loginpasswordtoggle | 0 |
|
||||
And I reload the page
|
||||
And "Toggle sensitive" "button" should not be visible
|
||||
|
||||
Scenario: Display the password visibility toggle icon for small screens only
|
||||
Given the following config values are set as admin:
|
||||
| loginpasswordtoggle | 2 |
|
||||
When I follow "Log in"
|
||||
Then "Toggle sensitive" "button" should not be visible
|
||||
And I change the viewport size to "mobile"
|
||||
And "Toggle sensitive" "button" should be visible
|
||||
|
||||
@@ -1312,6 +1312,7 @@ $string['sixtyfourbitsrequired'] = 'It has been detected that your site is not u
|
||||
$string['sixtyfourbitswarning'] = 'It has been detected that your site is not using a 64-bit PHP version. It is recommended that you upgrade your site to ensure future compatibility.';
|
||||
$string['slasharguments'] = 'Use slash arguments';
|
||||
$string['slashargumentswarning'] = 'It is recommended that the use of slash arguments is enabled. In future it will be required. For more details, see the documentation <a href="https://docs.moodle.org/en/admin/environment/slasharguments">Using slash arguments</a>.';
|
||||
$string['smallscreensonly'] = 'Small screens only';
|
||||
$string['smtp'] = 'SMTP';
|
||||
$string['smtpauthtype'] = 'SMTP Auth Type';
|
||||
$string['smtpdetail'] = 'Simple Mail Transfer Protocol (SMTP) settings for sending email.';
|
||||
|
||||
@@ -41,6 +41,8 @@ $string['auth_changepasswordurl_expl'] = 'Specify the url to send users who have
|
||||
$string['auth_changingemailaddress'] = 'You have requested a change of email address, from {$a->oldemail} to {$a->newemail}. For security reasons, we are sending you an email message at the new address to confirm that it belongs to you. Your email address will be updated as soon as you open the URL sent to you in that message.';
|
||||
$string['authinstructions'] = 'Leave this blank for the default login instructions to be displayed on the login page. If you want to provide custom login instructions, enter them here.';
|
||||
$string['auth_invalidnewemailkey'] = 'Error: if you are trying to confirm a change of email address, you may have made a mistake in copying the URL we sent you by email. Please copy the address and try again.';
|
||||
$string['auth_loginpasswordtoggle'] = 'Password visibility toggle';
|
||||
$string['auth_loginpasswordtoggle_desc'] = 'Add an icon to the password field on the login screen that allows users to show or hide their entered password.';
|
||||
$string['auth_loginrecaptcha'] = 'Enable reCAPTCHA for login';
|
||||
$string['auth_loginrecaptcha_desc'] = 'Add a visual/audio confirmation form element to the login page. This reduces the risk of unwarranted login attempts. See <a target="_blank" href="https://www.google.com/recaptcha">Google reCAPTCHA</a> for more details. ';
|
||||
$string['auth_multiplehosts'] = 'Multiple hosts OR addresses can be specified (eg host1.com;host2.com;host3.com) or (eg xxx.xxx.xxx.xxx;xxx.xxx.xxx.xxx)';
|
||||
|
||||
@@ -87,5 +87,6 @@ $string['suggestions'] = 'Suggestions';
|
||||
$string['time'] = 'Time';
|
||||
$string['timeunit'] = 'Time unit';
|
||||
$string['timing'] = 'Timing';
|
||||
$string['togglesensitive'] = 'Toggle sensitive';
|
||||
$string['unmaskpassword'] = 'Unmask';
|
||||
$string['year'] = 'Year';
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
define("core/togglesensitive",["exports","core/pagehelpers","core/templates","core/pending","core/prefetch","core/notification"],(function(_exports,_pagehelpers,_templates,_pending,_prefetch,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* JS module for toggling the sensitive input visibility (e.g. passwords, keys).
|
||||
*
|
||||
* @module core/togglesensitive
|
||||
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_pending=_interopRequireDefault(_pending),_prefetch=_interopRequireDefault(_prefetch),_notification=_interopRequireDefault(_notification);const SELECTORS_BUTTON=".toggle-sensitive-btn",PIX_EYE="t/hide",PIX_EYE_SLASH="t/show";let sensitiveElementId,smallScreensOnly;_exports.init=function(elementId){let isSmallScreensOnly=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const sensitiveInput=document.getElementById(elementId);null!==sensitiveInput&&(sensitiveElementId=elementId,smallScreensOnly=isSmallScreensOnly,_prefetch.default.prefetchTemplate("core/form_input_toggle_sensitive"),renderSensitiveToggle(sensitiveInput).then((()=>{})).catch(_notification.default.exception),registerListenerEvents())};const renderSensitiveToggle=async sensitiveInput=>{const{html:html}=await _templates.default.renderForPromise("core/form_input_toggle_sensitive",{smallscreensonly:smallScreensOnly,sensitiveinput:sensitiveInput.outerHTML});sensitiveInput.outerHTML=html},registerListenerEvents=()=>{document.addEventListener("click",handleButtonInteraction),smallScreensOnly&&window.addEventListener("resize",handleScreenResizing)},handleButtonInteraction=event=>{const toggleButton=event.target.closest(SELECTORS_BUTTON);if(toggleButton){const sensitiveInput=document.getElementById(sensitiveElementId);toggleSensitiveVisibility(sensitiveInput,toggleButton)}},handleScreenResizing=()=>{if(!(0,_pagehelpers.isExtraSmall)()){const sensitiveInput=document.getElementById(sensitiveElementId),toggleButton=sensitiveInput.parentNode.querySelector(SELECTORS_BUTTON);toggleSensitiveVisibility(sensitiveInput,toggleButton,!0)}},toggleSensitiveVisibility=function(sensitiveInput,toggleButton){let force=arguments.length>2&&void 0!==arguments[2]&&arguments[2];const pendingPromise=new _pending.default("core/togglesensitive:toggle");let type,icon;!0===force?(type="password",icon=PIX_EYE):(type="password"===sensitiveInput.getAttribute("type")?"text":"password",icon="password"===sensitiveInput.getAttribute("type")?PIX_EYE_SLASH:PIX_EYE),sensitiveInput.setAttribute("type",type),_templates.default.renderPix(icon,"core").then((icon=>{toggleButton.innerHTML=icon,pendingPromise.resolve()})).catch(_notification.default.exception)}}));
|
||||
|
||||
//# sourceMappingURL=togglesensitive.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,150 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* JS module for toggling the sensitive input visibility (e.g. passwords, keys).
|
||||
*
|
||||
* @module core/togglesensitive
|
||||
* @copyright 2023 David Woloszyn <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {isExtraSmall} from 'core/pagehelpers';
|
||||
import Templates from 'core/templates';
|
||||
import Pending from 'core/pending';
|
||||
import Prefetch from 'core/prefetch';
|
||||
import Notification from 'core/notification';
|
||||
|
||||
const SELECTORS = {
|
||||
BUTTON: '.toggle-sensitive-btn',
|
||||
ICON: '.toggle-sensitive-btn .icon',
|
||||
};
|
||||
|
||||
const PIX = {
|
||||
EYE: 't/hide',
|
||||
EYE_SLASH: 't/show',
|
||||
};
|
||||
|
||||
let sensitiveElementId;
|
||||
let smallScreensOnly;
|
||||
|
||||
/**
|
||||
* Entrypoint of the js.
|
||||
*
|
||||
* @method init
|
||||
* @param {String} elementId Form button element.
|
||||
* @param {boolean} isSmallScreensOnly Is this for small screens only?
|
||||
*/
|
||||
export const init = (elementId, isSmallScreensOnly = false) => {
|
||||
const sensitiveInput = document.getElementById(elementId);
|
||||
if (sensitiveInput === null) {
|
||||
// Exit early if invalid element id passed.
|
||||
return;
|
||||
}
|
||||
sensitiveElementId = elementId;
|
||||
smallScreensOnly = isSmallScreensOnly;
|
||||
Prefetch.prefetchTemplate('core/form_input_toggle_sensitive');
|
||||
// Render the sensitive input with a toggle button.
|
||||
renderSensitiveToggle(sensitiveInput).then(() => {
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
// Register event listeners.
|
||||
registerListenerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the new input html with toggle button and update the incoming html.
|
||||
*
|
||||
* @method renderSensitiveToggle
|
||||
* @param {HTMLElement} sensitiveInput HTML element for the sensitive input.
|
||||
*/
|
||||
const renderSensitiveToggle = async(sensitiveInput) => {
|
||||
const {html} = await Templates.renderForPromise(
|
||||
'core/form_input_toggle_sensitive',
|
||||
{
|
||||
smallscreensonly: smallScreensOnly,
|
||||
sensitiveinput: sensitiveInput.outerHTML,
|
||||
}
|
||||
);
|
||||
sensitiveInput.outerHTML = html;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*
|
||||
* @method registerListenerEvents
|
||||
*/
|
||||
const registerListenerEvents = () => {
|
||||
// Toggle the sensitive input visibility when interacting with the toggle button.
|
||||
document.addEventListener('click', handleButtonInteraction);
|
||||
// For small screens only, hide all sensitive inputs when the screen is enlarged.
|
||||
if (smallScreensOnly) {
|
||||
window.addEventListener('resize', handleScreenResizing);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle events trigger by interacting with the toggle button.
|
||||
*
|
||||
* @method handleButtonInteraction
|
||||
* @param {Event} event The button event.
|
||||
*/
|
||||
const handleButtonInteraction = (event) => {
|
||||
const toggleButton = event.target.closest(SELECTORS.BUTTON);
|
||||
if (toggleButton) {
|
||||
const sensitiveInput = document.getElementById(sensitiveElementId);
|
||||
toggleSensitiveVisibility(sensitiveInput, toggleButton);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle events trigger by resizing the screen.
|
||||
*
|
||||
* @method handleScreenResizing
|
||||
*/
|
||||
const handleScreenResizing = () => {
|
||||
if (!isExtraSmall()) {
|
||||
const sensitiveInput = document.getElementById(sensitiveElementId);
|
||||
const toggleButton = sensitiveInput.parentNode.querySelector(SELECTORS.BUTTON);
|
||||
toggleSensitiveVisibility(sensitiveInput, toggleButton, true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the sensitive input visibility and its associated icon.
|
||||
*
|
||||
* @method toggleSensitiveVisibility
|
||||
* @param {HTMLInputElement} sensitiveInput The sensitive input element.
|
||||
* @param {HTMLElement} toggleButton The toggle button.
|
||||
* @param {boolean} force Force the input back to password type.
|
||||
*/
|
||||
const toggleSensitiveVisibility = (sensitiveInput, toggleButton, force = false) => {
|
||||
const pendingPromise = new Pending('core/togglesensitive:toggle');
|
||||
let type;
|
||||
let icon;
|
||||
if (force === true) {
|
||||
type = 'password';
|
||||
icon = PIX.EYE;
|
||||
} else {
|
||||
type = sensitiveInput.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
icon = sensitiveInput.getAttribute('type') === 'password' ? PIX.EYE_SLASH : PIX.EYE;
|
||||
}
|
||||
sensitiveInput.setAttribute('type', type);
|
||||
Templates.renderPix(icon, 'core').then((icon) => {
|
||||
toggleButton.innerHTML = icon;
|
||||
pendingPromise.resolve();
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
};
|
||||
@@ -601,6 +601,21 @@ define('CONTACT_SUPPORT_ANYONE', 2);
|
||||
*/
|
||||
define('MAX_PASSWORD_CHARACTERS', 128);
|
||||
|
||||
/**
|
||||
* Toggle sensitive feature is disabled. Used for sensitive inputs (passwords, tokens, keys).
|
||||
*/
|
||||
define('TOGGLE_SENSITIVE_DISABLED', 0);
|
||||
|
||||
/**
|
||||
* Toggle sensitive feature is enabled. Used for sensitive inputs (passwords, tokens, keys).
|
||||
*/
|
||||
define('TOGGLE_SENSITIVE_ENABLED', 1);
|
||||
|
||||
/**
|
||||
* Toggle sensitive feature is enabled for small screens only. Used for sensitive inputs (passwords, tokens, keys).
|
||||
*/
|
||||
define('TOGGLE_SENSITIVE_SMALL_SCREENS_ONLY', 2);
|
||||
|
||||
// PARAMETER HANDLING.
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{{!
|
||||
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_input_toggle_sensitive
|
||||
|
||||
Moodle template for toggling senstive inputs.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"smallscreensonly": true,
|
||||
"sensitiveinput": "<input type=\"password\" name=\"password\" id=\"password\">"
|
||||
}
|
||||
}}
|
||||
<div class="toggle-sensitive-wrapper {{#smallscreensonly}}small-screens-only{{/smallscreensonly}} input-group mb-3">
|
||||
{{{sensitiveinput}}}
|
||||
<div class="input-group-append">
|
||||
<button class="toggle-sensitive-btn btn btn-secondary" type="button" {{!
|
||||
!}}aria-label="{{#str}}togglesensitive, form{{/str}}">
|
||||
{{#pix}}t/hide, core{{/pix}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,7 +91,9 @@
|
||||
"sitename": "Beer & Chips",
|
||||
"logintoken": "randomstring",
|
||||
"maintenance": "For full access to this site, you need to login in as an admin.",
|
||||
"languagemenu": "Choose language"
|
||||
"languagemenu": "Choose language",
|
||||
"togglepassword": true,
|
||||
"smallscreensonly": true
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -214,6 +216,11 @@
|
||||
|
||||
{{#js}}
|
||||
{{^error}}
|
||||
{{#togglepassword}}
|
||||
require(['core/togglesensitive'], function(ToggleSensitive) {
|
||||
ToggleSensitive.init("password", {{smallscreensonly}});
|
||||
});
|
||||
{{/togglepassword}}
|
||||
{{#autofocusform}}
|
||||
var userNameField = document.getElementById('username');
|
||||
if (userNameField.value.length == 0) {
|
||||
|
||||
@@ -480,3 +480,24 @@ textarea[data-auto-rows] {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the margins that come with the icon.
|
||||
.toggle-sensitive-btn .icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
// Input group elements display border radius depending on the proximity of other elements.
|
||||
// This corrects border radius of the input when 'small screen only' mode hides the toggle button.
|
||||
@media (min-width: 576px) {
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > {
|
||||
.form-control:not(:last-child) {
|
||||
@include border-radius($input-border-radius);
|
||||
}
|
||||
.form-control-lg:not(:last-child) {
|
||||
@include border-radius($input-border-radius-lg);
|
||||
}
|
||||
.input-group-append .toggle-sensitive-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33262,6 +33262,21 @@ textarea[data-auto-rows] {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.toggle-sensitive-btn .icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > .form-control:not(:last-child) {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > .form-control-lg:not(:last-child) {
|
||||
border-radius: 0.6rem;
|
||||
}
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > .input-group-append .toggle-sensitive-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.pagelayout-login #region-main {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
|
||||
@@ -33262,6 +33262,21 @@ textarea[data-auto-rows] {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.toggle-sensitive-btn .icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > .form-control:not(:last-child) {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > .form-control-lg:not(:last-child) {
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
.toggle-sensitive-wrapper.small-screens-only.input-group:not(.has-validation) > .input-group-append .toggle-sensitive-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.pagelayout-login #region-main {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
|
||||
Reference in New Issue
Block a user