From ecb92c7ed9f5d612214375ca2f9e600ae8cb10c7 Mon Sep 17 00:00:00 2001 From: Mathew May Date: Fri, 14 Jan 2022 16:06:30 +0800 Subject: [PATCH] MDL-72928 login: Update styling and functionality --- .../content/moodle/components/dom-modal.md | 134 ++++++++++++++ auth/tests/behat/rememberusername.feature | 24 --- lang/en/moodle.php | 8 +- lib/amd/build/confirm.min.js | 2 - lib/amd/build/confirm.min.js.map | 1 - lib/amd/build/utility.min.js | 2 + lib/amd/build/utility.min.js.map | 1 + lib/amd/src/confirm.js | 106 ----------- lib/amd/src/utility.js | 165 ++++++++++++++++++ lib/blocklib.php | 14 +- lib/outputrenderers.php | 6 - lib/outputrequirementslib.php | 2 +- lib/templates/loginform.mustache | 38 ++-- theme/boost/scss/moodle/login.scss | 13 +- theme/boost/style/moodle.css | 9 +- theme/classic/style/moodle.css | 9 +- 16 files changed, 358 insertions(+), 176 deletions(-) create mode 100644 admin/tool/componentlibrary/content/moodle/components/dom-modal.md delete mode 100644 lib/amd/build/confirm.min.js delete mode 100644 lib/amd/build/confirm.min.js.map create mode 100644 lib/amd/build/utility.min.js create mode 100644 lib/amd/build/utility.min.js.map delete mode 100644 lib/amd/src/confirm.js create mode 100644 lib/amd/src/utility.js diff --git a/admin/tool/componentlibrary/content/moodle/components/dom-modal.md b/admin/tool/componentlibrary/content/moodle/components/dom-modal.md new file mode 100644 index 00000000000..7d085c2604d --- /dev/null +++ b/admin/tool/componentlibrary/content/moodle/components/dom-modal.md @@ -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 >}} + +{{< /highlight >}} + +To display an alert modal. +{{< highlight html >}} + +{{< /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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Data attributeDescription
data-modalOne of either "confirmation", or "alert".
data-modal-title-strThe modal title language string identifier, must be provided in JSON encoded format.
data-modal-content-strThe modal content or content language string identifier, must be provided in JSON encoded format.
data-modal-yes-button-str + The language string identifier for the "Yes" button, must be provided in JSON encoded format. + Confirmation modals only. +
data-modal-toast + If set to "true" it will display a modal toast in the end. + Confirmation modals only. +
data-modal-toast-confirmation-str + The confirmation toast language string identifier, must be provided in JSON encoded format. + Confirmation modals only. +
data-modal-destination + An url to redirect the user to. + Confirmation modals only. +
+ +## Examples + +### Basic Alert modal + +{{< example >}} + +{{< /example >}} + +### Basic confirmation modal + +{{< example >}} + +{{< /example >}} + +### Confirmation modal with a toast + +{{< example >}} + +{{< /example >}} + +### Confirmation modal with redirect + +{{< example >}} + +{{< /example >}} diff --git a/auth/tests/behat/rememberusername.feature b/auth/tests/behat/rememberusername.feature index 754ce8fd217..b7d0388d921 100644 --- a/auth/tests/behat/rememberusername.feature +++ b/auth/tests/behat/rememberusername.feature @@ -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" diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 7de133931ce..05937463bfc 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -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:

+ +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['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'; diff --git a/lib/amd/build/confirm.min.js b/lib/amd/build/confirm.min.js deleted file mode 100644 index d13695ad3d8..00000000000 --- a/lib/amd/build/confirm.min.js +++ /dev/null @@ -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 diff --git a/lib/amd/build/confirm.min.js.map b/lib/amd/build/confirm.min.js.map deleted file mode 100644 index 389788fb0a2..00000000000 --- a/lib/amd/build/confirm.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/confirm.js"],"names":["registered","getConfirmationString","dataset","field","Str","get_string","apply","JSON","parse","Promise","resolve","registerConfirmationListeners","document","addEventListener","e","confirmRequest","target","closest","preventDefault","then","confirmationToast","stringForToast","str","catch","window","location","href","confirmationDestination"],"mappings":"qYA6CA,O,yiBAIIA,CAAAA,CAAU,G,CAYRC,CAAqB,CAAG,SAACC,CAAD,CAAUC,CAAV,CAAoB,CAC9C,GAAID,CAAO,uBAAgBC,CAAhB,QAAX,CAAwC,CACpC,MAAOC,CAAAA,CAAG,CAACC,UAAJ,CAAeC,KAAf,CAAqB,IAArB,CAA2BC,IAAI,CAACC,KAAL,CAAWN,CAAO,uBAAgBC,CAAhB,QAAlB,CAA3B,CACV,CACD,MAAOM,CAAAA,OAAO,CAACC,OAAR,CAAgBR,CAAO,uBAAgBC,CAAhB,EAAvB,CACV,C,CAQKQ,CAA6B,CAAG,UAAM,CACxCC,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAAAC,CAAC,CAAI,CACpC,GAAMC,CAAAA,CAAc,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiB,+BAAjB,CAAvB,CACA,GAAIF,CAAJ,CAAoB,CAChBD,CAAC,CAACI,cAAF,GACA,wBACIjB,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,OAAzB,CADzB,CAEID,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,UAAzB,CAFzB,CAGID,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,WAAzB,CAHzB,EAKCiB,IALD,CAKM,UAAM,CACR,GAAiD,MAA7C,GAAAJ,CAAc,CAACb,OAAf,CAAuBkB,iBAA3B,CAAyD,CACrD,GAAMC,CAAAA,CAAc,CAAGpB,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,mBAAzB,CAA5C,CACA,GAA8B,QAA1B,QAAOmB,CAAAA,CAAX,CAAwC,CACpC,UAASA,CAAT,CACH,CAFD,IAEO,CACHA,CAAc,CAACF,IAAf,CAAoB,SAAAG,CAAG,QAAI,UAASA,CAAT,CAAJ,CAAvB,EAA0CC,KAA1C,CAAgD,SAAAT,CAAC,QAAI,gBAAUA,CAAV,CAAJ,CAAjD,CACH,CACJ,CACDU,MAAM,CAACC,QAAP,CAAgBC,IAAhB,CAAuBX,CAAc,CAACb,OAAf,CAAuByB,uBAEjD,CAhBD,EAgBGJ,KAhBH,CAgBS,UAAM,CAEd,CAlBD,CAmBH,CACJ,CAxBD,CAyBH,C,CAED,GAAI,CAACvB,CAAL,CAAiB,CACbW,CAA6B,GAC7BX,CAAU,GACb,C","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 * Javascript events for the `core_confirm` modal.\n *\n * @module core/confirm\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n *\n * @example Calling the confirmation modal to delete a block\n *\n * // The following is an example of how to use this module via an indirect PHP call with a button.\n *\n * $controls[] = new action_menu_link_secondary(\n * $deleteactionurl,\n * new pix_icon('t/delete', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),\n * $str,\n * [\n * 'class' => 'editing_delete',\n * 'data-confirmation' => 'modal', // Needed so this module will pick it up in the click handler.\n * 'data-confirmation-title-str' => json_encode(['deletecheck_modal', 'block']),\n * 'data-confirmation-question-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),\n * 'data-confirmation-yes-button-str' => json_encode(['delete', 'core']),\n * 'data-confirmation-toast' => 'true', // Can be set to inform the user that their action was a success.\n * 'data-confirmation-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),\n * 'data-confirmation-destination' => $deleteconfirmationurl->out(false), // Where do you want to direct the user?\n * ]\n * );\n */\n\nimport {saveCancelPromise, exception} from 'core/notification';\nimport * as Str from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n// We want to ensure that we only initialize the listeners only once.\nlet registered = false;\n\n/**\n * Either fetch the string or return it from the dom node.\n *\n * @method getConfirmationString\n * @private\n * @param {HTMLElement} dataset The page element to fetch dataset items in\n * @param {String} field The dataset field name to fetch the contents of\n * @return {Promise}\n *\n */\nconst getConfirmationString = (dataset, field) => {\n if (dataset[`confirmation${field}Str`]) {\n return Str.get_string.apply(null, JSON.parse(dataset[`confirmation${field}Str`]));\n }\n return Promise.resolve(dataset[`confirmation${field}`]);\n};\n\n/**\n * Set up the listeners for the confirmation modal widget within the page.\n *\n * @method registerConfirmationListeners\n * @private\n */\nconst registerConfirmationListeners = () => {\n document.addEventListener('click', e => {\n const confirmRequest = e.target.closest('[data-confirmation=\"modal\"]');\n if (confirmRequest) {\n e.preventDefault();\n saveCancelPromise(\n getConfirmationString(confirmRequest.dataset, 'Title'),\n getConfirmationString(confirmRequest.dataset, 'Question'),\n getConfirmationString(confirmRequest.dataset, 'YesButton'),\n )\n .then(() => {\n if (confirmRequest.dataset.confirmationToast === 'true') {\n const stringForToast = getConfirmationString(confirmRequest.dataset, 'ToastConfirmation');\n if (typeof stringForToast === \"string\") {\n addToast(stringForToast);\n } else {\n stringForToast.then(str => addToast(str)).catch(e => exception(e));\n }\n }\n window.location.href = confirmRequest.dataset.confirmationDestination;\n return;\n }).catch(() => {\n return;\n });\n }\n });\n};\n\nif (!registered) {\n registerConfirmationListeners();\n registered = true;\n}\n"],"file":"confirm.min.js"} \ No newline at end of file diff --git a/lib/amd/build/utility.min.js b/lib/amd/build/utility.min.js new file mode 100644 index 00000000000..b5874641b6f --- /dev/null +++ b/lib/amd/build/utility.min.js @@ -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 diff --git a/lib/amd/build/utility.min.js.map b/lib/amd/build/utility.min.js.map new file mode 100644 index 00000000000..ca16257a4fb --- /dev/null +++ b/lib/amd/build/utility.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/utility.js"],"names":["registered","getModalString","dataset","type","field","Str","get_string","apply","JSON","parse","Promise","resolve","displayConfirmation","source","then","stringForToast","str","catch","e","window","location","href","displayAlert","title","content","pendingPromise","Pending","ModalFactory","create","types","ALERT","body","removeOnClose","modal","show","registerConfirmationListeners","document","addEventListener","confirmRequest","target","closest","preventDefault","modalConfirmation","alertRequest"],"mappings":"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","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 * Javascript handling for HTML attributes. This module gets autoloaded on page load.\n *\n * With the appropriate HTML attributes, various functionalities defined in this module can be used such as a displaying\n * an alert or a confirmation modal, etc.\n *\n * @module core/utility\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n *\n * @example Calling the confirmation modal to delete a block\n *\n * // The following is an example of how to use this module via an indirect PHP call with a button.\n *\n * $controls[] = new action_menu_link_secondary(\n * $deleteactionurl,\n * new pix_icon('t/delete', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),\n * $str,\n * [\n * 'class' => 'editing_delete',\n * 'data-modal' => 'confirmation', // Needed so this module will pick it up in the click handler.\n * 'data-modal-title-str' => json_encode(['deletecheck_modal', 'block']),\n * 'data-modal-content-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),\n * 'data-modal-yes-button-str' => json_encode(['delete', 'core']),\n * 'data-modal-toast' => 'true', // Can be set to inform the user that their action was a success.\n * 'data-modal-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),\n * 'data-modal-destination' => $deleteconfirmationurl->out(false), // Where do you want to direct the user?\n * ]\n * );\n */\n\nimport * as Str from 'core/str';\nimport Pending from 'core/pending';\nimport {add as addToast} from 'core/toast';\nimport {saveCancelPromise, exception} from 'core/notification';\n\n// We want to ensure that we only initialize the listeners only once.\nlet registered = false;\n\n/**\n * Either fetch the string or return it from the dom node.\n *\n * @method getConfirmationString\n * @private\n * @param {HTMLElement} dataset The page element to fetch dataset items in\n * @param {String} type The type of string to fetch\n * @param {String} field The dataset field name to fetch the contents of\n * @return {Promise}\n *\n */\nconst getModalString = (dataset, type, field) => {\n if (dataset[`${type}${field}Str`]) {\n return Str.get_string.apply(null, JSON.parse(dataset[`${type}${field}Str`]));\n }\n return Promise.resolve(dataset[`${type}${field}`]);\n};\n\n/**\n * Display a save/cancel confirmation.\n *\n * @private\n * @param {HTMLElement} source The title of the confirmation\n * @param {String} type The content of the confirmation\n * @returns {Promise}\n */\nconst displayConfirmation = (source, type) => {\n return saveCancelPromise(\n getModalString(source.dataset, type, 'Title'),\n getModalString(source.dataset, type, 'Content'),\n getModalString(source.dataset, type, 'YesButton'),\n )\n .then(() => {\n if (source.dataset[`${type}Toast`] === 'true') {\n const stringForToast = getModalString(source.dataset, type, 'ToastConfirmation');\n if (typeof stringForToast === \"string\") {\n addToast(stringForToast);\n } else {\n stringForToast.then(str => addToast(str)).catch(e => exception(e));\n }\n }\n window.location.href = source.dataset[`${type}Destination`];\n return;\n }).catch(() => {\n return;\n });\n};\n\n/**\n * Display an alert and return the promise from it.\n *\n * @private\n * @param {String} title The title of the alert\n * @param {String} content The content of the alert\n * @returns {Promise}\n */\nconst displayAlert = async(title, content) => {\n const pendingPromise = new Pending('core/confirm:alert');\n\n const ModalFactory = await import('core/modal_factory');\n\n return ModalFactory.create({\n type: ModalFactory.types.ALERT,\n title: title,\n body: content,\n removeOnClose: true,\n })\n .then(function(modal) {\n modal.show();\n pendingPromise.resolve();\n\n return modal;\n });\n};\n\n/**\n * Set up the listeners for the confirmation modal widget within the page.\n *\n * @method registerConfirmationListeners\n * @private\n */\nconst registerConfirmationListeners = () => {\n document.addEventListener('click', e => {\n const confirmRequest = e.target.closest('[data-confirmation=\"modal\"]');\n if (confirmRequest) {\n e.preventDefault();\n displayConfirmation(confirmRequest, 'confirmation');\n }\n\n const modalConfirmation = e.target.closest('[data-modal=\"confirmation\"]');\n if (modalConfirmation) {\n e.preventDefault();\n displayConfirmation(modalConfirmation, 'modal');\n }\n\n const alertRequest = e.target.closest('[data-modal=\"alert\"]');\n if (alertRequest) {\n e.preventDefault();\n displayAlert(\n getModalString(alertRequest.dataset, 'modal', 'Title'),\n getModalString(alertRequest.dataset, 'modal', 'Content'),\n );\n }\n });\n};\n\nif (!registered) {\n registerConfirmationListeners();\n registered = true;\n}\n"],"file":"utility.min.js"} \ No newline at end of file diff --git a/lib/amd/src/confirm.js b/lib/amd/src/confirm.js deleted file mode 100644 index 42991071234..00000000000 --- a/lib/amd/src/confirm.js +++ /dev/null @@ -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 . - -/** - * Javascript events for the `core_confirm` modal. - * - * @module core/confirm - * @copyright 2021 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since 4.0 - * - * @example Calling the confirmation modal to delete a block - * - * // 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; -} diff --git a/lib/amd/src/utility.js b/lib/amd/src/utility.js new file mode 100644 index 00000000000..66027496b44 --- /dev/null +++ b/lib/amd/src/utility.js @@ -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 . + +/** + * 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 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 4.0 + * + * @example Calling the confirmation modal to delete a block + * + * // 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; +} diff --git a/lib/blocklib.php b/lib/blocklib.php index 83e8ca7f356..81e84fe4c03 100644 --- a/lib/blocklib.php +++ b/lib/blocklib.php @@ -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), ] ); } diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index a4ff3c420b8..944bbe6edc4 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -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) { diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index afe8b9842b7..07428ae29b8 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -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(); diff --git a/lib/templates/loginform.mustache b/lib/templates/loginform.mustache index 82acc010fb7..f72af240dbd 100644 --- a/lib/templates/loginform.mustache +++ b/lib/templates/loginform.mustache @@ -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 @@ }}
- {{#languagemenu}} - - {{/languagemenu}} {{#logourl}} - {{#rememberusername}} - - {{/rememberusername}} {{#hasidentityproviders}} @@ -169,7 +154,7 @@