Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 * Contain the logic for the add random question modal.
18
 *
19
 * @module     mod_quiz/modal_add_random_question
20
 * @copyright  2018 Ryan Wyllie <ryan@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import $ from 'jquery';
25
import Modal from './add_question_modal';
26
import * as Notification from 'core/notification';
27
import * as Fragment from 'core/fragment';
28
import * as Templates from 'core/templates';
29
import * as FormChangeChecker from 'core_form/changechecker';
30
import {call as fetchMany} from 'core/ajax';
31
import Pending from 'core/pending';
32
 
33
const SELECTORS = {
34
    EXISTING_CATEGORY_CONTAINER: '[data-region="existing-category-container"]',
35
    EXISTING_CATEGORY_TAB: '#id_existingcategoryheader',
36
    NEW_CATEGORY_CONTAINER: '[data-region="new-category-container"]',
37
    NEW_CATEGORY_TAB: '#id_newcategoryheader',
38
    TAB_CONTENT: '[data-region="tab-content"]',
39
    ADD_ON_PAGE_FORM_ELEMENT: '[name="addonpage"]',
40
    ADD_RANDOM_BUTTON: 'input[type="submit"][name="addrandom"]',
41
    ADD_NEW_CATEGORY_BUTTON: 'input[type="submit"][name="newcategory"]',
42
    SUBMIT_BUTTON_ELEMENT: 'input[type="submit"][name="addrandom"], input[type="submit"][name="newcategory"]',
43
    FORM_HEADER: 'legend',
44
    SELECT_NUMBER_TO_ADD: '#menurandomcount',
45
    NEW_CATEGORY_ELEMENT: '#categoryname',
46
    PARENT_CATEGORY_ELEMENT: '#parentcategory',
47
    FILTER_CONDITION_ELEMENT: '[data-filtercondition]',
48
    FORM_ELEMENT: '#add_random_question_form',
49
    MESSAGE_INPUT: '[name="message"]',
50
};
51
 
52
export default class ModalAddRandomQuestion extends Modal {
53
    static TYPE = 'mod_quiz-quiz-add-random-question';
54
    static TEMPLATE = 'mod_quiz/modal_add_random_question';
55
 
56
    /**
57
     * Create the add random question modal.
58
     *
59
     * @param  {Number} contextId Current context id.
60
     * @param  {string} category Category id and category context id comma separated.
61
     * @param  {string} returnUrl URL to return to after form submission.
62
     * @param  {Number} cmid Current course module id.
63
     * @param  {boolean} showNewCategory Display the New category tab when selecting random questions.
64
     */
65
    static init(contextId, category, returnUrl, cmid, showNewCategory = true) {
66
        const selector = '.menu [data-action="addarandomquestion"]';
67
        document.addEventListener('click', (e) => {
68
            const trigger = e.target.closest(selector);
69
            if (!trigger) {
70
                return;
71
            }
72
            e.preventDefault();
73
 
74
            ModalAddRandomQuestion.create({
75
                contextId,
76
                category,
77
                returnUrl,
78
                cmid,
79
 
80
                title: trigger.dataset.header,
81
                addOnPage: trigger.dataset.addonpage,
82
 
83
                templateContext: {
84
                    hidden: showNewCategory,
85
                },
86
            });
87
        });
88
    }
89
 
90
    /**
91
     * Constructor for the Modal.
92
     *
93
     * @param {object} root The root jQuery element for the modal
94
     */
95
    constructor(root) {
96
        super(root);
97
        this.category = null;
98
        this.returnUrl = null;
99
        this.cmid = null;
100
        this.loadedForm = false;
101
    }
102
 
103
    configure(modalConfig) {
11 efrain 104
        modalConfig.removeOnClose = true;
105
 
1 efrain 106
        this.setCategory(modalConfig.category);
107
        this.setReturnUrl(modalConfig.returnUrl);
108
        this.setCMID(modalConfig.cmid);
109
 
110
        super.configure(modalConfig);
111
    }
112
 
113
    /**
114
     * Set the id of the page that the question should be added to
115
     * when the user clicks the add to quiz link.
116
     *
117
     * @method setAddOnPageId
118
     * @param {int} id
119
     */
120
    setAddOnPageId(id) {
121
        super.setAddOnPageId(id);
122
        this.getBody().find(SELECTORS.ADD_ON_PAGE_FORM_ELEMENT).val(id);
123
    }
124
 
125
    /**
126
     * Set the category for this form. The category is a comma separated
127
     * category id and category context id.
128
     *
129
     * @method setCategory
130
     * @param {string} category
131
     */
132
    setCategory(category) {
133
        this.category = category;
134
    }
135
 
136
    /**
137
     * Returns the saved category.
138
     *
139
     * @method getCategory
140
     * @return {string}
141
     */
142
    getCategory() {
143
        return this.category;
144
    }
145
 
146
    /**
147
     * Set the return URL for the form.
148
     *
149
     * @method setReturnUrl
150
     * @param {string} url
151
     */
152
    setReturnUrl(url) {
153
        this.returnUrl = url;
154
    }
155
 
156
    /**
157
     * Returns the return URL for the form.
158
     *
159
     * @method getReturnUrl
160
     * @return {string}
161
     */
162
    getReturnUrl() {
163
        return this.returnUrl;
164
    }
165
 
166
    /**
167
     * Set the course module id for the form.
168
     *
169
     * @method setCMID
170
     * @param {Number} id
171
     */
172
    setCMID(id) {
173
        this.cmid = id;
174
    }
175
 
176
    /**
177
     * Returns the course module id for the form.
178
     *
179
     * @method getCMID
180
     * @return {Number}
181
     */
182
    getCMID() {
183
        return this.cmid;
184
    }
185
 
186
    /**
187
     * Moves a given form element inside (a child of) a given tab element.
188
     *
189
     * Hides the 'legend' (e.g. header) element of the form element because the
190
     * tab has the name.
191
     *
192
     * Moves the submit button into a footer element at the bottom of the form
193
     * element for styling purposes.
194
     *
195
     * @method moveContentIntoTab
196
     * @param  {jquery} tabContent The form element to move into the tab.
197
     * @param  {jquey} tabElement The tab element for the form element to move into.
198
     */
199
    moveContentIntoTab(tabContent, tabElement) {
200
        // Hide the header because the tabs show us which part of the form we're
201
        // looking at.
202
        tabContent.find(SELECTORS.FORM_HEADER).addClass('hidden');
203
        // Move the element inside a tab.
204
        tabContent.wrap(tabElement);
205
    }
206
 
207
    /**
208
     * Empty the tab content container and move all tabs from the form into the
209
     * tab container element.
210
     *
211
     * @method moveTabsIntoTabContent
212
     * @param  {jquery} form The form element.
213
     */
214
    moveTabsIntoTabContent(form) {
215
        // Empty it to remove the loading icon.
216
        const tabContent = this.getBody().find(SELECTORS.TAB_CONTENT).empty();
217
        // Make sure all tabs are inside the tab content element.
218
        form.find('[role="tabpanel"]').wrapAll(tabContent);
219
    }
220
 
221
    /**
222
     * Make sure all of the tabs have a cancel button in their fotter to sit along
223
     * side the submit button.
224
     *
225
     * @method moveCancelButtonToTabs
226
     * @param  {jquey} form The form element.
227
     */
228
    moveCancelButtonToTabs(form) {
229
        const cancelButton = form.find(SELECTORS.CANCEL_BUTTON_ELEMENT).addClass('ml-1');
230
        const tabFooters = form.find('[data-region="footer"]');
231
        // Remove the buttons container element.
232
        cancelButton.closest(SELECTORS.BUTTON_CONTAINER).remove();
233
        cancelButton.clone().appendTo(tabFooters);
234
    }
235
 
236
    /**
237
     * Load the add random question form in a fragement and perform some transformation
238
     * on the HTML to convert it into tabs for rendering in the modal.
239
     *
240
     * @method loadForm
241
     * @return {promise} Resolved with form HTML and JS.
242
     */
243
    loadForm() {
244
        const cmid = this.getCMID();
245
        const cat = this.getCategory();
246
        const addonpage = this.getAddOnPageId();
247
        const returnurl = this.getReturnUrl();
248
 
249
        return Fragment.loadFragment(
250
            'mod_quiz',
251
            'add_random_question_form',
252
            this.getContextId(),
253
            {
254
                addonpage,
255
                cat,
256
                returnurl,
257
                cmid,
258
            }
259
        )
260
        .then((html, js) =>{
261
            const form = $(html);
262
            const existingCategoryTabContent = form.find(SELECTORS.EXISTING_CATEGORY_TAB);
263
            const existingCategoryTab = this.getBody().find(SELECTORS.EXISTING_CATEGORY_CONTAINER);
264
            const newCategoryTabContent = form.find(SELECTORS.NEW_CATEGORY_TAB);
265
            const newCategoryTab = this.getBody().find(SELECTORS.NEW_CATEGORY_CONTAINER);
266
 
267
            // Transform the form into tabs for better rendering in the modal.
268
            this.moveContentIntoTab(existingCategoryTabContent, existingCategoryTab);
269
            this.moveContentIntoTab(newCategoryTabContent, newCategoryTab);
270
            this.moveTabsIntoTabContent(form);
271
 
272
            Templates.replaceNode(this.getBody().find(SELECTORS.TAB_CONTENT), form, js);
273
            return;
274
        })
275
        .then(() => {
276
            // Make sure the form change checker is disabled otherwise it'll stop the user from navigating away from the
277
            // page once the modal is hidden.
278
            FormChangeChecker.disableAllChecks();
279
 
280
            // Add question to quiz.
281
            this.getBody()[0].addEventListener('click', (e) => {
282
                const button = e.target.closest(SELECTORS.SUBMIT_BUTTON_ELEMENT);
283
                if (!button) {
284
                    return;
285
                }
286
                e.preventDefault();
287
 
288
                // Add Random questions if the add random button was clicked.
289
                const addRandomButton = e.target.closest(SELECTORS.ADD_RANDOM_BUTTON);
290
                if (addRandomButton) {
291
                    const randomcount = document.querySelector(SELECTORS.SELECT_NUMBER_TO_ADD).value;
292
                    const filtercondition = document.querySelector(SELECTORS.FILTER_CONDITION_ELEMENT).dataset?.filtercondition;
293
 
294
                    this.addQuestions(cmid, addonpage, randomcount, filtercondition, '', '');
295
                    return;
296
                }
297
                // Add new category if the add category button was clicked.
298
                const addCategoryButton = e.target.closest(SELECTORS.ADD_NEW_CATEGORY_BUTTON);
299
                if (addCategoryButton) {
300
                    this.addQuestions(
301
                        cmid,
302
                        addonpage,
303
                        1,
304
                        '',
305
                        document.querySelector(SELECTORS.NEW_CATEGORY_ELEMENT).value,
306
                        document.querySelector(SELECTORS.PARENT_CATEGORY_ELEMENT).value
307
                    );
308
                    return;
309
                }
310
            });
311
        })
312
        .catch(Notification.exception);
313
    }
314
 
315
    /**
316
     * Call web service function to add random questions
317
     *
318
     * @param {number} cmid course module id
319
     * @param {number} addonpage the page where random questions will be added to
320
     * @param {number} randomcount Number of random questions
321
     * @param {string} filtercondition Filter condition
322
     * @param {string} newcategory add new category
323
     * @param {string} parentcategory parent category of new category
324
     */
325
    async addQuestions(
326
        cmid,
327
        addonpage,
328
        randomcount,
329
        filtercondition,
330
        newcategory,
331
        parentcategory
332
    ) {
333
        // We do not need to resolve this Pending because the form submission will result in a page redirect.
334
        new Pending('mod-quiz/modal_add_random_questions');
335
        const call = {
336
            methodname: 'mod_quiz_add_random_questions',
337
            args: {
338
                cmid,
339
                addonpage,
340
                randomcount,
341
                filtercondition,
342
                newcategory,
343
                parentcategory,
344
            }
345
        };
346
        try {
347
            const response = await fetchMany([call])[0];
348
            const form = document.querySelector(SELECTORS.FORM_ELEMENT);
349
            const messageInput = form.querySelector(SELECTORS.MESSAGE_INPUT);
350
            messageInput.value = response.message;
351
            form.submit();
352
        } catch (e) {
353
            Notification.exception(e);
354
        }
355
    }
356
 
357
    /**
358
     * Override the modal show function to load the form when this modal is first
359
     * shown.
360
     *
361
     * @method show
362
     */
363
    show() {
364
        super.show(this);
365
 
366
        if (!this.loadedForm) {
367
            this.loadForm(window.location.search);
368
            this.loadedForm = true;
369
        }
370
    }
371
}
372
 
373
ModalAddRandomQuestion.registerModalType();