MDL-61133 core_question: add new question tags modal
[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',
32 ],
33 function(
34 $,
35 Fragment,
36 Str,
37 ModalEvents,
38 ModalFactory,
39 Notification,
40 CustomEvents,
41 Repository
42 ) {
43
44 var SELECTORS = {
45 TAGS_LINK: '[data-action="edittags"]',
46 SAVE_BUTTON: '[data-action="save"]',
47 LOADING_ICON: '[data-region="overlay-icon-container"]',
48 };
49
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 };
59
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 };
69
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 };
80
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);
89
90 loadingIconContainer.removeClass('hidden');
91 };
92
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);
101
102 loadingIconContainer.addClass('hidden');
103 };
104
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);
127
128 modal.getRoot().on(ModalEvents.save, function(e) {
129 var form = modal.getBody().find('form');
130 form.submit();
131 e.preventDefault();
132 });
133
134 modal.getRoot().on('submit', 'form', function(e) {
135 save(modal, root).then(function() {
136 modal.hide();
137 return;
138 }).fail(Notification.exception);
139
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 });
145
146 return modal;
147 });
148
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);
154
155 var questionId = currentTarget.data('questionid'),
156 canEdit = !!currentTarget.data('canedit'),
157 contextId = currentTarget.data('contextid');
158
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);
165
166 var args = {
167 id: questionId
168 };
169
170 var tagsFragment = Fragment.loadFragment('question', 'tags_form', contextId, args);
171 modal.setBody(tagsFragment);
172
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);
184
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 }
192
193 return modal;
194 }).fail(Notification.exception);
195
196 e.preventDefault();
197 });
198 };
199
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);
212
213 var formData = getFormData(modal);
214
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 };
226
227 return {
228 init: function(root) {
229 root = $(root);
230 registerEventListeners(root);
231 }
232 };
233});