MDL-72928 login: Update styling and functionality

This commit is contained in:
Mathew May 2022-01-14 16:06:30 +08:00
parent 5897242361
commit ecb92c7ed9
16 changed files with 358 additions and 176 deletions

View File

@ -0,0 +1,134 @@
---
layout: docs
title: "HTML Modals"
description: "A reusable handled modal component"
date: 2021-12-09T14:48:00+08:00
draft: false
tags:
- MDL-71963
- MDL-72928
- "4.0"
---
## How it works
The core/utility module allows different modals to be displayed automatically when interacting with the page.
Modals are configured using a set of specific data-attributes.
## Source files
* `lib/amd/src/utility.js` ({{< jsdoc module="core/utility" >}})
* `lib/templates/modal.mustache`
## Usage
The confirmation AMD module is loaded automatically, so the only thing you need to do is to add some specific data attributes
to the target element.
To display a confirmation modal.
{{< highlight html >}}
<button type="button" class="btn btn-primary" data-modal="confirmation" data-modal-title-str='["delete", "core"]'
data-modal-content-str='["areyousure"]' data-modal-yes-button-str='["delete", "core"]'>Show confirmation modal</button>
{{< /highlight >}}
To display an alert modal.
{{< highlight html >}}
<button type="button" class="btn btn-primary" data-modal="alert" data-modal-title-str='["cookiesenabled", "core"]'
data-modal-content-str='["cookiesenabled_help_html", "core"]'>Show alert modal</button>
{{< /highlight >}}
You can also use it on PHP, you just need to set the attributes parameter to any moodle output component that takes attributes:
{{< php >}}
echo $OUTPUT->single_button('#', get_string('delete'), 'get', [
'data-modal' => 'modal',
'data-modal-title-str' => json_encode(['delete', 'core']),
'data-modal-content-str' => json_encode(['areyousure']),
'data-modal-yes-button-str' => json_encode(['delete', 'core'])
]);
{{< / php >}}
## Attributes
<table class="table">
<thead>
<tr>
<th style="width: 250px;">Data attribute</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>data-modal</td>
<td>One of either "confirmation", or "alert".</td>
</tr>
<tr>
<td>data-modal-title-str</td>
<td>The modal title language string identifier, must be provided in JSON encoded format.</td>
</tr>
<tr>
<td>data-modal-content-str</td>
<td>The modal content or content language string identifier, must be provided in JSON encoded format.</td>
</tr>
<tr>
<td>data-modal-yes-button-str</td>
<td>
The language string identifier for the "Yes" button, must be provided in JSON encoded format.
Confirmation modals only.
</td>
</tr>
<tr>
<td>data-modal-toast</td>
<td>
If set to "true" it will display a modal toast in the end.
Confirmation modals only.
</td>
</tr>
<tr>
<td>data-modal-toast-confirmation-str</td>
<td>
The confirmation toast language string identifier, must be provided in JSON encoded format.
Confirmation modals only.
</td>
</tr>
<tr>
<td>data-modal-destination</td>
<td>
An url to redirect the user to.
Confirmation modals only.
</td>
</tr>
</tbody>
</table>
## Examples
### Basic Alert modal
{{< example >}}
<button type="button" class="btn btn-primary" data-modal="alert" data-modal-title-str='["cookiesenabled", "core"]'
data-modal-content-str='["cookiesenabled_help_html", "core"]'>Show alert modal</button>
{{< /example >}}
### Basic confirmation modal
{{< example >}}
<button type="button" class="btn btn-primary" data-modal="confirmation" data-modal-title-str='["delete", "core"]'
data-modal-content-str='["areyousure"]' data-modal-yes-button-str='["delete", "core"]'>Show confirmation modal</button>
{{< /example >}}
### Confirmation modal with a toast
{{< example >}}
<button type="button" class="btn btn-primary" data-modal="confirmation" data-modal-title-str='["delete", "core"]'
data-modal-content-str='["areyousure"]' data-modal-yes-button-str='["delete", "core"]' data-modal-toast="true"
data-modal-toast-confirmation-str='["deleteblockinprogress", "block", "Online users"]'>Show confirmation modal</button>
{{< /example >}}
### Confirmation modal with redirect
{{< example >}}
<button type="button" class="btn btn-primary" data-modal="confirmation" data-modal-title-str='["delete", "core"]'
data-modal-content-str='["areyousure"]' data-modal-yes-button-str='["delete", "core"]'
data-modal-destination="http://moodle.com">Show confirmation modal</button>
{{< /example >}}

View File

@ -24,27 +24,3 @@ Feature: Test the 'remember username' feature works.
And I click on "Log in" "link" in the ".logininfo" "css_element"
Then the field "username" matches value "teacher1"
And the field "Remember username" matches value "1"
# Given the user has logged in before and selected 'Remember username', when they log in again and unset 'Remember username', then
# their username should be forgotten for future log in attempts.
Scenario: Check that 'remember username' unsetting works without javascript for teachers.
# Log in the first time and check the 'remember username' box.
Given I am on homepage
And I click on "Log in" "link" in the ".logininfo" "css_element"
And I set the field "Username" to "teacher1"
And I set the field "Password" to "teacher1"
And I set the field "Remember username" to "1"
And I press "Log in"
And I log out
# Log in again, unsetting the 'remember username' field.
When I am on homepage
And I click on "Log in" "link" in the ".logininfo" "css_element"
And I set the field "Password" to "teacher1"
And I set the field "Remember username" to "0"
And I press "Log in"
And I log out
# Check username has been forgotten.
Then I am on homepage
And I click on "Log in" "link" in the ".logininfo" "css_element"
Then the field "username" matches value ""
And the field "Remember username" matches value "0"

View File

@ -299,10 +299,16 @@ $string['cookiesenabled_help'] = 'Two cookies are used on this site:
The essential one is the session cookie, usually called MoodleSession. You must allow this cookie in your browser to provide continuity and to remain logged in when browsing the site. When you log out or close the browser, this cookie is destroyed (in your browser and on the server).
The other cookie is purely for convenience, usually called MOODLEID or similar. It just remembers your username in the browser. This means that when you return to this site, the username field on the login page is already filled in for you. It is safe to refuse this cookie - you will just have to retype your username each time you log in.';
$string['cookiesenabled_help_html'] = 'Two cookies are used on this site:<br/><br/>
The essential one is the session cookie, usually called MoodleSession. You must allow this cookie in your browser to provide continuity and to remain logged in when browsing the site. When you log out or close the browser, this cookie is destroyed (in your browser and on the server).<br/><br/>
The other cookie is purely for convenience, usually called MOODLEID or similar. It just remembers your username in the browser. This means that when you return to this site, the username field on the login page is already filled in for you. It is safe to refuse this cookie - you will just have to retype your username each time you log in.';
$string['cookiesenabledonlysession'] = 'Cookies must be enabled in your browser';
$string['cookiesenabledonlysession_help'] = 'This site uses one session cookie, usually called MoodleSession. You must allow this cookie in your browser to provide continuity and to remain logged in when browsing the site. When you log out or close the browser, this cookie is destroyed (in your browser and on the server).';
$string['cookiesnotenabled'] = 'Unfortunately, cookies are currently not enabled in your browser';
$string['cookiesnotice'] = 'Cookies notice';
$string['copy'] = 'copy';
$string['copyasnoun'] = 'copy';
$string['copycourse'] = 'Copy course';
@ -2250,7 +2256,7 @@ $string['userlist'] = 'User list';
$string['usermenu'] = 'User menu';
$string['usermenugoback'] = 'Go back to user menu';
$string['username'] = 'Username';
$string['usernameemail'] = 'Username / email';
$string['usernameemail'] = 'Username or email';
$string['usernameemailmatch'] = 'The username and email address do not relate to the same user';
$string['usernameexists'] = 'This username already exists, choose another';
$string['usernamelowercase'] = 'Only lowercase letters allowed';

View File

@ -1,2 +0,0 @@
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core/confirm",["core/notification","core/str","core/toast"],function(a,b,c){"use strict";b=e(b);function d(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;d=function(){return a};return a}function e(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=d();if(b&&b.has(a)){return b.get(a)}var c={},e=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=e?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(c,f,g)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}var f=!1,g=function(a,c){if(a["confirmation".concat(c,"Str")]){return b.get_string.apply(null,JSON.parse(a["confirmation".concat(c,"Str")]))}return Promise.resolve(a["confirmation".concat(c)])},h=function(){document.addEventListener("click",function(b){var d=b.target.closest("[data-confirmation=\"modal\"]");if(d){b.preventDefault();(0,a.saveCancelPromise)(g(d.dataset,"Title"),g(d.dataset,"Question"),g(d.dataset,"YesButton")).then(function(){if("true"===d.dataset.confirmationToast){var b=g(d.dataset,"ToastConfirmation");if("string"==typeof b){(0,c.add)(b)}else{b.then(function(a){return(0,c.add)(a)}).catch(function(b){return(0,a.exception)(b)})}}window.location.href=d.dataset.confirmationDestination}).catch(function(){})}})};if(!f){h();f=!0}});
//# sourceMappingURL=confirm.min.js.map

File diff suppressed because one or more lines are too long

2
lib/amd/build/utility.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core/utility",["core/str","core/pending","core/toast","core/notification"],function(a,b,c,d){"use strict";a=f(a);b=function(a){return a&&a.__esModule?a:{default:a}}(b);var i="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function e(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;e=function(){return a};return a}function f(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=e();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=d?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(c,f,g)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}function g(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function h(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){g(i,d,e,f,h,"next",a)}function h(a){g(i,d,e,f,h,"throw",a)}f(void 0)})}}var j=!1,k=function(b,c,d){if(b["".concat(c).concat(d,"Str")]){return a.get_string.apply(null,JSON.parse(b["".concat(c).concat(d,"Str")]))}return Promise.resolve(b["".concat(c).concat(d)])},l=function(a,b){return(0,d.saveCancelPromise)(k(a.dataset,b,"Title"),k(a.dataset,b,"Content"),k(a.dataset,b,"YesButton")).then(function(){if("true"===a.dataset["".concat(b,"Toast")]){var e=k(a.dataset,b,"ToastConfirmation");if("string"==typeof e){(0,c.add)(e)}else{e.then(function(a){return(0,c.add)(a)}).catch(function(a){return(0,d.exception)(a)})}}window.location.href=a.dataset["".concat(b,"Destination")]}).catch(function(){})},m=function(){var a=h(regeneratorRuntime.mark(function a(c,d){var e,f;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:e=new b.default("core/confirm:alert");a.next=3;return"function"==typeof i.define&&i.define.amd?new Promise(function(a,b){i.require(["core/modal_factory"],a,b)}):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&i.require&&"component"===i.require.loader?Promise.resolve(require(("core/modal_factory"))):Promise.resolve(i["core/modal_factory"]);case 3:f=a.sent;return a.abrupt("return",f.create({type:f.types.ALERT,title:c,body:d,removeOnClose:!0}).then(function(a){a.show();e.resolve();return a}));case 5:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),n=function(){document.addEventListener("click",function(a){var b=a.target.closest("[data-confirmation=\"modal\"]");if(b){a.preventDefault();l(b,"confirmation")}var c=a.target.closest("[data-modal=\"confirmation\"]");if(c){a.preventDefault();l(c,"modal")}var d=a.target.closest("[data-modal=\"alert\"]");if(d){a.preventDefault();m(k(d.dataset,"modal","Title"),k(d.dataset,"modal","Content"))}})};if(!j){n();j=!0}});
//# sourceMappingURL=utility.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,106 +0,0 @@
// 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/>.
/**
* Javascript events for the `core_confirm` modal.
*
* @module core/confirm
* @copyright 2021 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*
* @example <caption>Calling the confirmation modal to delete a block</caption>
*
* // The following is an example of how to use this module via an indirect PHP call with a button.
*
* $controls[] = new action_menu_link_secondary(
* $deleteactionurl,
* new pix_icon('t/delete', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),
* $str,
* [
* 'class' => 'editing_delete',
* 'data-confirmation' => 'modal', // Needed so this module will pick it up in the click handler.
* 'data-confirmation-title-str' => json_encode(['deletecheck_modal', 'block']),
* 'data-confirmation-question-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),
* 'data-confirmation-yes-button-str' => json_encode(['delete', 'core']),
* 'data-confirmation-toast' => 'true', // Can be set to inform the user that their action was a success.
* 'data-confirmation-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),
* 'data-confirmation-destination' => $deleteconfirmationurl->out(false), // Where do you want to direct the user?
* ]
* );
*/
import {saveCancelPromise, exception} from 'core/notification';
import * as Str from 'core/str';
import {add as addToast} from 'core/toast';
// We want to ensure that we only initialize the listeners only once.
let registered = false;
/**
* Either fetch the string or return it from the dom node.
*
* @method getConfirmationString
* @private
* @param {HTMLElement} dataset The page element to fetch dataset items in
* @param {String} field The dataset field name to fetch the contents of
* @return {Promise}
*
*/
const getConfirmationString = (dataset, field) => {
if (dataset[`confirmation${field}Str`]) {
return Str.get_string.apply(null, JSON.parse(dataset[`confirmation${field}Str`]));
}
return Promise.resolve(dataset[`confirmation${field}`]);
};
/**
* Set up the listeners for the confirmation modal widget within the page.
*
* @method registerConfirmationListeners
* @private
*/
const registerConfirmationListeners = () => {
document.addEventListener('click', e => {
const confirmRequest = e.target.closest('[data-confirmation="modal"]');
if (confirmRequest) {
e.preventDefault();
saveCancelPromise(
getConfirmationString(confirmRequest.dataset, 'Title'),
getConfirmationString(confirmRequest.dataset, 'Question'),
getConfirmationString(confirmRequest.dataset, 'YesButton'),
)
.then(() => {
if (confirmRequest.dataset.confirmationToast === 'true') {
const stringForToast = getConfirmationString(confirmRequest.dataset, 'ToastConfirmation');
if (typeof stringForToast === "string") {
addToast(stringForToast);
} else {
stringForToast.then(str => addToast(str)).catch(e => exception(e));
}
}
window.location.href = confirmRequest.dataset.confirmationDestination;
return;
}).catch(() => {
return;
});
}
});
};
if (!registered) {
registerConfirmationListeners();
registered = true;
}

165
lib/amd/src/utility.js Normal file
View File

@ -0,0 +1,165 @@
// 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/>.
/**
* Javascript handling for HTML attributes. This module gets autoloaded on page load.
*
* With the appropriate HTML attributes, various functionalities defined in this module can be used such as a displaying
* an alert or a confirmation modal, etc.
*
* @module core/utility
* @copyright 2021 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*
* @example <caption>Calling the confirmation modal to delete a block</caption>
*
* // The following is an example of how to use this module via an indirect PHP call with a button.
*
* $controls[] = new action_menu_link_secondary(
* $deleteactionurl,
* new pix_icon('t/delete', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),
* $str,
* [
* 'class' => 'editing_delete',
* 'data-modal' => 'confirmation', // Needed so this module will pick it up in the click handler.
* 'data-modal-title-str' => json_encode(['deletecheck_modal', 'block']),
* 'data-modal-content-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),
* 'data-modal-yes-button-str' => json_encode(['delete', 'core']),
* 'data-modal-toast' => 'true', // Can be set to inform the user that their action was a success.
* 'data-modal-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),
* 'data-modal-destination' => $deleteconfirmationurl->out(false), // Where do you want to direct the user?
* ]
* );
*/
import * as Str from 'core/str';
import Pending from 'core/pending';
import {add as addToast} from 'core/toast';
import {saveCancelPromise, exception} from 'core/notification';
// We want to ensure that we only initialize the listeners only once.
let registered = false;
/**
* Either fetch the string or return it from the dom node.
*
* @method getConfirmationString
* @private
* @param {HTMLElement} dataset The page element to fetch dataset items in
* @param {String} type The type of string to fetch
* @param {String} field The dataset field name to fetch the contents of
* @return {Promise}
*
*/
const getModalString = (dataset, type, field) => {
if (dataset[`${type}${field}Str`]) {
return Str.get_string.apply(null, JSON.parse(dataset[`${type}${field}Str`]));
}
return Promise.resolve(dataset[`${type}${field}`]);
};
/**
* Display a save/cancel confirmation.
*
* @private
* @param {HTMLElement} source The title of the confirmation
* @param {String} type The content of the confirmation
* @returns {Promise}
*/
const displayConfirmation = (source, type) => {
return saveCancelPromise(
getModalString(source.dataset, type, 'Title'),
getModalString(source.dataset, type, 'Content'),
getModalString(source.dataset, type, 'YesButton'),
)
.then(() => {
if (source.dataset[`${type}Toast`] === 'true') {
const stringForToast = getModalString(source.dataset, type, 'ToastConfirmation');
if (typeof stringForToast === "string") {
addToast(stringForToast);
} else {
stringForToast.then(str => addToast(str)).catch(e => exception(e));
}
}
window.location.href = source.dataset[`${type}Destination`];
return;
}).catch(() => {
return;
});
};
/**
* Display an alert and return the promise from it.
*
* @private
* @param {String} title The title of the alert
* @param {String} content The content of the alert
* @returns {Promise}
*/
const displayAlert = async(title, content) => {
const pendingPromise = new Pending('core/confirm:alert');
const ModalFactory = await import('core/modal_factory');
return ModalFactory.create({
type: ModalFactory.types.ALERT,
title: title,
body: content,
removeOnClose: true,
})
.then(function(modal) {
modal.show();
pendingPromise.resolve();
return modal;
});
};
/**
* Set up the listeners for the confirmation modal widget within the page.
*
* @method registerConfirmationListeners
* @private
*/
const registerConfirmationListeners = () => {
document.addEventListener('click', e => {
const confirmRequest = e.target.closest('[data-confirmation="modal"]');
if (confirmRequest) {
e.preventDefault();
displayConfirmation(confirmRequest, 'confirmation');
}
const modalConfirmation = e.target.closest('[data-modal="confirmation"]');
if (modalConfirmation) {
e.preventDefault();
displayConfirmation(modalConfirmation, 'modal');
}
const alertRequest = e.target.closest('[data-modal="alert"]');
if (alertRequest) {
e.preventDefault();
displayAlert(
getModalString(alertRequest.dataset, 'modal', 'Title'),
getModalString(alertRequest.dataset, 'modal', 'Content'),
);
}
});
};
if (!registered) {
registerConfirmationListeners();
registered = true;
}

View File

@ -1416,13 +1416,13 @@ class block_manager {
$str,
[
'class' => 'editing_delete',
'data-confirmation' => 'modal',
'data-confirmation-title-str' => json_encode(['deletecheck_modal', 'block']),
'data-confirmation-question-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),
'data-confirmation-yes-button-str' => json_encode(['delete', 'core']),
'data-confirmation-toast' => 'true',
'data-confirmation-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),
'data-confirmation-destination' => $deleteconfirmationurl->out(false),
'data-modal' => 'confirmation',
'data-modal-title-str' => json_encode(['deletecheck_modal', 'block']),
'data-modal-content-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),
'data-modal-yes-button-str' => json_encode(['delete', 'core']),
'data-modal-toast' => 'true',
'data-modal-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),
'data-modal-destination' => $deleteconfirmationurl->out(false),
]
);
}

View File

@ -4753,12 +4753,6 @@ EOD;
$context = $form->export_for_template($this);
// Override because rendering is not supported in template yet.
if ($CFG->rememberusername == 0) {
$context->cookieshelpiconformatted = $this->help_icon('cookiesenabledonlysession');
} else {
$context->cookieshelpiconformatted = $this->help_icon('cookiesenabled');
}
$context->errorformatted = $this->error_text($context->error);
$url = $this->get_logo_url();
if ($url) {

View File

@ -1669,7 +1669,7 @@ EOF;
$this->js_call_amd('core/log', 'setConfig', array($logconfig));
// Add any global JS that needs to run on all pages.
$this->js_call_amd('core/page_global', 'init');
$this->js_call_amd('core/confirm');
$this->js_call_amd('core/utility');
// Call amd init functions.
$output .= $this->get_amd_footercode();

View File

@ -35,7 +35,6 @@
* loginurl - Login url,
* rememberusername - Remeber username?,
* signupurl - Signup url,
* cookieshelpiconformatted - Formatted html of cookies help icon,
* errorformatted - Formatted error,
* logourl - Flag, logo url,
* sitename - Name of site.,
@ -97,11 +96,6 @@
}}
<div class="loginform">
{{#languagemenu}}
<div class="login-languagemenu">
{{>core/action_menu}}
</div>
{{/languagemenu}}
{{#logourl}}
<div id="loginlogo" class="login-logo">
<img id="logoimage" src="{{logourl}}" class="img-fluid" alt="{{sitename}}"/>
@ -135,7 +129,7 @@
{{/canloginbyemail}}
</label>
<input type="text" name="username" id="username" {{!
!}}class="form-control" {{!
!}}class="form-control form-control-lg" {{!
!}}value="{{username}}" {{!
!}}placeholder="{{^canloginbyemail}}{{#cleanstr}}username{{/cleanstr}}{{/canloginbyemail}}{{!
!}}{{#canloginbyemail}}{{#cleanstr}}usernameemail{{/cleanstr}}{{/canloginbyemail}}" {{!
@ -144,24 +138,15 @@
<div class="login-form-password form-group">
<label for="password" class="sr-only">{{#str}} password {{/str}}</label>
<input type="password" name="password" id="password" value="" {{!
!}}class="form-control" {{!
!}}class="form-control form-control-lg" {{!
!}}placeholder="{{#cleanstr}}password{{/cleanstr}}" {{!
!}}autocomplete="current-password">
</div>
{{#rememberusername}}
<div class="login-form-rememberusername form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" name="rememberusername" id="rememberusername" class="custom-control-input" value="1"
{{#username}}checked{{/username}} />
<label class="custom-control-label" for="rememberusername">{{#str}} rememberusername, admin {{/str}}</label>
</div>
</div>
{{/rememberusername}}
<div class="login-form-submit form-group">
<button class="btn btn-primary btn-lg" type="submit" id="loginbtn">{{#str}}login{{/str}}</button>
</div>
<div class="login-form-forgotpassword form-group">
<a href="{{forgotpasswordurl}}">{{#str}}forgotten{{/str}}</a>
<a href="{{forgotpasswordurl}}">{{#str}}forgotaccount{{/str}}</a>
</div>
</form>
{{#hasidentityproviders}}
@ -169,7 +154,7 @@
<div class="login-identityproviders">
<h2 class="login-heading">{{#str}} potentialidps, auth {{/str}}</h2>
{{#identityproviders}}
<a class="btn login-identityprovider-btn" href="{{url}}">
<a class="btn login-identityprovider-btn btn-block" href="{{url}}">
{{#iconurl}}
<img src="{{iconurl}}" alt="" width="24" height="24"/>
{{/iconurl}}
@ -201,9 +186,18 @@
</form>
{{/canloginasguest}}
<div class="login-divider"></div>
<div class="login-cookiemessage">
{{#str}} cookiesenabled {{/str}}
{{{cookieshelpiconformatted}}}
<div class="d-flex">
{{#languagemenu}}
<div class="login-languagemenu">
{{>core/action_menu}}
</div>
<div class="divider border-left align-self-center mx-3"></div>
{{/languagemenu}}
<button type="button" class="btn btn-secondary" {{!
}} data-modal="alert"{{!
}} data-modal-title-str='["cookiesenabled", "core"]' {{!
}} data-modal-content-str='["cookiesenabled_help_html", "core"]'{{!
}}>{{#str}}cookiesnotice{{/str}}</button>
</div>
</div>

View File

@ -35,8 +35,7 @@ $login-identity-provider-btn-border: $border-color !default;
margin-bottom: 2rem;
.login-languagemenu {
display: flex;
justify-content: flex-end;
margin-bottom: 1rem;
justify-content: flex-start;
}
.login-logo {
display: flex;
@ -61,6 +60,16 @@ $login-identity-provider-btn-border: $border-color !default;
border: $border-width solid $login-identity-provider-btn-border;
}
}
.divider {
width: $border-width;
background-color: $gray-300;
height: $font-size-base * 2;
}
.action-menu-trigger {
a {
margin: 0.5rem 0;
}
}
}
@include media-breakpoint-up(md) {

View File

@ -17626,8 +17626,7 @@ textarea[data-auto-rows] {
margin-bottom: 2rem; }
.login-container .login-languagemenu {
display: flex;
justify-content: flex-end;
margin-bottom: 1rem; }
justify-content: flex-start; }
.login-container .login-logo {
display: flex;
justify-content: center;
@ -17642,6 +17641,12 @@ textarea[data-auto-rows] {
font-size: 1.40625rem; }
.login-container .login-identityproviders .login-identityprovider-btn {
border: 1px solid #dee2e6; }
.login-container .divider {
width: 1px;
background-color: #dee2e6;
height: 1.875rem; }
.login-container .action-menu-trigger a {
margin: 0.5rem 0; }
@media (min-width: 768px) {
.login-container {

View File

@ -17626,8 +17626,7 @@ textarea[data-auto-rows] {
margin-bottom: 2rem; }
.login-container .login-languagemenu {
display: flex;
justify-content: flex-end;
margin-bottom: 1rem; }
justify-content: flex-start; }
.login-container .login-logo {
display: flex;
justify-content: center;
@ -17642,6 +17641,12 @@ textarea[data-auto-rows] {
font-size: 1.40625rem; }
.login-container .login-identityproviders .login-identityprovider-btn {
border: 1px solid #dee2e6; }
.login-container .divider {
width: 1px;
background-color: #dee2e6;
height: 1.875rem; }
.login-container .action-menu-trigger a {
margin: 0.5rem 0; }
@media (min-width: 768px) {
.login-container {