This commit is contained in:
Jun Pataleta
2022-02-11 22:28:11 +11:00
committed by Shamim Rezaie
21 changed files with 374 additions and 197 deletions
@@ -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 >}}
-5
View File
@@ -64,8 +64,6 @@ class login implements renderable, templatable {
public $instructions;
/** @var moodle_url The form action login URL. */
public $loginurl;
/** @var bool Whether the username should be remembered. */
public $rememberusername;
/** @var moodle_url The sign-up URL. */
public $signupurl;
/** @var string The user name to pre-fill the form with. */
@@ -96,10 +94,8 @@ class login implements renderable, templatable {
$this->cansignup = $CFG->registerauth == 'email' || !empty($CFG->registerauth);
if ($CFG->rememberusername == 0) {
$this->cookieshelpicon = new help_icon('cookiesenabledonlysession', 'core');
$this->rememberusername = false;
} else {
$this->cookieshelpicon = new help_icon('cookiesenabled', 'core');
$this->rememberusername = true;
}
$this->autofocusform = !empty($CFG->loginpageautofocus);
@@ -156,7 +152,6 @@ class login implements renderable, templatable {
list($data->instructions, $data->instructionsformat) = external_format_text($this->instructions, FORMAT_MOODLE,
context_system::instance()->id);
$data->loginurl = $this->loginurl->out(false);
$data->rememberusername = $this->rememberusername;
$data->signupurl = $this->signupurl->out(false);
$data->username = $this->username;
$data->logintoken = $this->logintoken;
-13
View File
@@ -40,19 +40,6 @@ Feature: Authentication
When I click on "Log out" "link" in the "#page-footer" "css_element"
Then I should see "You are not logged in" in the "page-footer" "region"
Scenario Outline: Checking the display of the Remember username checkbox
Given the following config values are set as admin:
| rememberusername | <settingvalue> |
And I am on homepage
When I click on "Log in" "link" in the ".logininfo" "css_element"
Then I should <expect> "Remember username"
Examples:
| settingvalue | expect |
| 0 | not see |
| 1 | see |
| 2 | see |
@javascript @accessibility
Scenario: Login page must be accessible
When I am on site homepage
-1
View File
@@ -89,7 +89,6 @@ Feature: Test if the login form provides the correct feedback
Then the focused element is "Username" "field"
And I set the field "Username" to "admin"
And I set the field "Password" to "admin"
And I set the field "Remember username" to "1"
And I press "Log in"
And I log out
And I follow "Log in"
+15 -13
View File
@@ -1,8 +1,8 @@
@core @core_auth
Feature: Test the 'remember username' feature works.
In order to see my saved username on the login form
In order for users to easily log in to the site
As a user
I need to have logged in once before and clicked 'Remember username'
I need the site to remember my username when the feature is enabled
Background:
Given the following "users" exist:
@@ -11,40 +11,42 @@ Feature: Test the 'remember username' feature works.
# Given the user has logged in and selected 'Remember username', when they log in again, then their username should be remembered.
Scenario: Check that 'remember username' works without javascript for teachers.
# Log in the first time and check the 'remember username' box.
Given I am on homepage
# Log in the first time with $CFG->rememberusername set to Yes.
Given the following config values are set as admin:
| rememberusername | 1 |
And 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 out and check that the username was remembered.
When I am on homepage
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
# Log in the first time with $CFG->rememberusername set to Optional.
Given the following config values are set as admin:
| rememberusername | 2 |
And 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.
# Log in again, the username should have been remembered.
When I am on homepage
And I click on "Log in" "link" in the ".logininfo" "css_element"
Then the field "username" matches value "teacher1"
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
And the following config values are set as admin:
| rememberusername | 0 |
# Check username has been forgotten.
Then I am on homepage
And 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"
-10
View File
@@ -77,16 +77,6 @@ class block_login extends block_base {
$this->content->text .= ' class="form-control" value="" autocomplete="current-password"/>';
$this->content->text .= '</div>';
if (isset($CFG->rememberusername) and $CFG->rememberusername == 2) {
$checked = $username ? 'checked="checked"' : '';
$this->content->text .= '<div class="form-check">';
$this->content->text .= '<label class="form-check-label">';
$this->content->text .= '<input type="checkbox" name="rememberusername" id="rememberusername"
class="form-check-input" value="1" '.$checked.'/> ';
$this->content->text .= get_string('rememberusername', 'admin').'</label>';
$this->content->text .= '</div>';
}
$this->content->text .= '<div class="form-group">';
$this->content->text .= '<input type="submit" class="btn btn-primary btn-block" value="'.get_string('login').'" />';
$this->content->text .= '</div>';
+7 -1
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';
-2
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
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
-106
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 <[email protected]>
* @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
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 <[email protected]>
* @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;
}
+7 -7
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),
]
);
}
-6
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) {
+1 -1
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();
+16 -24
View File
@@ -33,9 +33,7 @@
* instructions - Instructions,
* instructionsformat - Format of instructions,
* 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.,
@@ -84,7 +82,6 @@
"instructions": "For full access to this site, you first need to create an account.",
"instructionsformat": "1",
"loginurl": "http://localhost/stable_master/login/index.php",
"rememberusername": true,
"signupurl": "http://localhost/stable_master/login/signup.php",
"cookieshelpiconformatted": "",
"errorformatted": "",
@@ -97,11 +94,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 +127,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 +136,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 +152,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 +184,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>
+1 -1
View File
@@ -218,7 +218,7 @@ if ($frm and isset($frm->username)) { // Login WITH
// auth plugins can temporarily override this from loginpage_hook()
// do not save $CFG->nolastloggedin in database!
} else if (empty($CFG->rememberusername) or ($CFG->rememberusername == 2 and empty($frm->rememberusername))) {
} else if (empty($CFG->rememberusername)) {
// no permanent cookies, delete old one if exists
set_moodle_cookie('');
+11 -2
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) {
+7 -2
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 {
+7 -2
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 {