MDL-59934 calendar: remove more button from event form modal
[moodle.git] / calendar / amd / src / modal_event_form.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * Contain the logic for the quick add or update event modal.
18  *
19  * @module     calendar/modal_quick_add_event
20  * @class      modal_quick_add_event
21  * @package    core
22  * @copyright  2017 Ryan Wyllie <ryan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
25 define([
26             'jquery',
27             'core/event',
28             'core/str',
29             'core/notification',
30             'core/templates',
31             'core/custom_interaction_events',
32             'core/modal',
33             'core/modal_registry',
34             'core/fragment',
35             'core_calendar/events',
36             'core_calendar/repository'
37         ],
38         function(
39             $,
40             Event,
41             Str,
42             Notification,
43             Templates,
44             CustomEvents,
45             Modal,
46             ModalRegistry,
47             Fragment,
48             CalendarEvents,
49             Repository
50         ) {
52     var registered = false;
53     var SELECTORS = {
54         SAVE_BUTTON: '[data-action="save"]',
55         LOADING_ICON_CONTAINER: '[data-region="loading-icon-container"]',
56     };
58     /**
59      * Constructor for the Modal.
60      *
61      * @param {object} root The root jQuery element for the modal
62      */
63     var ModalEventForm = function(root) {
64         Modal.call(this, root);
65         this.eventId = null;
66         this.startTime = null;
67         this.courseId = null;
68         this.reloadingBody = false;
69         this.reloadingTitle = false;
70         this.saveButton = this.getFooter().find(SELECTORS.SAVE_BUTTON);
71     };
73     ModalEventForm.TYPE = 'core_calendar-modal_event_form';
74     ModalEventForm.prototype = Object.create(Modal.prototype);
75     ModalEventForm.prototype.constructor = ModalEventForm;
77     /**
78      * Set the course id to the given value.
79      *
80      * @method setCourseId
81      * @param {int} id The event id
82      */
83     ModalEventForm.prototype.setCourseId = function(id) {
84         this.courseId = id;
85     };
87     /**
88      * Retrieve the current course id, if any.
89      *
90      * @method getCourseId
91      * @return {int|null} The event id
92      */
93     ModalEventForm.prototype.getCourseId = function() {
94         return this.courseId;
95     };
97     /**
98      * Check if the modal has an course id.
99      *
100      * @method hasCourseId
101      * @return {bool}
102      */
103     ModalEventForm.prototype.hasCourseId = function() {
104         return this.courseId !== null;
105     };
107     /**
108      * Set the event id to the given value.
109      *
110      * @method setEventId
111      * @param {int} id The event id
112      */
113     ModalEventForm.prototype.setEventId = function(id) {
114         this.eventId = id;
115     };
117     /**
118      * Retrieve the current event id, if any.
119      *
120      * @method getEventId
121      * @return {int|null} The event id
122      */
123     ModalEventForm.prototype.getEventId = function() {
124         return this.eventId;
125     };
127     /**
128      * Check if the modal has an event id.
129      *
130      * @method hasEventId
131      * @return {bool}
132      */
133     ModalEventForm.prototype.hasEventId = function() {
134         return this.eventId !== null;
135     };
137     /**
138      * Set the start time to the given value.
139      *
140      * @method setStartTime
141      * @param {int} time The start time
142      */
143     ModalEventForm.prototype.setStartTime = function(time) {
144         this.startTime = time;
145     };
147     /**
148      * Retrieve the current start time, if any.
149      *
150      * @method getStartTime
151      * @return {int|null} The start time
152      */
153     ModalEventForm.prototype.getStartTime = function() {
154         return this.startTime;
155     };
157     /**
158      * Check if the modal has start time.
159      *
160      * @method hasStartTime
161      * @return {bool}
162      */
163     ModalEventForm.prototype.hasStartTime = function() {
164         return this.startTime !== null;
165     };
167     /**
168      * Get the form element from the modal.
169      *
170      * @method getForm
171      * @return {object}
172      */
173     ModalEventForm.prototype.getForm = function() {
174         return this.getBody().find('form');
175     };
177     /**
178      * Disable the buttons in the footer.
179      *
180      * @method disableButtons
181      */
182     ModalEventForm.prototype.disableButtons = function() {
183         this.saveButton.prop('disabled', true);
184     };
186     /**
187      * Enable the buttons in the footer.
188      *
189      * @method enableButtons
190      */
191     ModalEventForm.prototype.enableButtons = function() {
192         this.saveButton.prop('disabled', false);
193     };
195     /**
196      * Reload the title for the modal to the appropriate value
197      * depending on whether we are creating a new event or
198      * editing an existing event.
199      *
200      * @method reloadTitleContent
201      * @return {object} A promise resolved with the new title text
202      */
203     ModalEventForm.prototype.reloadTitleContent = function() {
204         if (this.reloadingTitle) {
205             return this.titlePromise;
206         }
208         this.reloadingTitle = true;
210         if (this.hasEventId()) {
211             this.titlePromise = Str.get_string('editevent', 'calendar');
212         } else {
213             this.titlePromise = Str.get_string('newevent', 'calendar');
214         }
216         this.titlePromise.then(function(string) {
217             this.setTitle(string);
218             return string;
219         }.bind(this))
220         .always(function() {
221             this.reloadingTitle = false;
222             return;
223         }.bind(this));
225         return this.titlePromise;
226     };
228     /**
229      * Send a request to the server to get the event_form in a fragment
230      * and render the result in the body of the modal.
231      *
232      * If serialised form data is provided then it will be sent in the
233      * request to the server to have the form rendered with the data. This
234      * is used when the form had a server side error and we need the server
235      * to re-render it for us to display the error to the user.
236      *
237      * @method reloadBodyContent
238      * @param {string} formData The serialised form data
239      * @return {object} A promise resolved with the fragment html and js from
240      */
241     ModalEventForm.prototype.reloadBodyContent = function(formData) {
242         if (this.reloadingBody) {
243             return this.bodyPromise;
244         }
246         this.reloadingBody = true;
247         this.disableButtons();
249         var contextId = this.saveButton.attr('data-context-id');
250         var args = {};
252         if (this.hasEventId()) {
253             args.eventid = this.getEventId();
254         }
256         if (this.hasStartTime()) {
257             args.starttime = this.getStartTime();
258         }
260         if (this.hasCourseId()) {
261             args.courseid = this.getCourseId();
262         }
264         if (typeof formData !== 'undefined') {
265             args.formdata = formData;
266         }
268         this.bodyPromise = Fragment.loadFragment('calendar', 'event_form', contextId, args);
270         this.setBody(this.bodyPromise);
272         this.bodyPromise.then(function() {
273             this.enableButtons();
274             return;
275         }.bind(this))
276         .catch(Notification.exception)
277         .always(function() {
278             this.reloadingBody = false;
279             return;
280         }.bind(this));
282         return this.bodyPromise;
283     };
285     /**
286      * Reload both the title and body content.
287      *
288      * @method reloadAllContent
289      * @return {object} promise
290      */
291     ModalEventForm.prototype.reloadAllContent = function() {
292         return $.when(this.reloadTitleContent(), this.reloadBodyContent());
293     };
295     /**
296      * Kick off a reload the modal content before showing it. This
297      * is to allow us to re-use the same modal for creating and
298      * editing different events within the page.
299      *
300      * We do the reload when showing the modal rather than hiding it
301      * to save a request to the server if the user closes the modal
302      * and never re-opens it.
303      *
304      * @method show
305      */
306     ModalEventForm.prototype.show = function() {
307         this.reloadAllContent();
308         Modal.prototype.show.call(this);
309     };
311     /**
312      * Clear the event id from the modal when it's closed so
313      * that it is loaded fresh next time it's displayed.
314      *
315      * The event id will be set by the calling code if it wants
316      * to edit a specific event.
317      *
318      * @method hide
319      */
320     ModalEventForm.prototype.hide = function() {
321         Modal.prototype.hide.call(this);
322         this.setEventId(null);
323         this.setStartTime(null);
324     };
326     /**
327      * Get the serialised form data.
328      *
329      * @method getFormData
330      * @return {string} serialised form data
331      */
332     ModalEventForm.prototype.getFormData = function() {
333         return this.getForm().serialize();
334     };
336     /**
337      * Send the form data to the server to create or update
338      * an event.
339      *
340      * If there is a server side validation error then we re-request the
341      * rendered form (with the data) from the server in order to get the
342      * server side errors to display.
343      *
344      * On success the modal is hidden and the page is reloaded so that the
345      * new event will display.
346      *
347      * @method save
348      * @return {object} A promise
349      */
350     ModalEventForm.prototype.save = function() {
351         var loadingContainer = this.saveButton.find(SELECTORS.LOADING_ICON_CONTAINER);
353         loadingContainer.removeClass('hidden');
354         this.disableButtons();
356         var formData = this.getFormData();
357         // Send the form data to the server for processing.
358         return Repository.submitCreateUpdateForm(formData)
359             .then(function(response) {
360                 if (response.validationerror) {
361                     // If there was a server side validation error then
362                     // we need to re-request the rendered form from the server
363                     // in order to display the error for the user.
364                     return this.reloadBodyContent(formData);
365                 } else {
366                     // No problemo! Our work here is done.
367                     this.hide();
369                     // Trigger the appropriate calendar event so that the view can
370                     // be updated.
371                     if (this.hasEventId()) {
372                         $('body').trigger(CalendarEvents.updated, [response.event]);
373                     } else {
374                         $('body').trigger(CalendarEvents.created, [response.event]);
375                     }
376                 }
378                 return;
379             }.bind(this))
380             .always(function() {
381                 // Regardless of success or error we should always stop
382                 // the loading icon and re-enable the buttons.
383                 loadingContainer.addClass('hidden');
384                 this.enableButtons();
385             }.bind(this))
386             .catch(Notification.exception);
387     };
389     /**
390      * Set up all of the event handling for the modal.
391      *
392      * @method registerEventListeners
393      */
394     ModalEventForm.prototype.registerEventListeners = function() {
395         // Apply parent event listeners.
396         Modal.prototype.registerEventListeners.call(this);
398         // When the user clicks the save button we trigger the form submission. We need to
399         // trigger an actual submission because there is some JS code in the form that is
400         // listening for this event and doing some stuff (e.g. saving draft areas etc).
401         this.getModal().on(CustomEvents.events.activate, SELECTORS.SAVE_BUTTON, function(e, data) {
402             this.getForm().submit();
403             data.originalEvent.preventDefault();
404             e.stopPropagation();
405         }.bind(this));
407         // Catch the submit event before it is actually processed by the browser and
408         // prevent the submission. We'll take it from here.
409         this.getModal().on('submit', function(e) {
410             this.save();
412             // Stop the form from actually submitting and prevent it's
413             // propagation because we have already handled the event.
414             e.preventDefault();
415             e.stopPropagation();
416         }.bind(this));
417     };
419     // Automatically register with the modal registry the first time this module is imported so that you can create modals
420     // of this type using the modal factory.
421     if (!registered) {
422         ModalRegistry.register(ModalEventForm.TYPE, ModalEventForm, 'calendar/modal_event_form');
423         registered = true;
424     }
426     return ModalEventForm;
427 });