MDL-68963 qtype_calculatedmulti: show question content inline
[moodle.git] / user / amd / src / status_field.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  * AMD module for the user enrolment status field in the course participants page.
18  *
19  * @module     core_user/status_field
20  * @copyright  2017 Jun Pataleta
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
24 import * as DynamicTable from 'core_table/dynamic';
25 import * as Repository from './repository';
26 import * as Str from 'core/str';
27 import DynamicTableSelectors from 'core_table/local/dynamic/selectors';
28 import Fragment from 'core/fragment';
29 import ModalEvents from 'core/modal_events';
30 import ModalFactory from 'core/modal_factory';
31 import Notification from 'core/notification';
32 import Templates from 'core/templates';
33 import {add as notifyUser} from 'core/toast';
35 const Selectors = {
36     editEnrolment: '[data-action="editenrolment"]',
37     showDetails: '[data-action="showdetails"]',
38     unenrol: '[data-action="unenrol"]',
39     statusElement: '[data-status]',
40 };
42 /**
43  * Get the dynamic table from the specified link.
44  *
45  * @param {HTMLElement} link
46  * @returns {HTMLElement}
47  */
48 const getDynamicTableFromLink = link => link.closest(DynamicTableSelectors.main.region);
50 /**
51  * Get the status container from the specified link.
52  *
53  * @param {HTMLElement} link
54  * @returns {HTMLElement}
55  */
56 const getStatusContainer = link => link.closest(Selectors.statusElement);
58 /**
59  * Get user enrolment id from the specified link
60  *
61  * @param {HTMLElement} link
62  * @returns {Number}
63  */
64 const getUserEnrolmentIdFromLink = link => link.getAttribute('rel');
66 /**
67  * Register all event listeners for the status fields.
68  *
69  * @param {Number} contextId
70  * @param {Number} uniqueId
71  */
72 const registerEventListeners = (contextId, uniqueId) => {
73     const getBodyFunction = (userEnrolmentId, formData) => getBody(contextId, userEnrolmentId, formData);
75     document.addEventListener('click', e => {
76         const tableRoot = e.target.closest(DynamicTableSelectors.main.fromRegionId(uniqueId));
77         if (!tableRoot) {
78             return;
79         }
81         const editLink = e.target.closest(Selectors.editEnrolment);
82         if (editLink) {
83             e.preventDefault();
85             showEditDialogue(editLink, getBodyFunction);
86         }
88         const unenrolLink = e.target.closest(Selectors.unenrol);
89         if (unenrolLink) {
90             e.preventDefault();
92             showUnenrolConfirmation(unenrolLink);
93         }
95         const showDetailsLink = e.target.closest(Selectors.showDetails);
96         if (showDetailsLink) {
97             e.preventDefault();
99             showStatusDetails(showDetailsLink);
100         }
101     });
102 };
104 /**
105  * Show the edit dialogue.
106  *
107  * @param {HTMLElement} link
108  * @param {Function} getBody Function to get the body for the specified user enrolment
109  */
110 const showEditDialogue = (link, getBody) => {
111     const container = getStatusContainer(link);
112     const userEnrolmentId = getUserEnrolmentIdFromLink(link);
114     ModalFactory.create({
115         large: true,
116         title: Str.get_string('edituserenrolment', 'enrol', container.dataset.fullname),
117         type: ModalFactory.types.SAVE_CANCEL,
118         body: getBody(userEnrolmentId)
119     })
120     .then(modal => {
121         // Handle save event.
122         modal.getRoot().on(ModalEvents.save, e => {
123             // Don't close the modal yet.
124             e.preventDefault();
126             // Submit form data.
127             submitEditFormAjax(link, getBody, modal, userEnrolmentId, container.dataset);
128         });
130         // Handle hidden event.
131         modal.getRoot().on(ModalEvents.hidden, () => {
132             // Destroy when hidden.
133             modal.destroy();
134         });
136         // Show the modal.
137         modal.show();
139         return modal;
140     })
141     .catch(Notification.exception);
142 };
144 /**
145  * Show and handle the unenrolment confirmation dialogue.
146  *
147  * @param {HTMLElement} link
148  */
149 const showUnenrolConfirmation = link => {
150     const container = getStatusContainer(link);
151     const userEnrolmentId = getUserEnrolmentIdFromLink(link);
153     ModalFactory.create({
154         type: ModalFactory.types.SAVE_CANCEL,
155     })
156     .then(modal => {
157         // Handle confirm event.
158         modal.getRoot().on(ModalEvents.save, e => {
159             // Don't close the modal yet.
160             e.preventDefault();
162             // Submit data.
163             submitUnenrolFormAjax(
164                 link,
165                 modal,
166                 {
167                     ueid: userEnrolmentId,
168                 },
169                 container.dataset
170             );
171         });
173         // Handle hidden event.
174         modal.getRoot().on(ModalEvents.hidden, () => {
175             // Destroy when hidden.
176             modal.destroy();
177         });
179         // Display the delete confirmation modal.
180         modal.show();
182         const stringData = [
183             {
184                 key: 'unenrol',
185                 component: 'enrol',
186             },
187             {
188                 key: 'unenrolconfirm',
189                 component: 'enrol',
190                 param: {
191                     user: container.dataset.fullname,
192                     course: container.dataset.coursename,
193                     enrolinstancename: container.dataset.enrolinstancename,
194                 }
195             }
196         ];
198         return Promise.all([Str.get_strings(stringData), modal]);
199     })
200     .then(([strings, modal]) => {
201         modal.setTitle(strings[0]);
202         modal.setSaveButtonText(strings[0]);
203         modal.setBody(strings[1]);
205         return modal;
206     })
207     .catch(Notification.exception);
208 };
210 /**
211  * Show the user details dialogue.
212  *
213  * @param {HTMLElement} link
214  */
215 const showStatusDetails = link => {
216     const container = getStatusContainer(link);
218     const context = {
219         editenrollink: '',
220         statusclass: container.querySelector('span.badge').getAttribute('class'),
221         ...container.dataset,
222     };
224     // Find the edit enrolment link.
225     const editEnrolLink = container.querySelector(Selectors.editEnrolment);
226     if (editEnrolLink) {
227         // If there's an edit enrolment link for this user, clone it into the context for the modal.
228         context.editenrollink = editEnrolLink.outerHTML;
229     }
231     ModalFactory.create({
232         large: true,
233         type: ModalFactory.types.CANCEL,
234         title: Str.get_string('enroldetails', 'enrol'),
235         body: Templates.render('core_user/status_details', context),
236     })
237     .then(modal => {
238         if (editEnrolLink) {
239             modal.getRoot().on('click', Selectors.editEnrolment, e => {
240                 e.preventDefault();
241                 modal.hide();
243                 // Trigger click event for the edit enrolment link to show the edit enrolment modal.
244                 editEnrolLink.click();
245             });
246         }
248         modal.show();
250         // Handle hidden event.
251         modal.getRoot().on(ModalEvents.hidden, () => modal.destroy());
253         return modal;
254     })
255     .catch(Notification.exception);
256 };
258 /**
259  * Submit the edit dialogue.
260  *
261  * @param {HTMLElement} clickedLink
262  * @param {Function} getBody
263  * @param {Object} modal
264  * @param {Number} userEnrolmentId
265  * @param {Object} userData
266  */
267 const submitEditFormAjax = (clickedLink, getBody, modal, userEnrolmentId, userData) => {
268     const form = modal.getRoot().find('form');
270     Repository.submitUserEnrolmentForm(form.serialize())
271     .then(data => {
272         if (!data.result) {
273             throw data.result;
274         }
276         // Dismiss the modal.
277         modal.hide();
278         modal.destroy();
280         return data;
281     })
282     .then(() => {
283         DynamicTable.refreshTableContent(getDynamicTableFromLink(clickedLink));
285         return Str.get_string('enrolmentupdatedforuser', 'core_enrol', userData);
286     })
287     .then(notificationString => {
288         notifyUser(notificationString);
290         return;
291     })
292     .catch(() => {
293         modal.setBody(getBody(userEnrolmentId, JSON.stringify(form.serialize())));
295         return modal;
296     });
297 };
299 /**
300  * Submit the unenrolment form.
301  *
302  * @param {HTMLElement} clickedLink
303  * @param {Object} modal
304  * @param {Object} args
305  * @param {Object} userData
306  */
307 const submitUnenrolFormAjax = (clickedLink, modal, args, userData) => {
308     Repository.unenrolUser(args.ueid)
309     .then(data => {
310         if (!data.result) {
311             // Display an alert containing the error message
312             Notification.alert(data.errors[0].key, data.errors[0].message);
314             return data;
315         }
317         // Dismiss the modal.
318         modal.hide();
319         modal.destroy();
321         return data;
322     })
323     .then(() => {
324         DynamicTable.refreshTableContent(getDynamicTableFromLink(clickedLink));
326         return Str.get_string('unenrolleduser', 'core_enrol', userData);
327     })
328     .then(notificationString => {
329         notifyUser(notificationString);
331         return;
332     })
333     .catch(Notification.exception);
334 };
336 /**
337  * Get the body fragment.
338  *
339  * @param {Number} contextId
340  * @param {Number} ueid The user enrolment id
341  * @param {Object} formdata
342  * @returns {Promise}
343  */
344 const getBody = (contextId, ueid, formdata = null) => Fragment.loadFragment(
345     'enrol',
346     'user_enrolment_form',
347     contextId,
348     {
349         ueid,
350         formdata,
351     }
352 );
354 /**
355  * Initialise the statu field handler.
356  *
357  * @param {Number} contextid
358  * @param {Number} uniqueid
359  */
360 export const init = ({contextid, uniqueid}) => {
361     registerEventListeners(contextid, uniqueid);
362 };