MDL-59920 calendar: Allow user to choose if deleting one or all events
authorAndrew Nicols <andrew@nicols.co.uk>
Tue, 12 Sep 2017 07:46:18 +0000 (15:46 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Mon, 18 Sep 2017 03:48:51 +0000 (11:48 +0800)
12 files changed:
calendar/amd/build/events.min.js
calendar/amd/build/modal_delete.min.js [new file with mode: 0644]
calendar/amd/build/repository.min.js
calendar/amd/build/summary_modal.min.js
calendar/amd/src/events.js
calendar/amd/src/modal_delete.js [new file with mode: 0644]
calendar/amd/src/repository.js
calendar/amd/src/summary_modal.js
calendar/classes/external/event_exporter_base.php
calendar/templates/event_delete_modal.mustache [new file with mode: 0644]
calendar/templates/event_summary_body.mustache
lang/en/calendar.php

index 77b2153..99bf77b 100644 (file)
Binary files a/calendar/amd/build/events.min.js and b/calendar/amd/build/events.min.js differ
diff --git a/calendar/amd/build/modal_delete.min.js b/calendar/amd/build/modal_delete.min.js
new file mode 100644 (file)
index 0000000..78456c6
Binary files /dev/null and b/calendar/amd/build/modal_delete.min.js differ
index a33feed..aa194f4 100644 (file)
Binary files a/calendar/amd/build/repository.min.js and b/calendar/amd/build/repository.min.js differ
index 0a9f53a..fdca2a5 100644 (file)
Binary files a/calendar/amd/build/summary_modal.min.js and b/calendar/amd/build/summary_modal.min.js differ
index 29b5fd2..9134944 100644 (file)
@@ -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 (file)
index 0000000..9541dec
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+/**
+ * Contain the logic for the save/cancel modal.
+ *
+ * @module     core_calendar/modal_delete
+ * @class      modal_delete
+ * @package    core_calendar
+ * @copyright  2017 Andrew Nicols <andrew@nicols.co.uk>
+ * @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;
+});
index 55c9db9..cf91b01 100644 (file)
@@ -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,
                 }]
             }
         };
index da138c3..ff9317f 100644 (file)
  * @copyright  2017 Simey Lameze <simey@moodle.com>
  * @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
index 813cb58..eeffda0 100644 (file)
@@ -87,6 +87,7 @@ class event_exporter_base extends exporter {
 
         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 @@ class event_exporter_base extends exporter {
                 '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 (file)
index 0000000..5f33933
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @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}}
+        <button type="button" class="btn btn-primary" data-action="deleteone">{{#str}} deleteoneevent, core_calendar {{/str}}</button>
+        <button type="button" class="btn btn-secondary" data-action="deleteall">{{#str}} deleteallevents, core_calendar {{/str}}</button>
+        <button type="button" class="btn btn-secondary" data-action="cancel">{{#str}} cancel {{/str}}</button>
+    {{/footer}}
+{{/ core/modal }}
index 2ad48aa..70d207e 100644 (file)
         "groupname": "Group 1"
     }
 }}
-<div data-region="summary-modal-container" data-event-id="{{id}}" data-event-title="{{name}}"
-     data-action-event="{{isactionevent}}" data-edit-url="{{editurl}}">
+<div{{!
+    }} data-region="summary-modal-container"{{!
+    }} data-event-id="{{id}}"{{!
+    }} data-event-title="{{name}}"{{!
+    }} data-event-event-count="{{eventcount}}"{{!
+    }} data-event-="{{repeatid}}"{{!
+    }} data-action-event="{{isactionevent}}"{{!
+    }} data-edit-url="{{editurl}}"{{!
+    }}>
     <h4>{{#str}} when, core_calendar {{/str}}</h4>
     {{#userdate}} {{timestart}}, {{#str}} strftimerecentfull {{/str}} {{/userdate}}
     <br>
index 30a936c..dffa73a 100644 (file)
@@ -40,6 +40,7 @@ $string['colpoll'] = 'Update';
 $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['daywithnoevents'] = 'There are no events this day.';
 $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';