Merge branch 'MDL-62079-master' of git://github.com/andrewnicols/moodle
[moodle.git] / question / amd / src / edit_tags.js
CommitLineData
fd5e2ead
SL
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/>.
15
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 */
23define([
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',
d1db765a 32 'core_question/selectors',
fd5e2ead
SL
33 ],
34 function(
35 $,
36 Fragment,
37 Str,
38 ModalEvents,
39 ModalFactory,
40 Notification,
41 CustomEvents,
d1db765a
SL
42 Repository,
43 QuestionSelectors
fd5e2ead
SL
44 ) {
45
fd5e2ead
SL
46 /**
47 * Enable the save button in the footer.
48 *
49 * @param {object} root The container element.
50 * @method enableSaveButton
51 */
52 var enableSaveButton = function(root) {
d1db765a 53 root.find(QuestionSelectors.actions.save).prop('disabled', false);
fd5e2ead
SL
54 };
55
56 /**
57 * Disable the save button in the footer.
58 *
59 * @param {object} root The container element.
60 * @method disableSaveButton
61 */
62 var disableSaveButton = function(root) {
d1db765a 63 root.find(QuestionSelectors.actions.save).prop('disabled', true);
fd5e2ead
SL
64 };
65
66 /**
67 * Get the serialised form data.
68 *
69 * @method getFormData
70 * @param {object} modal The modal object.
71 * @return {string} serialised form data
72 */
73 var getFormData = function(modal) {
74 return modal.getBody().find('form').serialize();
75 };
76
77 /**
78 * Set the element state to loading.
79 *
80 * @param {object} root The container element
81 * @method startLoading
82 */
83 var startLoading = function(root) {
d1db765a 84 var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);
fd5e2ead
SL
85
86 loadingIconContainer.removeClass('hidden');
87 };
88
89 /**
90 * Remove the loading state from the element.
91 *
92 * @param {object} root The container element
93 * @method stopLoading
94 */
95 var stopLoading = function(root) {
d1db765a 96 var loadingIconContainer = root.find(QuestionSelectors.containers.loadingIcon);
fd5e2ead
SL
97
98 loadingIconContainer.addClass('hidden');
99 };
100
e6890b11
SL
101 /**
102 * Set the context Id data attribute on the modal.
103 *
104 * @param {Promise} modal The modal promise.
105 * @param {int} contextId The context id.
106 */
107 var setContextId = function(modal, contextId) {
108 modal.getBody().attr('data-contextid', contextId);
109 };
110
111 /**
112 * Get the context Id data attribute value from the modal body.
113 *
114 * @param {Promise} modal The modal promise.
115 * @return {int} The context id.
116 */
117 var getContextId = function(modal) {
118 return modal.getBody().data('contextid');
119 };
120
121 /**
122 * Set the question Id data attribute on the modal.
123 *
124 * @param {Promise} modal The modal promise.
125 * @param {int} questionId The question Id.
126 */
127 var setQuestionId = function(modal, questionId) {
128 modal.getBody().attr('data-questionid', questionId);
129 };
130
131 /**
132 * Get the question Id data attribute value from the modal body.
133 *
134 * @param {Promise} modal The modal promise.
135 * @return {int} The question Id.
136 */
137 var getQuestionId = function(modal) {
138 return modal.getBody().data('questionid');
139 };
140
fd5e2ead
SL
141 /**
142 * Register event listeners for the module.
143 *
144 * @param {object} root The calendar root element
145 */
146 var registerEventListeners = function(root) {
147 var modalPromise = ModalFactory.create(
148 {
149 type: ModalFactory.types.SAVE_CANCEL,
150 large: false
151 },
d1db765a 152 [root, QuestionSelectors.actions.edittags]
fd5e2ead
SL
153 ).then(function(modal) {
154 // All of this code only executes once, when the modal is
155 // first created. This allows us to add any code that should
156 // only be run once, such as adding event handlers to the modal.
157 Str.get_string('questiontags', 'question')
158 .then(function(string) {
159 modal.setTitle(string);
160 return string;
161 })
162 .fail(Notification.exception);
163
164 modal.getRoot().on(ModalEvents.save, function(e) {
165 var form = modal.getBody().find('form');
166 form.submit();
167 e.preventDefault();
168 });
169
170 modal.getRoot().on('submit', 'form', function(e) {
171 save(modal, root).then(function() {
172 modal.hide();
c91da280 173 location.reload();
fd5e2ead
SL
174 return;
175 }).fail(Notification.exception);
176
177 // Stop the form from actually submitting and prevent it's
178 // propagation because we have already handled the event.
179 e.preventDefault();
180 e.stopPropagation();
181 });
182
183 return modal;
184 });
185
186 // We need to add an event handler to the tags link because there are
187 // multiple links on the page and without adding a listener we don't know
188 // which one the user clicked on the show the modal.
d1db765a 189 root.on(CustomEvents.events.activate, QuestionSelectors.actions.edittags, function(e) {
fd5e2ead
SL
190 var currentTarget = $(e.currentTarget);
191
192 var questionId = currentTarget.data('questionid'),
e2795e86 193 canTag = !!currentTarget.data('cantag'),
fd5e2ead
SL
194 contextId = currentTarget.data('contextid');
195
196 // This code gets called each time the user clicks the tag link
197 // so we can use it to reload the contents of the tag modal.
198 modalPromise.then(function(modal) {
199 // Display spinner and disable save button.
200 disableSaveButton(root);
201 startLoading(root);
202
203 var args = {
204 id: questionId
205 };
206
207 var tagsFragment = Fragment.loadFragment('question', 'tags_form', contextId, args);
208 modal.setBody(tagsFragment);
209
210 tagsFragment.then(function() {
211 enableSaveButton(root);
212 return;
213 })
214 .always(function() {
215 // Always hide the loading spinner when the request
216 // has completed.
217 stopLoading(root);
218 return;
219 })
220 .fail(Notification.exception);
221
222 // Show or hide the save button depending on whether the user
223 // has the capability to edit the tags.
e2795e86 224 if (canTag) {
d1db765a 225 modal.getRoot().find(QuestionSelectors.actions.save).show();
fd5e2ead 226 } else {
d1db765a 227 modal.getRoot().find(QuestionSelectors.actions.save).hide();
fd5e2ead
SL
228 }
229
e6890b11
SL
230 setQuestionId(modal, questionId);
231 setContextId(modal, contextId);
232
fd5e2ead
SL
233 return modal;
234 }).fail(Notification.exception);
235
236 e.preventDefault();
237 });
238 };
239
240 /**
241 * Send the form data to the server to save question tags.
242 *
243 * @method save
244 * @param {object} modal The modal object.
245 * @param {object} root The container element.
246 * @return {object} A promise
247 */
248 var save = function(modal, root) {
249 // Display spinner and disable save button.
250 disableSaveButton(root);
251 startLoading(root);
252
253 var formData = getFormData(modal);
e6890b11
SL
254 var questionId = getQuestionId(modal);
255 var contextId = getContextId(modal);
fd5e2ead
SL
256
257 // Send the form data to the server for processing.
e6890b11 258 return Repository.submitTagCreateUpdateForm(questionId, contextId, formData)
fd5e2ead
SL
259 .always(function() {
260 // Regardless of success or error we should always stop
261 // the loading icon and re-enable the buttons.
262 stopLoading(root);
263 enableSaveButton(root);
264 return;
265 })
266 .fail(Notification.exception);
267 };
268
269 return {
270 init: function(root) {
271 root = $(root);
272 registerEventListeners(root);
273 }
274 };
275});