diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php index 33ddda70751..43909fba346 100644 --- a/admin/settings/plugins.php +++ b/admin/settings/plugins.php @@ -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); diff --git a/auth/classes/output/login.php b/auth/classes/output/login.php index 3800cb72ecc..6854234c0d4 100644 --- a/auth/classes/output/login.php +++ b/auth/classes/output/login.php @@ -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; } diff --git a/auth/tests/behat/loginform.feature b/auth/tests/behat/loginform.feature index a87301d421a..cc4c01338e7 100644 --- a/auth/tests/behat/loginform.feature +++ b/auth/tests/behat/loginform.feature @@ -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 diff --git a/lang/en/admin.php b/lang/en/admin.php index 71dbb942050..dc10097c83d 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -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 Using slash arguments.'; +$string['smallscreensonly'] = 'Small screens only'; $string['smtp'] = 'SMTP'; $string['smtpauthtype'] = 'SMTP Auth Type'; $string['smtpdetail'] = 'Simple Mail Transfer Protocol (SMTP) settings for sending email.'; diff --git a/lang/en/auth.php b/lang/en/auth.php index c73816c063c..2a42ae444a1 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -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 Google reCAPTCHA 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)'; diff --git a/lang/en/form.php b/lang/en/form.php index 440997cb63e..0ddade386d0 100644 --- a/lang/en/form.php +++ b/lang/en/form.php @@ -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'; diff --git a/lib/amd/build/togglesensitive.min.js b/lib/amd/build/togglesensitive.min.js new file mode 100644 index 00000000000..60aaba458c1 --- /dev/null +++ b/lib/amd/build/togglesensitive.min.js @@ -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 + * @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 \ No newline at end of file diff --git a/lib/amd/build/togglesensitive.min.js.map b/lib/amd/build/togglesensitive.min.js.map new file mode 100644 index 00000000000..874832ab0ed --- /dev/null +++ b/lib/amd/build/togglesensitive.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"togglesensitive.min.js","sources":["../src/togglesensitive.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS module for toggling the sensitive input visibility (e.g. passwords, keys).\n *\n * @module core/togglesensitive\n * @copyright 2023 David Woloszyn \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {isExtraSmall} from 'core/pagehelpers';\nimport Templates from 'core/templates';\nimport Pending from 'core/pending';\nimport Prefetch from 'core/prefetch';\nimport Notification from 'core/notification';\n\nconst SELECTORS = {\n BUTTON: '.toggle-sensitive-btn',\n ICON: '.toggle-sensitive-btn .icon',\n};\n\nconst PIX = {\n EYE: 't/hide',\n EYE_SLASH: 't/show',\n};\n\nlet sensitiveElementId;\nlet smallScreensOnly;\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n * @param {String} elementId Form button element.\n * @param {boolean} isSmallScreensOnly Is this for small screens only?\n */\nexport const init = (elementId, isSmallScreensOnly = false) => {\n const sensitiveInput = document.getElementById(elementId);\n if (sensitiveInput === null) {\n // Exit early if invalid element id passed.\n return;\n }\n sensitiveElementId = elementId;\n smallScreensOnly = isSmallScreensOnly;\n Prefetch.prefetchTemplate('core/form_input_toggle_sensitive');\n // Render the sensitive input with a toggle button.\n renderSensitiveToggle(sensitiveInput).then(() => {\n return;\n }).catch(Notification.exception);\n // Register event listeners.\n registerListenerEvents();\n};\n\n/**\n * Render the new input html with toggle button and update the incoming html.\n *\n * @method renderSensitiveToggle\n * @param {HTMLElement} sensitiveInput HTML element for the sensitive input.\n */\nconst renderSensitiveToggle = async(sensitiveInput) => {\n const {html} = await Templates.renderForPromise(\n 'core/form_input_toggle_sensitive',\n {\n smallscreensonly: smallScreensOnly,\n sensitiveinput: sensitiveInput.outerHTML,\n }\n );\n sensitiveInput.outerHTML = html;\n};\n\n/**\n * Register event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n // Toggle the sensitive input visibility when interacting with the toggle button.\n document.addEventListener('click', handleButtonInteraction);\n // For small screens only, hide all sensitive inputs when the screen is enlarged.\n if (smallScreensOnly) {\n window.addEventListener('resize', handleScreenResizing);\n }\n};\n\n/**\n * Handle events trigger by interacting with the toggle button.\n *\n * @method handleButtonInteraction\n * @param {Event} event The button event.\n */\nconst handleButtonInteraction = (event) => {\n const toggleButton = event.target.closest(SELECTORS.BUTTON);\n if (toggleButton) {\n const sensitiveInput = document.getElementById(sensitiveElementId);\n toggleSensitiveVisibility(sensitiveInput, toggleButton);\n }\n};\n\n/**\n * Handle events trigger by resizing the screen.\n *\n * @method handleScreenResizing\n */\nconst handleScreenResizing = () => {\n if (!isExtraSmall()) {\n const sensitiveInput = document.getElementById(sensitiveElementId);\n const toggleButton = sensitiveInput.parentNode.querySelector(SELECTORS.BUTTON);\n toggleSensitiveVisibility(sensitiveInput, toggleButton, true);\n }\n};\n\n/**\n * Toggle the sensitive input visibility and its associated icon.\n *\n * @method toggleSensitiveVisibility\n * @param {HTMLInputElement} sensitiveInput The sensitive input element.\n * @param {HTMLElement} toggleButton The toggle button.\n * @param {boolean} force Force the input back to password type.\n */\nconst toggleSensitiveVisibility = (sensitiveInput, toggleButton, force = false) => {\n const pendingPromise = new Pending('core/togglesensitive:toggle');\n let type;\n let icon;\n if (force === true) {\n type = 'password';\n icon = PIX.EYE;\n } else {\n type = sensitiveInput.getAttribute('type') === 'password' ? 'text' : 'password';\n icon = sensitiveInput.getAttribute('type') === 'password' ? PIX.EYE_SLASH : PIX.EYE;\n }\n sensitiveInput.setAttribute('type', type);\n Templates.renderPix(icon, 'core').then((icon) => {\n toggleButton.innerHTML = icon;\n pendingPromise.resolve();\n return;\n }).catch(Notification.exception);\n};\n"],"names":["SELECTORS","PIX","sensitiveElementId","smallScreensOnly","elementId","isSmallScreensOnly","sensitiveInput","document","getElementById","prefetchTemplate","renderSensitiveToggle","then","catch","Notification","exception","registerListenerEvents","async","html","Templates","renderForPromise","smallscreensonly","sensitiveinput","outerHTML","addEventListener","handleButtonInteraction","window","handleScreenResizing","event","toggleButton","target","closest","toggleSensitiveVisibility","parentNode","querySelector","force","pendingPromise","Pending","type","icon","getAttribute","setAttribute","renderPix","innerHTML","resolve"],"mappings":";;;;;;;gRA6BMA,iBACM,wBAINC,QACG,SADHA,cAES,aAGXC,mBACAC,+BASgB,SAACC,eAAWC,iFACtBC,eAAiBC,SAASC,eAAeJ,WACxB,OAAnBE,iBAIJJ,mBAAqBE,UACrBD,iBAAmBE,qCACVI,iBAAiB,oCAE1BC,sBAAsBJ,gBAAgBK,MAAK,SAExCC,MAAMC,sBAAaC,WAEtBC,iCASEL,sBAAwBM,MAAAA,uBACpBC,KAACA,YAAcC,mBAAUC,iBAC3B,mCACA,CACIC,iBAAkBjB,iBAClBkB,eAAgBf,eAAegB,YAGvChB,eAAegB,UAAYL,MAQzBF,uBAAyB,KAE3BR,SAASgB,iBAAiB,QAASC,yBAE/BrB,kBACAsB,OAAOF,iBAAiB,SAAUG,uBAUpCF,wBAA2BG,cACvBC,aAAeD,MAAME,OAAOC,QAAQ9B,qBACtC4B,aAAc,OACRtB,eAAiBC,SAASC,eAAeN,oBAC/C6B,0BAA0BzB,eAAgBsB,gBAS5CF,qBAAuB,UACpB,+BAAgB,OACXpB,eAAiBC,SAASC,eAAeN,oBACzC0B,aAAetB,eAAe0B,WAAWC,cAAcjC,kBAC7D+B,0BAA0BzB,eAAgBsB,cAAc,KAY1DG,0BAA4B,SAACzB,eAAgBsB,kBAAcM,oEACvDC,eAAiB,IAAIC,iBAAQ,mCAC/BC,KACAC,MACU,IAAVJ,OACAG,KAAO,WACPC,KAAOrC,UAEPoC,KAA+C,aAAxC/B,eAAeiC,aAAa,QAAyB,OAAS,WACrED,KAA+C,aAAxChC,eAAeiC,aAAa,QAAyBtC,cAAgBA,SAEhFK,eAAekC,aAAa,OAAQH,yBAC1BI,UAAUH,KAAM,QAAQ3B,MAAM2B,OACpCV,aAAac,UAAYJ,KACzBH,eAAeQ,aAEhB/B,MAAMC,sBAAaC"} \ No newline at end of file diff --git a/lib/amd/src/togglesensitive.js b/lib/amd/src/togglesensitive.js new file mode 100644 index 00000000000..1040b248e41 --- /dev/null +++ b/lib/amd/src/togglesensitive.js @@ -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 . + +/** + * JS module for toggling the sensitive input visibility (e.g. passwords, keys). + * + * @module core/togglesensitive + * @copyright 2023 David Woloszyn + * @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); +}; diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 1db650c1cf2..d5a9808cf6a 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -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. /** diff --git a/lib/templates/form_input_toggle_sensitive.mustache b/lib/templates/form_input_toggle_sensitive.mustache new file mode 100644 index 00000000000..ba47b469c0c --- /dev/null +++ b/lib/templates/form_input_toggle_sensitive.mustache @@ -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 . +}} +{{! + @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": "" + } +}} +
+ {{{sensitiveinput}}} +
+ +
+
diff --git a/lib/templates/loginform.mustache b/lib/templates/loginform.mustache index 05060f3ff6e..6dc17fa437c 100644 --- a/lib/templates/loginform.mustache +++ b/lib/templates/loginform.mustache @@ -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) { diff --git a/theme/boost/scss/moodle/forms.scss b/theme/boost/scss/moodle/forms.scss index 57a91eb4c82..8e856746f4d 100644 --- a/theme/boost/scss/moodle/forms.scss +++ b/theme/boost/scss/moodle/forms.scss @@ -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; + } + } +} diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 8ba5690c500..9d7a523074f 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -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; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 5d6a658ebde..d296862a14a 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -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;