diff --git a/calendar/amd/build/events.min.js b/calendar/amd/build/events.min.js index 77b21536776a1..99bf77b661a91 100644 --- a/calendar/amd/build/events.min.js +++ b/calendar/amd/build/events.min.js @@ -1 +1 @@ -define([],function(){return{created:"calendar-events:created",deleted:"calendar-events:deleted",updated:"calendar-events:updated",editEvent:"calendar-events:edit_event",editActionEvent:"calendar-events:edit_action_event",eventMoved:"calendar-events:event_moved",monthChanged:"calendar-events:month_changed",moveEvent:"calendar-events:move_event",filterChanged:"calendar-events:filter_changed",viewUpdated:"calendar-events:view_updated"}}); \ No newline at end of file +define([],function(){return{created:"calendar-events:created",deleted:"calendar-events:deleted",deleteAll:"calendar-events:delete_all",updated:"calendar-events:updated",editEvent:"calendar-events:edit_event",editActionEvent:"calendar-events:edit_action_event",eventMoved:"calendar-events:event_moved",monthChanged:"calendar-events:month_changed",moveEvent:"calendar-events:move_event",filterChanged:"calendar-events:filter_changed",viewUpdated:"calendar-events:view_updated"}}); \ No newline at end of file diff --git a/calendar/amd/build/modal_delete.min.js b/calendar/amd/build/modal_delete.min.js new file mode 100644 index 0000000000000..78456c61e6c02 --- /dev/null +++ b/calendar/amd/build/modal_delete.min.js @@ -0,0 +1 @@ +define(["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_events","core/modal_registry","core_calendar/events"],function(a,b,c,d,e,f,g){var h=!1,i={DELETE_ONE_BUTTON:'[data-action="deleteone"]',DELETE_ALL_BUTTON:'[data-action="deleteall"]',CANCEL_BUTTON:'[data-action="cancel"]'},j=function(a){d.call(this,a)};return j.TYPE="core_calendar-modal_delete",j.prototype=Object.create(d.prototype),j.prototype.constructor=j,j.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this),this.getModal().on(c.events.activate,i.DELETE_ONE_BUTTON,function(b,c){var d=a.Event(e.save);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this)),this.getModal().on(c.events.activate,i.DELETE_ALL_BUTTON,function(b,c){var d=a.Event(g.deleteAll);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this)),this.getModal().on(c.events.activate,i.CANCEL_BUTTON,function(b,c){var d=a.Event(e.cancel);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this))},h||(f.register(j.TYPE,j,"calendar/event_delete_modal"),h=!0),j}); \ No newline at end of file diff --git a/calendar/amd/build/repository.min.js b/calendar/amd/build/repository.min.js index a33feeda93f8a..aa194f414f34d 100644 --- a/calendar/amd/build/repository.min.js +++ b/calendar/amd/build/repository.min.js @@ -1 +1 @@ -define(["jquery","core/ajax"],function(a,b){var c=function(a){var c={methodname:"core_calendar_delete_calendar_events",args:{events:[{eventid:a,repeat:1}]}};return b.call([c])[0]},d=function(a){var c={methodname:"core_calendar_get_calendar_event_by_id",args:{eventid:a}};return b.call([c])[0]},e=function(a){var c={methodname:"core_calendar_submit_create_update_form",args:{formdata:a}};return b.call([c])[0]},f=function(a,c){var d={methodname:"core_calendar_get_calendar_monthly_view",args:{time:a,courseid:c}};return b.call([d])[0]},g=function(a,c){var d={methodname:"core_calendar_update_event_start_day",args:{eventid:a,daytimestamp:c}};return b.call([d])[0]};return{getEventById:d,deleteEvent:c,updateEventStartDay:g,submitCreateUpdateForm:e,getCalendarMonthData:f}}); \ No newline at end of file +define(["jquery","core/ajax"],function(a,b){var c=function(a,c){"undefined"==typeof c&&(c=!1);var d={methodname:"core_calendar_delete_calendar_events",args:{events:[{eventid:a,repeat:c}]}};return b.call([d])[0]},d=function(a){var c={methodname:"core_calendar_get_calendar_event_by_id",args:{eventid:a}};return b.call([c])[0]},e=function(a){var c={methodname:"core_calendar_submit_create_update_form",args:{formdata:a}};return b.call([c])[0]},f=function(a,c){var d={methodname:"core_calendar_get_calendar_monthly_view",args:{time:a,courseid:c}};return b.call([d])[0]},g=function(a,c){var d={methodname:"core_calendar_update_event_start_day",args:{eventid:a,daytimestamp:c}};return b.call([d])[0]};return{getEventById:d,deleteEvent:c,updateEventStartDay:g,submitCreateUpdateForm:e,getCalendarMonthData:f}}); \ No newline at end of file diff --git a/calendar/amd/build/summary_modal.min.js b/calendar/amd/build/summary_modal.min.js index 0a9f53a95bbb8..fdca2a5a967e3 100644 --- a/calendar/amd/build/summary_modal.min.js +++ b/calendar/amd/build/summary_modal.min.js @@ -1 +1 @@ -define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/repository","core_calendar/events"],function(a,b,c,d,e,f,g,h,i,j){function k(d,e){var f=[{key:"deleteevent",component:"calendar"},{key:"confirmeventdelete",component:"calendar",param:e}],k=d.getEventId(),l=b.get_strings(f),m=g.create({type:g.types.SAVE_CANCEL},d.getDeleteButton());a.when(l,m).then(function(b,e){return e.setTitle(b[0]),e.setBody(b[1]),e.setSaveButtonText(b[0]),e.getRoot().on(h.save,function(){i.deleteEvent(k).then(function(){a("body").trigger(j.deleted,[k]),d.hide()})["catch"](c.exception)}),e}).fail(c.exception)}var l=!1,m={ROOT:"[data-region='summary-modal-container']",EDIT_BUTTON:'[data-action="edit"]',DELETE_BUTTON:'[data-action="delete"]'},n=function(a){e.call(this,a)};return n.TYPE="core_calendar-event_summary",n.prototype=Object.create(e.prototype),n.prototype.constructor=n,n.prototype.getEditButton=function(){return"undefined"==typeof this.editButton&&(this.editButton=this.getFooter().find(m.EDIT_BUTTON)),this.editButton},n.prototype.getDeleteButton=function(){return"undefined"==typeof this.deleteButton&&(this.deleteButton=this.getFooter().find(m.DELETE_BUTTON)),this.deleteButton},n.prototype.getEventId=function(){return this.getBody().find(m.ROOT).attr("data-event-id")},n.prototype.getEditUrl=function(){return this.getBody().find(m.ROOT).attr("data-edit-url")},n.prototype.isActionEvent=function(){return"true"==this.getBody().find(m.ROOT).attr("data-action-event")},n.prototype.registerEventListeners=function(){e.prototype.registerEventListeners.call(this),this.getRoot().on(h.bodyRendered,function(){var a=this.getBody().find(m.ROOT).attr("data-event-title");k(this,a)}.bind(this)),d.define(this.getEditButton(),[d.events.activate]),this.getEditButton().on(d.events.activate,function(b,c){this.isActionEvent()?a("body").trigger(j.editActionEvent,[this.getEditUrl()]):a("body").trigger(j.editEvent,[this.getEventId()]),this.hide(),b.preventDefault(),b.stopPropagation(),c.originalEvent.preventDefault(),c.originalEvent.stopPropagation()}.bind(this))},l||(f.register(n.TYPE,n,"core_calendar/event_summary_modal"),l=!0),n}); \ No newline at end of file +define(["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/repository","core_calendar/events","core_calendar/modal_delete"],function(a,b,c,d,e,f,g,h,i,j,k){function l(d,e){var f,l=[{key:"deleteevent",component:"calendar"}],m=parseInt(d.getEventCount(),10),n=m>1;n?(l.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:e,count:m}}),f=g.create({type:k.TYPE},d.getDeleteButton())):(l.push({key:"confirmeventdelete",component:"calendar",param:e}),f=g.create({type:g.types.SAVE_CANCEL},d.getDeleteButton()));var o=d.getEventId(),p=b.get_strings(l);a.when(p,f).then(function(b,e){return e.setTitle(b[0]),e.setBody(b[1]),n||e.setSaveButtonText(b[0]),e.getRoot().on(h.save,function(){i.deleteEvent(o,!1).then(function(){a("body").trigger(j.deleted,[o,!1]),d.hide()})["catch"](c.exception)}),e.getRoot().on(j.deleteAll,function(){i.deleteEvent(o,!0).then(function(){a("body").trigger(j.deleted,[o,!0]),d.hide()})["catch"](c.exception)}),e}).fail(c.exception)}var m=!1,n={ROOT:"[data-region='summary-modal-container']",EDIT_BUTTON:'[data-action="edit"]',DELETE_BUTTON:'[data-action="delete"]'},o=function(a){e.call(this,a)};return o.TYPE="core_calendar-event_summary",o.prototype=Object.create(e.prototype),o.prototype.constructor=o,o.prototype.getEditButton=function(){return"undefined"==typeof this.editButton&&(this.editButton=this.getFooter().find(n.EDIT_BUTTON)),this.editButton},o.prototype.getDeleteButton=function(){return"undefined"==typeof this.deleteButton&&(this.deleteButton=this.getFooter().find(n.DELETE_BUTTON)),this.deleteButton},o.prototype.getEventId=function(){return this.getBody().find(n.ROOT).attr("data-event-id")},o.prototype.getEventCount=function(){return this.getBody().find(n.ROOT).attr("data-event-event-count")},o.prototype.getEditUrl=function(){return this.getBody().find(n.ROOT).attr("data-edit-url")},o.prototype.isActionEvent=function(){return"true"==this.getBody().find(n.ROOT).attr("data-action-event")},o.prototype.registerEventListeners=function(){e.prototype.registerEventListeners.call(this),this.getRoot().on(h.bodyRendered,function(){var a=this.getBody().find(n.ROOT).attr("data-event-title");l(this,a)}.bind(this)),d.define(this.getEditButton(),[d.events.activate]),this.getEditButton().on(d.events.activate,function(b,c){this.isActionEvent()?a("body").trigger(j.editActionEvent,[this.getEditUrl()]):a("body").trigger(j.editEvent,[this.getEventId()]),this.hide(),b.preventDefault(),b.stopPropagation(),c.originalEvent.preventDefault(),c.originalEvent.stopPropagation()}.bind(this))},m||(f.register(o.TYPE,o,"core_calendar/event_summary_modal"),m=!0),o}); \ No newline at end of file diff --git a/calendar/amd/src/events.js b/calendar/amd/src/events.js index 29b5fd263cbc5..913494461908f 100644 --- a/calendar/amd/src/events.js +++ b/calendar/amd/src/events.js @@ -26,6 +26,7 @@ define([], function() { return { created: 'calendar-events:created', deleted: 'calendar-events:deleted', + deleteAll: 'calendar-events:delete_all', updated: 'calendar-events:updated', editEvent: 'calendar-events:edit_event', editActionEvent: 'calendar-events:edit_action_event', diff --git a/calendar/amd/src/modal_delete.js b/calendar/amd/src/modal_delete.js new file mode 100644 index 0000000000000..9541dec35e2db --- /dev/null +++ b/calendar/amd/src/modal_delete.js @@ -0,0 +1,112 @@ +// 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 . + +/** + * Contain the logic for the save/cancel modal. + * + * @module core_calendar/modal_delete + * @class modal_delete + * @package core_calendar + * @copyright 2017 Andrew Nicols + * @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', + 'core/modal_registry', + 'core_calendar/events', +], +function( + $, + Notification, + CustomEvents, + Modal, + ModalEvents, + ModalRegistry, + CalendarEvents +) { + + var registered = false; + var SELECTORS = { + DELETE_ONE_BUTTON: '[data-action="deleteone"]', + DELETE_ALL_BUTTON: '[data-action="deleteall"]', + CANCEL_BUTTON: '[data-action="cancel"]', + }; + + /** + * Constructor for the Modal. + * + * @param {object} root The root jQuery element for the modal + */ + var ModalDelete = function(root) { + Modal.call(this, root); + }; + + ModalDelete.TYPE = 'core_calendar-modal_delete'; + ModalDelete.prototype = Object.create(Modal.prototype); + ModalDelete.prototype.constructor = ModalDelete; + + /** + * Set up all of the event handling for the modal. + * + * @method registerEventListeners + */ + ModalDelete.prototype.registerEventListeners = function() { + // Apply parent event listeners. + Modal.prototype.registerEventListeners.call(this); + + this.getModal().on(CustomEvents.events.activate, SELECTORS.DELETE_ONE_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.DELETE_ALL_BUTTON, function(e, data) { + var saveEvent = $.Event(CalendarEvents.deleteAll); + 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)); + }; + + // Automatically register with the modal registry the first time this module is imported so that you can create modals + // of this type using the modal factory. + if (!registered) { + ModalRegistry.register(ModalDelete.TYPE, ModalDelete, 'calendar/event_delete_modal'); + registered = true; + } + + return ModalDelete; +}); diff --git a/calendar/amd/src/repository.js b/calendar/amd/src/repository.js index 55c9db927cca7..cf91b0124a688 100644 --- a/calendar/amd/src/repository.js +++ b/calendar/amd/src/repository.js @@ -29,16 +29,19 @@ define(['jquery', 'core/ajax'], function($, Ajax) { * * @method deleteEvent * @param {int} eventId The event id. + * @arapm {bool} deleteSeries Whether to delete all events in the series * @return {promise} Resolved with requested calendar event */ - var deleteEvent = function(eventId) { - + var deleteEvent = function(eventId, deleteSeries) { + if (typeof deleteSeries === 'undefined') { + deleteSeries = false; + } var request = { methodname: 'core_calendar_delete_calendar_events', args: { events: [{ eventid: eventId, - repeat: 1 + repeat: deleteSeries, }] } }; diff --git a/calendar/amd/src/summary_modal.js b/calendar/amd/src/summary_modal.js index da138c3101cba..ff9317f5e945b 100644 --- a/calendar/amd/src/summary_modal.js +++ b/calendar/amd/src/summary_modal.js @@ -21,11 +21,32 @@ * @copyright 2017 Simey Lameze * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_events', 'core/modal', - 'core/modal_registry', 'core/modal_factory', 'core/modal_events', 'core_calendar/repository', - 'core_calendar/events'], - function($, Str, Notification, CustomEvents, Modal, ModalRegistry, ModalFactory, ModalEvents, CalendarRepository, - CalendarEvents) { +define([ + 'jquery', + 'core/str', + 'core/notification', + 'core/custom_interaction_events', + 'core/modal', + 'core/modal_registry', + 'core/modal_factory', + 'core/modal_events', + 'core_calendar/repository', + 'core_calendar/events', + 'core_calendar/modal_delete', +], +function( + $, + Str, + Notification, + CustomEvents, + Modal, + ModalRegistry, + ModalFactory, + ModalEvents, + CalendarRepository, + CalendarEvents, + ModalDelete +) { var registered = false; var SELECTORS = { @@ -89,6 +110,18 @@ define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_even return this.getBody().find(SELECTORS.ROOT).attr('data-event-id'); }; + /** + * Get the number of events in the series for the event being shown in + * this modal. This value is not cached because it will change + * depending on which event is being displayed. + * + * @method getEventCount + * @return {int} + */ + ModalEventSummary.prototype.getEventCount = function() { + return this.getBody().find(SELECTORS.ROOT).attr('data-event-event-count'); + }; + /** * Get the url for the event being shown in this modal. * @@ -163,34 +196,76 @@ define(['jquery', 'core/str', 'core/notification', 'core/custom_interaction_even key: 'deleteevent', component: 'calendar' }, - { + ]; + + var eventCount = parseInt(summaryModal.getEventCount(), 10); + var deletePromise; + var isRepeatedEvent = eventCount > 1; + if (isRepeatedEvent) { + deleteStrings.push({ + key: 'confirmeventseriesdelete', + component: 'calendar', + param: { + name: eventTitle, + count: eventCount, + }, + }); + + deletePromise = ModalFactory.create( + { + type: ModalDelete.TYPE + }, + summaryModal.getDeleteButton() + ); + } else { + deleteStrings.push({ key: 'confirmeventdelete', component: 'calendar', param: eventTitle - } - ]; + }); + + deletePromise = ModalFactory.create( + { + type: ModalFactory.types.SAVE_CANCEL + }, + summaryModal.getDeleteButton() + ); + } + var eventId = summaryModal.getEventId(); var stringsPromise = Str.get_strings(deleteStrings); - var deletePromise = ModalFactory.create( - { - type: ModalFactory.types.SAVE_CANCEL - }, - summaryModal.getDeleteButton() - ); - $.when(stringsPromise, deletePromise).then(function(strings, deleteModal) { + $.when(stringsPromise, deletePromise) + .then(function(strings, deleteModal) { deleteModal.setTitle(strings[0]); deleteModal.setBody(strings[1]); - deleteModal.setSaveButtonText(strings[0]); + if (!isRepeatedEvent) { + deleteModal.setSaveButtonText(strings[0]); + } + deleteModal.getRoot().on(ModalEvents.save, function() { - CalendarRepository.deleteEvent(eventId).then(function() { - $('body').trigger(CalendarEvents.deleted, [eventId]); - summaryModal.hide(); - return; - }).catch(Notification.exception); + CalendarRepository.deleteEvent(eventId, false) + .then(function() { + $('body').trigger(CalendarEvents.deleted, [eventId, false]); + summaryModal.hide(); + return; + }) + .catch(Notification.exception); }); + + deleteModal.getRoot().on(CalendarEvents.deleteAll, function() { + CalendarRepository.deleteEvent(eventId, true) + .then(function() { + $('body').trigger(CalendarEvents.deleted, [eventId, true]); + summaryModal.hide(); + return; + }) + .catch(Notification.exception); + }); + return deleteModal; - }).fail(Notification.exception); + }) + .fail(Notification.exception); } // Automatically register with the modal registry the first time this module is imported so that you can create modals diff --git a/calendar/classes/external/event_exporter_base.php b/calendar/classes/external/event_exporter_base.php index 813cb588cc38c..eeffda067c705 100644 --- a/calendar/classes/external/event_exporter_base.php +++ b/calendar/classes/external/event_exporter_base.php @@ -87,6 +87,7 @@ public function __construct(event_interface $event, $related = []) { if ($repeats = $event->get_repeats()) { $data->repeatid = $repeats->get_id(); + $data->eventcount = $repeats->get_num() + 1; } if ($cm = $event->get_course_module()) { @@ -136,6 +137,12 @@ protected static function define_properties() { 'default' => null, 'null' => NULL_ALLOWED ], + 'eventcount' => [ + 'type' => PARAM_INT, + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED + ], 'modulename' => [ 'type' => PARAM_TEXT, 'optional' => true, diff --git a/calendar/templates/event_delete_modal.mustache b/calendar/templates/event_delete_modal.mustache new file mode 100644 index 0000000000000..5f33933a44f27 --- /dev/null +++ b/calendar/templates/event_delete_modal.mustache @@ -0,0 +1,47 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core_calendar/event_delete_modal + + Moodle modal template with save and cancel buttons. + + The purpose of this template is to render a modal. + + 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 delete event seriesmodal", + "body": "Do you want to delete this event, or all events in the series?" + } +}} + +{{< core/modal }} + {{$footer}} + + + + {{/footer}} +{{/ core/modal }} diff --git a/calendar/templates/event_summary_body.mustache b/calendar/templates/event_summary_body.mustache index 2ad48aa15492b..70d207e4f044a 100644 --- a/calendar/templates/event_summary_body.mustache +++ b/calendar/templates/event_summary_body.mustache @@ -28,8 +28,15 @@ "groupname": "Group 1" } }} -
+

{{#str}} when, core_calendar {{/str}}

{{#userdate}} {{timestart}}, {{#str}} strftimerecentfull {{/str}} {{/userdate}}
diff --git a/lang/en/calendar.php b/lang/en/calendar.php index 30a936c13873d..dffa73a3a15f9 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -40,6 +40,7 @@ $string['colactions'] = 'Actions'; $string['commontasks'] = 'Options'; $string['confirmeventdelete'] = 'Are you sure you want to delete the "{$a}" event?'; +$string['confirmeventseriesdelete'] = 'The "{$a->name}" event is part of a series. Do you want to delete just this event, or all {$a->count} events in the series?'; $string['course'] = 'Course'; $string['courseevent'] = 'Course event'; $string['courseevents'] = 'Course events'; @@ -52,6 +53,8 @@ $string['default'] = 'Default'; $string['deleteevent'] = 'Delete event'; $string['deleteevents'] = 'Delete events'; +$string['deleteoneevent'] = 'Delete this event'; +$string['deleteallevents'] = 'Delete all events'; $string['detailedmonthviewfor'] = 'Detailed month view for:'; $string['detailedmonthviewtitle'] = 'Detailed month view: {$a}'; $string['durationminutes'] = 'Duration in minutes';