Merge branch 'MDL-68409-master' of git://github.com/andrewnicols/moodle

This commit is contained in:
Jun Pataleta 2020-05-04 15:22:12 +08:00
commit 716ac2c902
30 changed files with 546 additions and 391 deletions

View File

@ -1,2 +1,2 @@
define ("core_calendar/crud",["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(d,e,f){var i=[{key:"deleteevent",component:"calendar"}];f=parseInt(f,10);var m,o=1<f;if(o){i.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:f}});m=g.create({type:l.TYPE})}else{i.push({key:"confirmeventdelete",component:"calendar",param:e});m=g.create({type:g.types.SAVE_CANCEL})}var p=b.get_strings(i),q=a.when(p,m).then(function(b,e){e.setTitle(b[0]);e.setBody(b[1]);if(!o){e.setSaveButtonText(b[0])}e.show();e.getRoot().on(h.save,function(){var b=new n("calendar/crud:initModal:deletedevent");j.deleteEvent(d,!1).then(function(){a("body").trigger(k.deleted,[d,!1])}).then(b.resolve).catch(c.exception)});e.getRoot().on(k.deleteAll,function(){var b=new n("calendar/crud:initModal:deletedallevent");j.deleteEvent(d,!0).then(function(){a("body").trigger(k.deleted,[d,!0])}).then(b.resolve).catch(c.exception)});return e}).catch(c.exception);return q}return{registerRemove:function(b){b.on("click",m.actions.remove,function(b){var c=a(this).closest(m.eventItem),d=c.data("eventId"),e=c.data("eventTitle"),f=c.data("eventCount");o(d,e,f);b.preventDefault()})},registerEditListeners:function(b,d){d.then(function(c){a("body").on(k.editEvent,function(a,d){var e=b.find(m.wrapper);c.setEventId(d);c.setContextId(e.data("contextId"));c.show();a.stopImmediatePropagation()})}).fail(c.exception);return d},registerEventFormModal:function registerEventFormModal(b){var d=g.create({type:i.TYPE,large:!0});b.on("click",m.actions.create,function(a){d.then(function(a){var c=b.find(m.wrapper),d=c.data("categoryid");if("undefined"!=typeof d){a.setCategoryId(d)}var e=b.find(m.today),f=b.find(m.day);if(!e.length&&f.length){a.setStartTime(f.data("newEventTimestamp"))}a.setContextId(c.data("contextId"));a.setCourseId(c.data("courseid"));a.show()}).fail(c.exception);a.preventDefault()});b.on("click",m.actions.edit,function(b){b.preventDefault();var e=a(b.currentTarget),f=e.closest(m.wrapper),g=e.closest(m.eventItem);d.then(function(a){a.setEventId(g.data("eventId"));a.setContextId(f.data("contextId"));a.show();b.stopImmediatePropagation()}).fail(c.exception)});return d}}});
define ("core_calendar/crud",["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){function o(d,e,f){var i=new n("core_calendar/crud:confirmDeletion"),m=[{key:"deleteevent",component:"calendar"}];f=parseInt(f,10);var o,p=1<f;if(p){m.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:f}});o=g.create({type:l.TYPE})}else{m.push({key:"confirmeventdelete",component:"calendar",param:e});o=g.create({type:g.types.SAVE_CANCEL})}var q=b.get_strings(m),r=a.when(q,o).then(function(b,e){e.setTitle(b[0]);e.setBody(b[1]);if(!p){e.setSaveButtonText(b[0])}e.show();e.getRoot().on(h.save,function(){var b=new n("calendar/crud:initModal:deletedevent");j.deleteEvent(d,!1).then(function(){a("body").trigger(k.deleted,[d,!1])}).then(b.resolve).catch(c.exception)});e.getRoot().on(k.deleteAll,function(){var b=new n("calendar/crud:initModal:deletedallevent");j.deleteEvent(d,!0).then(function(){a("body").trigger(k.deleted,[d,!0])}).then(b.resolve).catch(c.exception)});return e}).then(function(a){i.resolve();return a}).catch(c.exception);return r}return{registerRemove:function(b){b.on("click",m.actions.remove,function(b){var c=a(this).closest(m.eventItem),d=c.data("eventId"),e=c.data("eventTitle"),f=c.data("eventCount");o(d,e,f);b.preventDefault()})},registerEditListeners:function(b,d){d.then(function(c){a("body").on(k.editEvent,function(a,d){var e=b.find(m.wrapper);c.setEventId(d);c.setContextId(e.data("contextId"));c.show();a.stopImmediatePropagation()})}).fail(c.exception);return d},registerEventFormModal:function registerEventFormModal(b){var d=g.create({type:i.TYPE,large:!0});b.on("click",m.actions.create,function(a){d.then(function(a){var c=b.find(m.wrapper),d=c.data("categoryid");if("undefined"!=typeof d){a.setCategoryId(d)}var e=b.find(m.today),f=b.find(m.day);if(!e.length&&f.length){a.setStartTime(f.data("newEventTimestamp"))}a.setContextId(c.data("contextId"));a.setCourseId(c.data("courseid"));a.show()}).fail(c.exception);a.preventDefault()});b.on("click",m.actions.edit,function(b){b.preventDefault();var e=a(b.currentTarget),f=e.closest(m.wrapper),g=e.closest(m.eventItem);d.then(function(a){a.setEventId(g.data("eventId"));a.setContextId(f.data("contextId"));a.show();b.stopImmediatePropagation()}).fail(c.exception)});return d}}});
//# sourceMappingURL=crud.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
define ("core_calendar/modal_delete",["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_events","core/modal_registry","core_calendar/events"],function(a,b,c,d,f,g,h){var i=!1,j={DELETE_ONE_BUTTON:"[data-action=\"deleteone\"]",DELETE_ALL_BUTTON:"[data-action=\"deleteall\"]",CANCEL_BUTTON:"[data-action=\"cancel\"]"},k=function(a){d.call(this,a)};k.TYPE="core_calendar-modal_delete";k.prototype=Object.create(d.prototype);k.prototype.constructor=k;k.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this);this.getModal().on(c.events.activate,j.DELETE_ONE_BUTTON,function(b,c){var d=a.Event(f.save);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this));this.getModal().on(c.events.activate,j.DELETE_ALL_BUTTON,function(b,c){var d=a.Event(h.deleteAll);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this));this.getModal().on(c.events.activate,j.CANCEL_BUTTON,function(b,c){var d=a.Event(f.cancel);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this))};if(!i){g.register(k.TYPE,k,"calendar/event_delete_modal");i=!0}return k});
define ("core_calendar/modal_delete",["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_events","core/modal_registry","core_calendar/events"],function(a,b,c,d,f,g,h){var i=!1,j={DELETE_ONE_BUTTON:"[data-action=\"deleteone\"]",DELETE_ALL_BUTTON:"[data-action=\"deleteall\"]",CANCEL_BUTTON:"[data-action=\"cancel\"]"},k=function(a){d.call(this,a);this.setRemoveOnClose(!0)};k.TYPE="core_calendar-modal_delete";k.prototype=Object.create(d.prototype);k.prototype.constructor=k;k.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this);this.getModal().on(c.events.activate,j.DELETE_ONE_BUTTON,function(b,c){var d=a.Event(f.save);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this));this.getModal().on(c.events.activate,j.DELETE_ALL_BUTTON,function(b,c){var d=a.Event(h.deleteAll);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this));this.getModal().on(c.events.activate,j.CANCEL_BUTTON,function(b,c){var d=a.Event(f.cancel);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this))};if(!i){g.register(k.TYPE,k,"calendar/event_delete_modal");i=!0}return k});
//# sourceMappingURL=modal_delete.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -63,6 +63,7 @@ function(
* @return {Promise}
*/
function confirmDeletion(eventId, eventTitle, eventCount) {
var pendingPromise = new Pending('core_calendar/crud:confirmDeletion');
var deleteStrings = [
{
key: 'deleteevent',
@ -139,6 +140,11 @@ function(
return deleteModal;
})
.then(function(modal) {
pendingPromise.resolve();
return modal;
})
.catch(Notification.exception);
return finalPromise;

View File

@ -55,6 +55,8 @@ function(
*/
var ModalDelete = function(root) {
Modal.call(this, root);
this.setRemoveOnClose(true);
};
ModalDelete.TYPE = 'core_calendar-modal_delete';

View File

@ -1499,6 +1499,7 @@ $string['numwords'] = '{$a} words';
$string['numyear'] = '{$a} year';
$string['numyears'] = '{$a} years';
$string['ok'] = 'OK';
$string['okay'] = 'Ok';
$string['oldpassword'] = 'Current password';
$string['olduserdirectory'] = 'This is the OLD users directory, and is no longer needed. You may safely delete it. The files it contains have been copied to the NEW user directory.';
$string['optional'] = 'optional';

View File

@ -0,0 +1,2 @@
define ("core/local/modal/alert",["exports","core/modal"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a){if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function f(a,b,c){if(b)e(a.prototype,b);if(c)e(a,c);return a}function g(a,b){if(b&&("object"===c(b)||"function"==typeof b)){return b}return h(a)}function h(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function i(a,b,c){if("undefined"!=typeof Reflect&&Reflect.get){i=Reflect.get}else{i=function(a,b,c){var d=j(a,b);if(!d)return;var e=Object.getOwnPropertyDescriptor(d,b);if(e.get){return e.get.call(c)}return e.value}}return i(a,b,c||a)}function j(a,b){while(!Object.prototype.hasOwnProperty.call(a,b)){a=k(a);if(null===a)break}return a}function k(a){k=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return k(a)}function l(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)m(a,b)}function m(a,b){m=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return m(a,b)}var n=function(a){l(b,a);function b(){d(this,b);return g(this,k(b).apply(this,arguments))}f(b,[{key:"registerEventListeners",value:function registerEventListeners(){i(k(b.prototype),"registerEventListeners",this).call(this);this.registerCloseOnCancel()}}]);return b}(b.default);a.default=n;return a.default});
//# sourceMappingURL=alert.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../../../src/local/modal/alert.js"],"names":["registerCloseOnCancel","Modal"],"mappings":"uJAyBA,uD,2rDAM6B,CAErB,2DAGA,KAAKA,qBAAL,EACH,C,cAVwBC,S","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 <http://www.gnu.org/licenses/>.\n\n/**\n * Alert modal.\n *\n * @module core/modal_alert\n * @class modal_alert\n * @package core\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\n\nexport default class extends Modal {\n /**\n * Register all event listeners.\n */\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on cancel.\n this.registerCloseOnCancel();\n }\n}\n"],"file":"alert.min.js"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
define ("core/modal_cancel",["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_events"],function(a,b,c,d,f){var g={CANCEL_BUTTON:"[data-action=\"cancel\"]"},h=function(a){d.call(this,a);if(!this.getFooter().find(g.CANCEL_BUTTON).length){b.exception({message:"No cancel button found"})}};h.prototype=Object.create(d.prototype);h.prototype.constructor=h;h.prototype.setFooter=function(){b.exception({message:"Can not change the footer of a cancel modal"})};h.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this);this.getModal().on(c.events.activate,g.CANCEL_BUTTON,function(b,c){var d=a.Event(f.cancel);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this))};return h});
define ("core/modal_cancel",["exports","core/modal"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a){if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function f(a,b,c){if(b)e(a.prototype,b);if(c)e(a,c);return a}function g(a,b){if(b&&("object"===c(b)||"function"==typeof b)){return b}return h(a)}function h(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function i(a,b,c){if("undefined"!=typeof Reflect&&Reflect.get){i=Reflect.get}else{i=function(a,b,c){var d=j(a,b);if(!d)return;var e=Object.getOwnPropertyDescriptor(d,b);if(e.get){return e.get.call(c)}return e.value}}return i(a,b,c||a)}function j(a,b){while(!Object.prototype.hasOwnProperty.call(a,b)){a=k(a);if(null===a)break}return a}function k(a){k=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return k(a)}function l(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)m(a,b)}function m(a,b){m=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return m(a,b)}var n=function(a){l(b,a);function b(a){var c;d(this,b);c=g(this,k(b).call(this,a));if(!c.getFooter().find(c.getActionSelector("cancel")).length){Notification.exception({message:"No cancel button found"})}return c}f(b,[{key:"registerEventListeners",value:function registerEventListeners(){i(k(b.prototype),"registerEventListeners",this).call(this);this.registerCloseOnCancel()}}]);return b}(b.default);a.default=n;return a.default});
//# sourceMappingURL=modal_cancel.min.js.map

View File

@ -1 +1 @@
{"version":3,"sources":["../src/modal_cancel.js"],"names":["define","$","Notification","CustomEvents","Modal","ModalEvents","SELECTORS","CANCEL_BUTTON","ModalCancel","root","call","getFooter","find","length","exception","message","prototype","Object","create","constructor","setFooter","registerEventListeners","getModal","on","events","activate","e","data","cancelEvent","Event","cancel","getRoot","trigger","isDefaultPrevented","hide","originalEvent","preventDefault","bind"],"mappings":"AAwBAA,OAAM,qBAAC,CAAC,QAAD,CAAW,mBAAX,CAAgC,gCAAhC,CAAkE,YAAlE,CAAgF,mBAAhF,CAAD,CACE,SAASC,CAAT,CAAYC,CAAZ,CAA0BC,CAA1B,CAAwCC,CAAxC,CAA+CC,CAA/C,CAA4D,IAE5DC,CAAAA,CAAS,CAAG,CACZC,aAAa,CAAE,0BADH,CAFgD,CAW5DC,CAAW,CAAG,SAASC,CAAT,CAAe,CAC7BL,CAAK,CAACM,IAAN,CAAW,IAAX,CAAiBD,CAAjB,EAEA,GAAI,CAAC,KAAKE,SAAL,GAAiBC,IAAjB,CAAsBN,CAAS,CAACC,aAAhC,EAA+CM,MAApD,CAA4D,CACxDX,CAAY,CAACY,SAAb,CAAuB,CAACC,OAAO,CAAE,wBAAV,CAAvB,CACH,CACJ,CAjB+D,CAmBhEP,CAAW,CAACQ,SAAZ,CAAwBC,MAAM,CAACC,MAAP,CAAcd,CAAK,CAACY,SAApB,CAAxB,CACAR,CAAW,CAACQ,SAAZ,CAAsBG,WAAtB,CAAoCX,CAApC,CAKAA,CAAW,CAACQ,SAAZ,CAAsBI,SAAtB,CAAkC,UAAW,CACzClB,CAAY,CAACY,SAAb,CAAuB,CAACC,OAAO,CAAE,6CAAV,CAAvB,CAEH,CAHD,CAUAP,CAAW,CAACQ,SAAZ,CAAsBK,sBAAtB,CAA+C,UAAW,CAEtDjB,CAAK,CAACY,SAAN,CAAgBK,sBAAhB,CAAuCX,IAAvC,CAA4C,IAA5C,EAEA,KAAKY,QAAL,GAAgBC,EAAhB,CAAmBpB,CAAY,CAACqB,MAAb,CAAoBC,QAAvC,CAAiDnB,CAAS,CAACC,aAA3D,CAA0E,SAASmB,CAAT,CAAYC,CAAZ,CAAkB,CACxF,GAAIC,CAAAA,CAAW,CAAG3B,CAAC,CAAC4B,KAAF,CAAQxB,CAAW,CAACyB,MAApB,CAAlB,CACA,KAAKC,OAAL,GAAeC,OAAf,CAAuBJ,CAAvB,CAAoC,IAApC,EAEA,GAAI,CAACA,CAAW,CAACK,kBAAZ,EAAL,CAAuC,CACnC,KAAKC,IAAL,GACAP,CAAI,CAACQ,aAAL,CAAmBC,cAAnB,EACH,CACJ,CARyE,CAQxEC,IARwE,CAQnE,IARmE,CAA1E,CASH,CAbD,CAeA,MAAO7B,CAAAA,CACV,CApDK,CAAN","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 <http://www.gnu.org/licenses/>.\n\n/**\n * Contain the logic for the cancel modal.\n *\n * @module core/modal_cancel\n * @class modal_cancel\n * @package core\n * @copyright 2016 Ryan Wyllie <ryan@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_events'],\n function($, Notification, CustomEvents, Modal, ModalEvents) {\n\n var SELECTORS = {\n CANCEL_BUTTON: '[data-action=\"cancel\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalCancel = function(root) {\n Modal.call(this, root);\n\n if (!this.getFooter().find(SELECTORS.CANCEL_BUTTON).length) {\n Notification.exception({message: 'No cancel button found'});\n }\n };\n\n ModalCancel.prototype = Object.create(Modal.prototype);\n ModalCancel.prototype.constructor = ModalCancel;\n\n /**\n * Override parent implementation to prevent changing the footer content.\n */\n ModalCancel.prototype.setFooter = function() {\n Notification.exception({message: 'Can not change the footer of a cancel modal'});\n return;\n };\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalCancel.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {\n var cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n };\n\n return ModalCancel;\n});\n"],"file":"modal_cancel.min.js"}
{"version":3,"sources":["../src/modal_cancel.js"],"names":["root","getFooter","find","getActionSelector","length","Notification","exception","message","registerCloseOnCancel","Modal"],"mappings":"kJAwBA,uD,gjDAGI,WAAYA,CAAZ,CAAkB,iBACd,wBAAMA,CAAN,GAEA,GAAI,CAAC,EAAKC,SAAL,GAAiBC,IAAjB,CAAsB,EAAKC,iBAAL,CAAuB,QAAvB,CAAtB,EAAwDC,MAA7D,CAAqE,CACjEC,YAAY,CAACC,SAAb,CAAuB,CAACC,OAAO,CAAE,wBAAV,CAAvB,CACH,CALa,QAMjB,C,0EAKwB,CAErB,2DAGA,KAAKC,qBAAL,EACH,C,cAlBwBC,S","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 <http://www.gnu.org/licenses/>.\n\n/**\n * Contain the logic for the cancel modal.\n *\n * @module core/modal_cancel\n * @class modal_cancel\n * @package core\n * @copyright 2016 Ryan Wyllie <ryan@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Modal from 'core/modal';\n\nexport default class extends Modal {\n constructor(root) {\n super(root);\n\n if (!this.getFooter().find(this.getActionSelector('cancel')).length) {\n Notification.exception({message: 'No cancel button found'});\n }\n }\n\n /**\n * Register all event listeners.\n */\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on cancel.\n this.registerCloseOnCancel();\n }\n}\n"],"file":"modal_cancel.min.js"}

View File

@ -1,2 +1,2 @@
define ("core/modal_factory",["jquery","core/modal_events","core/modal_registry","core/modal","core/modal_save_cancel","core/modal_cancel","core/templates","core/notification","core/custom_interaction_events","core/pending"],function(a,b,c,d,e,f,g,h,i,j){var k={DEFAULT:"core/modal",SAVE_CANCEL:"core/modal_save_cancel",CANCEL:"core/modal_cancel"},l={DEFAULT:"DEFAULT",SAVE_CANCEL:"SAVE_CANCEL",CANCEL:"CANCEL"};c.register(l.DEFAULT,d,k.DEFAULT);c.register(l.SAVE_CANCEL,e,k.SAVE_CANCEL);c.register(l.CANCEL,f,k.CANCEL);var m=function(c,d,e){var f=null,g="function"==typeof e.preShowCallback,h=function(b,d){var h=new j("core/modal_factory:setUpTrigger:triggeredCallback");f=a(b.currentTarget);c.then(function(a){if(g){e.preShowCallback(f,a)}a.show();return a}).then(h.resolve);d.originalEvent.preventDefault()};if(Array.isArray(d)){var k=d[1];d=d[0];i.define(d,[i.events.activate]);d.on(i.events.activate,k,h)}else{i.define(d,[i.events.activate]);d.on(i.events.activate,h)}c.then(function(a){a.getRoot().on(b.hidden,function(){if(null!==f){f.focus()}});return a})},n=function(b,c){c=a(c);var d=b.module,e=new d(c);return e},o=function(b,c){var d=b.template,e=g.render(d,c).then(function(c){var d=a(c);return n(b,d)}).fail(h.exception);return e};return{create:function create(a,b){var d=a.type||l.DEFAULT,e=a.large?!0:!1,f=null,g={};f=c.get(d);if(!f){h.exception({message:"Unable to find modal of type: "+d})}if("undefined"!=typeof a.templateContext){g=a.templateContext}var i=o(f,g).then(function(b){if("undefined"!=typeof a.title){b.setTitle(a.title)}if("undefined"!=typeof a.body){b.setBody(a.body)}if("undefined"!=typeof a.footer){b.setFooter(a.footer)}if(e){b.setLarge()}return b});if("undefined"!=typeof b){m(i,b,a)}return i},types:l}});
function _slicedToArray(a,b){return _arrayWithHoles(a)||_iterableToArrayLimit(a,b)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function _iterableToArrayLimit(a,b){var c=[],d=!0,e=!1,f=void 0;try{for(var g=a[Symbol.iterator](),h;!(d=(h=g.next()).done);d=!0){c.push(h.value);if(b&&c.length===b)break}}catch(a){e=!0;f=a}finally{try{if(!d&&null!=g["return"])g["return"]()}finally{if(e)throw f}}return c}function _arrayWithHoles(a){if(Array.isArray(a))return a}define ("core/modal_factory",["jquery","core/modal_events","core/modal_registry","core/modal","core/modal_save_cancel","core/modal_cancel","core/local/modal/alert","core/templates","core/notification","core/custom_interaction_events","core/pending"],function(a,b,c,d,e,f,g,h,i,j,k){var l={DEFAULT:"core/modal",SAVE_CANCEL:"core/modal_save_cancel",CANCEL:"core/modal_cancel",ALERT:"core/local/modal/alert"},m={DEFAULT:"DEFAULT",SAVE_CANCEL:"SAVE_CANCEL",CANCEL:"CANCEL",ALERT:"ALERT"};c.register(m.DEFAULT,d,l.DEFAULT);c.register(m.SAVE_CANCEL,e,l.SAVE_CANCEL);c.register(m.CANCEL,f,l.CANCEL);c.register(m.ALERT,g,l.ALERT);var n=function(c,d,e){var f=null,g="function"==typeof e.preShowCallback,h=function(b,d){var h=new k("core/modal_factory:setUpTrigger:triggeredCallback");f=a(b.currentTarget);c.then(function(a){if(g){e.preShowCallback(f,a)}a.show();return a}).then(h.resolve);d.originalEvent.preventDefault()};if(Array.isArray(d)){var i=d[1];d=d[0];j.define(d,[j.events.activate]);d.on(j.events.activate,i,h)}else{j.define(d,[j.events.activate]);d.on(j.events.activate,h)}c.then(function(a){a.getRoot().on(b.hidden,function(){if(null!==f){f.focus()}});return a})},o=function(b,c){c=a(c);var d=b.module,e=new d(c);return e},p=function(b,c){var d=b.template,e=h.render(d,c).then(function(c){var d=a(c);return o(b,d)}).fail(i.exception);return e};return{create:function create(a,b){var d=a.type||m.DEFAULT,e=a.large?!0:!1,f=null,g={};f=c.get(d);if(!f){i.exception({message:"Unable to find modal of type: "+d})}if("undefined"!=typeof a.templateContext){g=a.templateContext}var h=p(f,g).then(function(b){if("undefined"!=typeof a.title){b.setTitle(a.title)}if("undefined"!=typeof a.body){b.setBody(a.body)}if("undefined"!=typeof a.footer){b.setFooter(a.footer)}if(a.buttons){Object.entries(a.buttons).forEach(function(a){var c=_slicedToArray(a,2),d=c[0],e=c[1];b.setButtonText(d,e)})}if(e){b.setLarge()}if("undefined"!=typeof a.removeOnClose){b.setRemoveOnClose(a.removeOnClose)}return b});if("undefined"!=typeof b){n(h,b,a)}return h},types:m}});
//# sourceMappingURL=modal_factory.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
define ("core/modal_save_cancel",["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_events"],function(a,b,c,d,f){var g={SAVE_BUTTON:"[data-action=\"save\"]",CANCEL_BUTTON:"[data-action=\"cancel\"]"},h=function(a){d.call(this,a);if(!this.getFooter().find(g.SAVE_BUTTON).length){b.exception({message:"No save button found"})}if(!this.getFooter().find(g.CANCEL_BUTTON).length){b.exception({message:"No cancel button found"})}};h.prototype=Object.create(d.prototype);h.prototype.constructor=h;h.prototype.setFooter=function(){b.exception({message:"Can not change the footer of a save cancel modal"})};h.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this);this.getModal().on(c.events.activate,g.SAVE_BUTTON,function(b,c){var d=a.Event(f.save);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this));this.getModal().on(c.events.activate,g.CANCEL_BUTTON,function(b,c){var d=a.Event(f.cancel);this.getRoot().trigger(d,this);if(!d.isDefaultPrevented()){this.hide();c.originalEvent.preventDefault()}}.bind(this))};h.prototype.setSaveButtonText=function(a){var b=this.getFooter().find(g.SAVE_BUTTON);this.asyncSet(a,b.text.bind(b))};return h});
define ("core/modal_save_cancel",["exports","core/modal"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a){if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function f(a,b,c){if(b)e(a.prototype,b);if(c)e(a,c);return a}function g(a,b){if(b&&("object"===c(b)||"function"==typeof b)){return b}return h(a)}function h(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function i(a,b,c){if("undefined"!=typeof Reflect&&Reflect.get){i=Reflect.get}else{i=function(a,b,c){var d=j(a,b);if(!d)return;var e=Object.getOwnPropertyDescriptor(d,b);if(e.get){return e.get.call(c)}return e.value}}return i(a,b,c||a)}function j(a,b){while(!Object.prototype.hasOwnProperty.call(a,b)){a=k(a);if(null===a)break}return a}function k(a){k=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return k(a)}function l(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)m(a,b)}function m(a,b){m=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return m(a,b)}var n=function(a){l(b,a);function b(a){var c;d(this,b);c=g(this,k(b).call(this,a));if(!c.getFooter().find(c.getActionSelector("save")).length){Notification.exception({message:"No save button found"})}if(!c.getFooter().find(c.getActionSelector("cancel")).length){Notification.exception({message:"No cancel button found"})}return c}f(b,[{key:"registerEventListeners",value:function registerEventListeners(){i(k(b.prototype),"registerEventListeners",this).call(this);this.registerCloseOnSave();this.registerCloseOnCancel()}},{key:"setFooter",value:function setFooter(){Notification.exception({message:"Can not change the footer of a save cancel modal"})}},{key:"setSaveButtonText",value:function setSaveButtonText(a){return this.setButtonText("save",a)}}]);return b}(b.default);a.default=n;return a.default});
//# sourceMappingURL=modal_save_cancel.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
// 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/>.
/**
* Alert modal.
*
* @module core/modal_alert
* @class modal_alert
* @package core
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Modal from 'core/modal';
export default class extends Modal {
/**
* Register all event listeners.
*/
registerEventListeners() {
// Call the parent registration.
super.registerEventListeners();
// Register to close on cancel.
this.registerCloseOnCancel();
}
}

View File

@ -610,10 +610,11 @@ define([
* already been.
*
* @method show
* @returns {Promise}
*/
Modal.prototype.show = function() {
if (this.isVisible()) {
return;
return $.Deferred().resolve();
}
var pendingPromise = new Pending('core/modal:show');
@ -628,7 +629,7 @@ define([
this.attachToDOM();
}
this.getBackdrop()
return this.getBackdrop()
.then(function(backdrop) {
var currentIndex = this.calculateZIndex();
var newIndex = currentIndex + 2;
@ -699,6 +700,7 @@ define([
* @method destroy
*/
Modal.prototype.destroy = function() {
this.hide();
this.root.remove();
this.root.trigger(ModalEvents.destroyed, this);
};
@ -802,6 +804,52 @@ define([
}.bind(this));
};
/**
* Register a listener to close the dialogue when the cancel button is pressed.
*
* @method registerCloseOnCancel
*/
Modal.prototype.registerCloseOnCancel = function() {
// Handle the clicking of the Cancel button.
this.getModal().on(CustomEvents.events.activate, this.getActionSelector('cancel'), function(e, data) {
var cancelEvent = $.Event(ModalEvents.cancel);
this.getRoot().trigger(cancelEvent, this);
if (!cancelEvent.isDefaultPrevented()) {
data.originalEvent.preventDefault();
if (this.removeOnClose) {
this.destroy();
} else {
this.hide();
}
}
}.bind(this));
};
/**
* Register a listener to close the dialogue when the save button is pressed.
*
* @method registerCloseOnSave
*/
Modal.prototype.registerCloseOnSave = function() {
// Handle the clicking of the Cancel button.
this.getModal().on(CustomEvents.events.activate, this.getActionSelector('save'), function(e, data) {
var saveEvent = $.Event(ModalEvents.save);
this.getRoot().trigger(saveEvent, this);
if (!saveEvent.isDefaultPrevented()) {
data.originalEvent.preventDefault();
if (this.removeOnClose) {
this.destroy();
} else {
this.hide();
}
}
}.bind(this));
};
/**
* Set or resolve and set the value using the function.
*
@ -827,5 +875,44 @@ define([
return p;
};
/**
* Set the title text of a button.
*
* This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with
* text most commonly from a Str.get_string call.
*
* @param {DOMString} action The action of the button
* @param {(String|object)} value The button text, or a promise which will resolve to it
* @returns {Promise}
*/
Modal.prototype.setButtonText = function(action, value) {
const button = this.getFooter().find(this.getActionSelector(action));
if (!button) {
throw new Error("Unable to find the '" + action + "' button");
}
return this.asyncSet(value, button.text.bind(button));
};
/**
* Get the Selector for an action.
*
* @param {String} action
* @returns {DOMString}
*/
Modal.prototype.getActionSelector = function(action) {
return "[data-action='" + action + "']";
};
/**
* Set the flag to remove the modal from the DOM on close.
*
* @param {Boolean} remove
*/
Modal.prototype.setRemoveOnClose = function(remove) {
this.removeOnClose = remove;
};
return Modal;
});

View File

@ -22,56 +22,25 @@
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_events'],
function($, Notification, CustomEvents, Modal, ModalEvents) {
import Modal from 'core/modal';
var SELECTORS = {
CANCEL_BUTTON: '[data-action="cancel"]',
};
export default class extends Modal {
constructor(root) {
super(root);
/**
* Constructor for the Modal.
*
* @param {object} root The root jQuery element for the modal
*/
var ModalCancel = function(root) {
Modal.call(this, root);
if (!this.getFooter().find(SELECTORS.CANCEL_BUTTON).length) {
if (!this.getFooter().find(this.getActionSelector('cancel')).length) {
Notification.exception({message: 'No cancel button found'});
}
};
ModalCancel.prototype = Object.create(Modal.prototype);
ModalCancel.prototype.constructor = ModalCancel;
}
/**
* Override parent implementation to prevent changing the footer content.
* Register all event listeners.
*/
ModalCancel.prototype.setFooter = function() {
Notification.exception({message: 'Can not change the footer of a cancel modal'});
return;
};
registerEventListeners() {
// Call the parent registration.
super.registerEventListeners();
/**
* Set up all of the event handling for the modal.
*
* @method registerEventListeners
*/
ModalCancel.prototype.registerEventListeners = function() {
// Apply parent event listeners.
Modal.prototype.registerEventListeners.call(this);
this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {
var cancelEvent = $.Event(ModalEvents.cancel);
this.getRoot().trigger(cancelEvent, this);
if (!cancelEvent.isDefaultPrevented()) {
this.hide();
data.originalEvent.preventDefault();
}
}.bind(this));
};
return ModalCancel;
});
// Register to close on cancel.
this.registerCloseOnCancel();
}
}

View File

@ -23,17 +23,18 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
'core/modal_save_cancel', 'core/modal_cancel',
'core/modal_save_cancel', 'core/modal_cancel', 'core/local/modal/alert',
'core/templates', 'core/notification', 'core/custom_interaction_events',
'core/pending'],
function($, ModalEvents, ModalRegistry, Modal, ModalSaveCancel,
ModalCancel, Templates, Notification, CustomEvents, Pending) {
ModalCancel, ModalAlert, Templates, Notification, CustomEvents, Pending) {
// The templates for each type of modal.
var TEMPLATES = {
DEFAULT: 'core/modal',
SAVE_CANCEL: 'core/modal_save_cancel',
CANCEL: 'core/modal_cancel',
ALERT: 'core/local/modal/alert',
};
// The available types of modals.
@ -41,12 +42,14 @@ define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
DEFAULT: 'DEFAULT',
SAVE_CANCEL: 'SAVE_CANCEL',
CANCEL: 'CANCEL',
ALERT: 'ALERT',
};
// Register the common set of modals.
ModalRegistry.register(TYPES.DEFAULT, Modal, TEMPLATES.DEFAULT);
ModalRegistry.register(TYPES.SAVE_CANCEL, ModalSaveCancel, TEMPLATES.SAVE_CANCEL);
ModalRegistry.register(TYPES.CANCEL, ModalCancel, TEMPLATES.CANCEL);
ModalRegistry.register(TYPES.ALERT, ModalAlert, TEMPLATES.ALERT);
/**
* Set up the events required to show the modal and return focus when the modal
@ -185,10 +188,21 @@ define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
modal.setFooter(modalConfig.footer);
}
if (modalConfig.buttons) {
Object.entries(modalConfig.buttons).forEach(function([key, value]) {
modal.setButtonText(key, value);
});
}
if (isLarge) {
modal.setLarge();
}
if (typeof modalConfig.removeOnClose !== 'undefined') {
// If configured remove the modal when hiding it.
modal.setRemoveOnClose(modalConfig.removeOnClose);
}
return modal;
});

View File

@ -22,85 +22,48 @@
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_events'],
function($, Notification, CustomEvents, Modal, ModalEvents) {
import Modal from 'core/modal';
var SELECTORS = {
SAVE_BUTTON: '[data-action="save"]',
CANCEL_BUTTON: '[data-action="cancel"]',
};
export default class extends Modal {
constructor(root) {
super(root);
/**
* Constructor for the Modal.
*
* @param {object} root The root jQuery element for the modal
*/
var ModalSaveCancel = function(root) {
Modal.call(this, root);
if (!this.getFooter().find(SELECTORS.SAVE_BUTTON).length) {
if (!this.getFooter().find(this.getActionSelector('save')).length) {
Notification.exception({message: 'No save button found'});
}
if (!this.getFooter().find(SELECTORS.CANCEL_BUTTON).length) {
if (!this.getFooter().find(this.getActionSelector('cancel')).length) {
Notification.exception({message: 'No cancel button found'});
}
};
}
ModalSaveCancel.prototype = Object.create(Modal.prototype);
ModalSaveCancel.prototype.constructor = ModalSaveCancel;
/**
* Register all event listeners.
*/
registerEventListeners() {
// Call the parent registration.
super.registerEventListeners();
// Register to close on save/cancel.
this.registerCloseOnSave();
this.registerCloseOnCancel();
}
/**
* Override parent implementation to prevent changing the footer content.
*/
ModalSaveCancel.prototype.setFooter = function() {
setFooter() {
Notification.exception({message: 'Can not change the footer of a save cancel modal'});
return;
};
}
/**
* Set up all of the event handling for the modal.
* Set the title of the save button.
*
* @method registerEventListeners
* @param {String|Promise} value The button text, or a Promise which will resolve it
* @returns{Promise}
*/
ModalSaveCancel.prototype.registerEventListeners = function() {
// Apply parent event listeners.
Modal.prototype.registerEventListeners.call(this);
this.getModal().on(CustomEvents.events.activate, SELECTORS.SAVE_BUTTON, function(e, data) {
var saveEvent = $.Event(ModalEvents.save);
this.getRoot().trigger(saveEvent, this);
if (!saveEvent.isDefaultPrevented()) {
this.hide();
data.originalEvent.preventDefault();
}
}.bind(this));
this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {
var cancelEvent = $.Event(ModalEvents.cancel);
this.getRoot().trigger(cancelEvent, this);
if (!cancelEvent.isDefaultPrevented()) {
this.hide();
data.originalEvent.preventDefault();
}
}.bind(this));
};
/**
* Allows to overwrite the text of "Save changes" button.
*
* This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with
* text most commonly from a Str.get_string call.
*
* @param {(String|object)} value The button text, or a jQuery promise which will resolve it
*/
ModalSaveCancel.prototype.setSaveButtonText = function(value) {
var button = this.getFooter().find(SELECTORS.SAVE_BUTTON);
this.asyncSet(value, button.text.bind(button));
};
return ModalSaveCancel;
});
setSaveButtonText(value) {
return this.setButtonText('save', value);
}
}

View File

@ -13,283 +13,306 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A system for displaying notifications to users from the session.
*
* Wrapper for the YUI M.core.notification class. Allows us to
* use the YUI version in AMD code until it is replaced.
*
* @module core/notification
* @class notification
* @package core
* @copyright 2015 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
define(['core/yui', 'jquery', 'core/log', 'core/pending'],
function(Y, $, log, Pending) {
var notificationModule = {
types: {
'success': 'core/notification_success',
'info': 'core/notification_info',
'warning': 'core/notification_warning',
'error': 'core/notification_error',
},
import Pending from 'core/pending';
import Log from 'core/log';
fieldName: 'user-notifications',
let currentContextId = M.cfg.contextid;
fetchNotifications: function() {
var pendingPromise = new Pending('core/notification:fetchNotifications');
const notificationTypes = {
success: 'core/notification_success',
info: 'core/notification_info',
warning: 'core/notification_warning',
error: 'core/notification_error',
};
require(['core/ajax'], function(ajax) {
var promises = ajax.call([{
methodname: 'core_fetch_notifications',
args: {
contextid: notificationModule.contextid
}
}]);
const notificationRegionId = 'user-notifications';
// This currently fails when not logged in.
// eslint-disable-next-line promise/catch-or-return
promises[0]
.then(notificationModule.addNotifications)
.always(pendingPromise.resolve);
});
},
const Selectors = {
notificationRegion: `#${notificationRegionId}`,
fallbackRegionParents: [
'#region-main',
'[role="main"]',
'body',
],
};
addNotifications: function(notifications) {
var pendingPromise = new Pending('core/notification:addNotifications');
const setupTargetRegion = () => {
let targetRegion = getNotificationRegion();
if (targetRegion) {
return false;
}
if (!notifications) {
notifications = [];
}
const newRegion = document.createElement('span');
newRegion.id = notificationRegionId;
$.each(notifications, function(i, notification) {
notificationModule.renderNotification(notification.template, notification.variables);
});
return Selectors.fallbackRegionParents.some(selector => {
const targetRegion = document.querySelector(selector);
pendingPromise.resolve();
},
setupTargetRegion: function() {
var targetRegion = $('#' + notificationModule.fieldName);
if (targetRegion.length) {
return false;
}
var newRegion = $('<span>').attr('id', notificationModule.fieldName);
targetRegion = $('#region-main');
if (targetRegion.length) {
return targetRegion.prepend(newRegion);
}
targetRegion = $('[role="main"]');
if (targetRegion.length) {
return targetRegion.prepend(newRegion);
}
targetRegion = $('body');
return targetRegion.prepend(newRegion);
},
addNotification: function(notification) {
var pendingPromise = new Pending('core/notification:addNotifications');
var template = notificationModule.types.error;
notification = $.extend({
closebutton: true,
announce: true,
type: 'error'
}, notification);
if (notification.template) {
template = notification.template;
delete notification.template;
} else if (notification.type) {
if (typeof notificationModule.types[notification.type] !== 'undefined') {
template = notificationModule.types[notification.type];
}
delete notification.type;
}
pendingPromise.resolve();
return notificationModule.renderNotification(template, notification);
},
renderNotification: function(template, variables) {
if (typeof variables.message === 'undefined' || !variables.message) {
log.debug('Notification received without content. Skipping.');
return;
}
var pendingPromise = new Pending('core/notification:renderNotification');
require(['core/templates'], function(templates) {
templates.render(template, variables)
.then(function(html, js) {
$('#' + notificationModule.fieldName).prepend(html);
templates.runTemplateJS(js);
return;
})
.always(pendingPromise.resolve)
.catch(notificationModule.exception);
});
},
alert: function(title, message, yesLabel) {
var pendingPromise = new Pending('core/notification:alert');
// Here we are wrapping YUI. This allows us to start transitioning, but
// wait for a good alternative without having inconsistent dialogues.
Y.use('moodle-core-notification-alert', function() {
var alert = new M.core.alert({
title: title,
message: message,
yesLabel: yesLabel
});
alert.show();
pendingPromise.resolve();
});
},
confirm: function(title, question, yesLabel, noLabel, yesCallback, noCallback) {
var pendingPromise = new Pending('core/notification:confirm');
// Here we are wrapping YUI. This allows us to start transitioning, but
// wait for a good alternative without having inconsistent dialogues.
Y.use('moodle-core-notification-confirm', function() {
var modal = new M.core.confirm({
title: title,
question: question,
yesLabel: yesLabel,
noLabel: noLabel
});
modal.on('complete-yes', function() {
yesCallback();
});
if (noCallback) {
modal.on('complete-no', function() {
noCallback();
});
}
modal.show();
pendingPromise.resolve();
});
},
exception: function(ex) {
var pendingPromise = new Pending('core/notification:addNotifications');
// Fudge some parameters.
if (typeof ex.stack == 'undefined') {
ex.stack = '';
}
if (ex.debuginfo) {
ex.stack += ex.debuginfo + '\n';
}
if (!ex.backtrace && ex.stacktrace) {
ex.backtrace = ex.stacktrace;
}
if (ex.backtrace) {
ex.stack += ex.backtrace;
var ln = ex.backtrace.match(/line ([^ ]*) of/);
var fn = ex.backtrace.match(/ of ([^:]*): /);
if (ln && ln[1]) {
ex.lineNumber = ln[1];
}
if (fn && fn[1]) {
ex.fileName = fn[1];
if (ex.fileName.length > 30) {
ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 27);
}
}
}
if (typeof ex.name == 'undefined' && ex.errorcode) {
ex.name = ex.errorcode;
}
Y.use('moodle-core-notification-exception', function() {
var modal = new M.core.exception(ex);
modal.show();
pendingPromise.resolve();
});
if (targetRegion) {
targetRegion.prepend(newRegion);
return true;
}
return false;
});
};
/**
* Poll the server for any new notifications.
*
* @returns {Promise}
*/
export const fetchNotifications = async() => {
const Ajax = await import('core/ajax');
return Ajax.call([{
methodname: 'core_fetch_notifications',
args: {
contextid: currentContextId
}
}])[0]
.then(addNotifications);
};
/**
* Add all of the supplied notifications.
*
* @param {Array} notifications The list of notificaitons
* @returns {Promise}
*/
const addNotifications = notifications => {
if (!notifications.length) {
return Promise.resolve();
}
const pendingPromise = new Pending('core/notification:addNotifications');
notifications.forEach(notification => renderNotification(notification.template, notification.variables));
return pendingPromise.resolve();
};
/**
* Add a notification to the page.
*
* Note: This does not cause the notification to be added to the session.
*
* @param {Object} notification The notification to add.
* @param {string} notification.message The body of the notification
* @param {string} notification.type The type of notification to add (error, warning, info, success).
* @param {Boolean} notification.closebutton Whether to show the close button.
* @param {Boolean} notification.announce Whether to announce to screen readers.
* @returns {Promise}
*/
export const addNotification = notification => {
const pendingPromise = new Pending('core/notification:addNotifications');
let template = notificationTypes.error;
notification = {
closebutton: true,
announce: true,
type: 'error',
...notification,
};
return /** @alias module:core/notification */{
init: function(contextid, notifications) {
notificationModule.contextid = contextid;
if (notification.template) {
template = notification.template;
delete notification.template;
} else if (notification.type) {
if (typeof notificationTypes[notification.type] !== 'undefined') {
template = notificationTypes[notification.type];
}
delete notification.type;
}
// Setup the message target region if it isn't setup already
notificationModule.setupTargetRegion();
return renderNotification(template, notification)
.then(pendingPromise.resolve);
};
// Add provided notifications.
notificationModule.addNotifications(notifications);
const renderNotification = async(template, variables) => {
if (typeof variables.message === 'undefined' || !variables.message) {
Log.debug('Notification received without content. Skipping.');
return;
}
// Poll for any new notifications.
notificationModule.fetchNotifications();
const pendingPromise = new Pending('core/notification:renderNotification');
const Templates = await import('core/templates');
Templates.renderForPromise(template, variables)
.then(({html, js = ''}) => {
Templates.prependNodeContents(getNotificationRegion(), html, js);
return;
})
.then(pendingPromise.resolve)
.catch(exception);
};
const getNotificationRegion = () => document.querySelector(Selectors.notificationRegion);
/**
* Alert dialogue.
*
* @param {String|Promise} title
* @param {String|Promise} message
* @param {String|Promise} cancelText
* @returns {Promise}
*/
export const alert = async(title, message, cancelText) => {
var pendingPromise = new Pending('core/notification:alert');
const ModalFactory = await import('core/modal_factory');
return ModalFactory.create({
type: ModalFactory.types.ALERT,
body: message,
title: title,
buttons: {
cancel: cancelText,
},
removeOnClose: true,
})
.then(function(modal) {
modal.show();
/**
* Poll the server for any new notifications.
*
* @method fetchNotifications
*/
fetchNotifications: notificationModule.fetchNotifications,
pendingPromise.resolve();
return modal;
});
};
/**
* Add a notification to the page.
*
* Note: This does not cause the notification to be added to the session.
*
* @method addNotification
* @param {Object} notification The notification to add.
* @param {string} notification.message The body of the notification
* @param {string} notification.type The type of notification to add (error, warning, info, success).
* @param {Boolean} notification.closebutton Whether to show the close button.
* @param {Boolean} notification.announce Whether to announce to screen readers.
*/
addNotification: notificationModule.addNotification,
/**
* The confirm has now been replaced with a save and cancel dialogue.
*
* @param {String|Promise} title
* @param {String|Promise} question
* @param {String|Promise} saveLabel
* @param {String|Promise} noLabel
* @param {String|Promise} saveCallback
* @param {String|Promise} cancelCallback
* @returns {Promise}
*/
export const confirm = (title, question, saveLabel, noLabel, saveCallback, cancelCallback) =>
saveCancel(title, question, saveLabel, saveCallback, cancelCallback);
/**
* Wrap M.core.alert.
*
* @method alert
* @param {string} title
* @param {string} message
* @param {string} yesLabel
*/
alert: notificationModule.alert,
/**
* The Save and Cancel dialogue helper.
*
* @param {String|Promise} title
* @param {String|Promise} question
* @param {String|Promise} saveLabel
* @param {String|Promise} saveCallback
* @param {String|Promise} cancelCallback
* @returns {Promise}
*/
export const saveCancel = async(title, question, saveLabel, saveCallback, cancelCallback) => {
const pendingPromise = new Pending('core/notification:confirm');
/**
* Wrap M.core.confirm.
*
* @method confirm
* @param {string} title
* @param {string} question
* @param {string} yesLabel
* @param {string} noLabel
* @param {function} yesCallback
* @param {function} noCallback Optional parameter to be called if the user presses cancel.
*/
confirm: notificationModule.confirm,
const [
ModalFactory,
ModalEvents,
] = await Promise.all([
import('core/modal_factory'),
import('core/modal_events'),
]);
/**
* Wrap M.core.exception.
*
* @method exception
* @param {Error} ex
*/
exception: notificationModule.exception
};
});
return ModalFactory.create({
type: ModalFactory.types.SAVE_CANCEL,
title: title,
body: question,
buttons: {
// Note: The noLabel is no longer supported.
save: saveLabel,
},
removeOnClose: true,
})
.then(function(modal) {
modal.show();
modal.getRoot().on(ModalEvents.save, saveCallback);
modal.getRoot().on(ModalEvents.cancel, cancelCallback);
pendingPromise.resolve();
return modal;
});
};
/**
* Wrap M.core.exception.
*
* @param {Error} ex
*/
export const exception = async ex => {
const pendingPromise = new Pending('core/notification:displayException');
// Fudge some parameters.
if (!ex.stack) {
ex.stack = '';
}
if (ex.debuginfo) {
ex.stack += ex.debuginfo + '\n';
}
if (!ex.backtrace && ex.stacktrace) {
ex.backtrace = ex.stacktrace;
}
if (ex.backtrace) {
ex.stack += ex.backtrace;
const ln = ex.backtrace.match(/line ([^ ]*) of/);
const fn = ex.backtrace.match(/ of ([^:]*): /);
if (ln && ln[1]) {
ex.lineNumber = ln[1];
}
if (fn && fn[1]) {
ex.fileName = fn[1];
if (ex.fileName.length > 30) {
ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 27);
}
}
}
if (typeof ex.name === 'undefined' && ex.errorcode) {
ex.name = ex.errorcode;
}
const Y = await import('core/yui');
Y.use('moodle-core-notification-exception', function() {
var modal = new M.core.exception(ex);
modal.show();
pendingPromise.resolve();
});
};
/**
* Initialise the page for the suppled context, and displaying the supplied notifications.
*
* @param {Number} contextId
* @param {Array} notificationList
*/
export const init = (contextId, notificationList) => {
currentContextId = contextId;
// Setup the message target region if it isn't setup already
setupTargetRegion();
// Add provided notifications.
addNotifications(notificationList);
// Perform an initial poll for any new notifications.
fetchNotifications();
};
// To maintain backwards compatability we export default here.
export default {
init,
fetchNotifications,
addNotification,
alert,
confirm,
saveCancel,
exception,
};

View File

@ -0,0 +1,45 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/local/modal/alert
Moodle modal template with one oaky button.
The purpose of this template is to render an alert modal with an acceptance option.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* title A cleaned string (use clean_text()) to display.
* body HTML content for the boday
Example context (json):
{
"title": "Example cancel modal",
"body": "Some example content for the body"
}
}}
{{< core/modal }}
{{$footer}}
<button type="button" class="btn btn-primary" data-action="cancel">{{#str}}okay, moodle{{/str}}</button>
{{/footer}}
{{/ core/modal }}

View File

@ -49,6 +49,9 @@ information provided here is intended especially for developers.
* Implement a more direct xsendfile_file() method for an alternative_file_system_class
* A new `dynamic` table interface has been defined, which allows any `flexible_table` to be converted into a table which
is updatable via ajax calls. See MDL-68495 and `\core_table\dynamic` for further information.
* The core/notification module has been updated to use AMD modals for its confirmation and alert dialogues.
The confirmation dialogue no longer has a configurable "No" button as per similar changes in MDL-59759.
This set of confirmation modals was unintentionally missed from that deprecation process.
=== 3.8 ===
* Add CLI option to notify all cron tasks to stop: admin/cli/cron.php --stop

View File

@ -56,7 +56,7 @@ Feature: In an assignment, teacher can view the feedback for a previous attempt.
And I should see "The changes to the grade and feedback were saved"
And I press "Ok"
And I follow "View a different attempt"
And I click on "//div[contains(@class, 'moodle-dialogue-bd')]//label[2]" "xpath_element"
And I click on "Attempt 1" "radio" in the "View a different attempt" "dialogue"
And I press "View"
And I wait until the page is ready
And I should see "You are editing the feedback for a previous attempt. This is attempt 1 out of 2."

View File

@ -60,7 +60,7 @@ Feature: In an assignment, teachers can edit feedback for a students previous su
And I navigate to "View all submissions" in current page administration
And I click on "Grade" "link" in the "Student 2" "table_row"
And I click on "View a different attempt" "link"
And I click on "//div[contains(concat(' ', normalize-space(@class), ' '), ' confirmation-dialogue ')]//input[@value='0']" "xpath_element"
And I click on "Attempt 1" "radio" in the "View a different attempt" "dialogue"
And I click on "View" "button"
And I set the following fields to these values:
| Grade | 50 |

View File

@ -53,7 +53,7 @@ Feature: Manager is able to delete tags
And I follow "Default collection"
And I click on "Delete" "link" in the "Turtle" "table_row"
Then I should see "Are you sure you want to delete this tag?"
And I press "No"
And I click on "Cancel" "button" in the "Delete" "dialogue"
And I should not see "Tag(s) deleted"
And I should see "Turtle"
And I click on "Delete" "link" in the "Dog" "table_row"
@ -81,7 +81,7 @@ Feature: Manager is able to delete tags
| Select tag Cat | 1 |
And I press "Delete selected"
And I should see "Are you sure you want to delete selected tags?"
And I press "No"
And I click on "Cancel" "button" in the "Delete" "dialogue"
And I should not see "Tag(s) deleted"
And I should see "Cat"
And I set the following fields to these values: