MDL-61133 core_question: add new question tags modal
[moodle.git] / question / amd / src / edit_tags.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  * A javascript module to handle question tags editing.
18  *
19  * @module     core_question/edit_tags
20  * @copyright  2018 Simey Lameze <simey@moodle.com>
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
23 define([
24             'jquery',
25             'core/fragment',
26             'core/str',
27             'core/modal_events',
28             'core/modal_factory',
29             'core/notification',
30             'core/custom_interaction_events',
31             'core_question/repository',
32         ],
33         function(
34             $,
35             Fragment,
36             Str,
37             ModalEvents,
38             ModalFactory,
39             Notification,
40             CustomEvents,
41             Repository
42         ) {
44     var SELECTORS = {
45         TAGS_LINK: '[data-action="edittags"]',
46         SAVE_BUTTON: '[data-action="save"]',
47         LOADING_ICON: '[data-region="overlay-icon-container"]',
48     };
50     /**
51      * Enable the save button in the footer.
52      *
53      * @param {object} root The container element.
54      * @method enableSaveButton
55      */
56     var enableSaveButton = function(root) {
57         root.find(SELECTORS.SAVE_BUTTON).prop('disabled', false);
58     };
60     /**
61      * Disable the save button in the footer.
62      *
63      * @param {object} root The container element.
64      * @method disableSaveButton
65      */
66     var disableSaveButton = function(root) {
67         root.find(SELECTORS.SAVE_BUTTON).prop('disabled', true);
68     };
70     /**
71      * Get the serialised form data.
72      *
73      * @method getFormData
74      * @param {object} modal The modal object.
75      * @return {string} serialised form data
76      */
77     var getFormData = function(modal) {
78         return modal.getBody().find('form').serialize();
79     };
81     /**
82      * Set the element state to loading.
83      *
84      * @param {object} root The container element
85      * @method startLoading
86      */
87     var startLoading = function(root) {
88         var loadingIconContainer = root.find(SELECTORS.LOADING_ICON);
90         loadingIconContainer.removeClass('hidden');
91     };
93     /**
94      * Remove the loading state from the element.
95      *
96      * @param {object} root The container element
97      * @method stopLoading
98      */
99     var stopLoading = function(root) {
100         var loadingIconContainer = root.find(SELECTORS.LOADING_ICON);
102         loadingIconContainer.addClass('hidden');
103     };
105     /**
106      * Register event listeners for the module.
107      *
108      * @param {object} root The calendar root element
109      */
110     var registerEventListeners = function(root) {
111         var modalPromise = ModalFactory.create(
112             {
113                 type: ModalFactory.types.SAVE_CANCEL,
114                 large: false
115             },
116             [root, SELECTORS.TAGS_LINK]
117         ).then(function(modal) {
118             // All of this code only executes once, when the modal is
119             // first created. This allows us to add any code that should
120             // only be run once, such as adding event handlers to the modal.
121             Str.get_string('questiontags', 'question')
122                 .then(function(string) {
123                     modal.setTitle(string);
124                     return string;
125                 })
126                 .fail(Notification.exception);
128             modal.getRoot().on(ModalEvents.save, function(e) {
129                 var form = modal.getBody().find('form');
130                 form.submit();
131                 e.preventDefault();
132             });
134             modal.getRoot().on('submit', 'form', function(e) {
135                 save(modal, root).then(function() {
136                     modal.hide();
137                     return;
138                 }).fail(Notification.exception);
140                 // Stop the form from actually submitting and prevent it's
141                 // propagation because we have already handled the event.
142                 e.preventDefault();
143                 e.stopPropagation();
144             });
146             return modal;
147         });
149         // We need to add an event handler to the tags link because there are
150         // multiple links on the page and without adding a listener we don't know
151         // which one the user clicked on the show the modal.
152         root.on(CustomEvents.events.activate, SELECTORS.TAGS_LINK, function(e) {
153             var currentTarget = $(e.currentTarget);
155             var questionId = currentTarget.data('questionid'),
156                 canEdit = !!currentTarget.data('canedit'),
157                 contextId = currentTarget.data('contextid');
159             // This code gets called each time the user clicks the tag link
160             // so we can use it to reload the contents of the tag modal.
161             modalPromise.then(function(modal) {
162                 // Display spinner and disable save button.
163                 disableSaveButton(root);
164                 startLoading(root);
166                 var args = {
167                     id: questionId
168                 };
170                 var tagsFragment = Fragment.loadFragment('question', 'tags_form', contextId, args);
171                 modal.setBody(tagsFragment);
173                 tagsFragment.then(function() {
174                         enableSaveButton(root);
175                         return;
176                     })
177                     .always(function() {
178                         // Always hide the loading spinner when the request
179                         // has completed.
180                         stopLoading(root);
181                         return;
182                     })
183                 .fail(Notification.exception);
185                 // Show or hide the save button depending on whether the user
186                 // has the capability to edit the tags.
187                 if (canEdit) {
188                     modal.getRoot().find(SELECTORS.SAVE_BUTTON).show();
189                 } else {
190                     modal.getRoot().find(SELECTORS.SAVE_BUTTON).hide();
191                 }
193                 return modal;
194             }).fail(Notification.exception);
196             e.preventDefault();
197         });
198     };
200     /**
201      * Send the form data to the server to save question tags.
202      *
203      * @method save
204      * @param {object} modal The modal object.
205      * @param {object} root The container element.
206      * @return {object} A promise
207      */
208     var save = function(modal, root) {
209         // Display spinner and disable save button.
210         disableSaveButton(root);
211         startLoading(root);
213         var formData = getFormData(modal);
215         // Send the form data to the server for processing.
216         return Repository.submitTagCreateUpdateForm(formData)
217             .always(function() {
218                 // Regardless of success or error we should always stop
219                 // the loading icon and re-enable the buttons.
220                 stopLoading(root);
221                 enableSaveButton(root);
222                 return;
223             })
224             .fail(Notification.exception);
225     };
227     return {
228         init: function(root) {
229             root = $(root);
230             registerEventListeners(root);
231         }
232     };
233 });