Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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
 * Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an
18
 * LTI tool provider that supports Content-Item type message.
19
 *
20
 * See template: mod_lti/contentitem
21
 *
22
 * @module     mod_lti/contentitem
23
 * @copyright  2016 Jun Pataleta <jun@moodle.com>
24
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 * @since      3.2
26
 */
27
define(
28
    [
29
        'jquery',
30
        'core/notification',
31
        'core/str',
32
        'core/templates',
33
        'mod_lti/form-field',
34
        'core/modal',
35
        'core/modal_events'
36
    ],
37
    function($, notification, str, templates, FormField, Modal, ModalEvents) {
38
        var dialogue;
39
        var doneCallback;
40
        var contentItem = {
41
            /**
42
             * Init function.
43
             *
44
             * @param {string} url The URL for the content item selection.
45
             * @param {object} postData The data to be sent for the content item selection request.
46
             * @param {Function} cb The callback to run once the content item has been processed.
47
             */
48
            init: function(url, postData, cb) {
49
                doneCallback = cb;
50
                var context = {
51
                    url: url,
52
                    postData: postData
53
                };
54
                var bodyPromise = templates.render('mod_lti/contentitem', context);
55
 
56
                if (dialogue) {
57
                    // Set dialogue body.
58
                    dialogue.setBody(bodyPromise);
59
                    // Display the dialogue.
60
                    dialogue.show();
61
                    return;
62
                }
63
 
64
                str.get_string('selectcontent', 'lti').then(function(title) {
65
                    return Modal.create({
66
                        title: title,
67
                        body: bodyPromise,
68
                        large: true,
69
                        show: true,
70
                    });
71
                }).then(function(modal) {
72
                    dialogue = modal;
73
                    // On hide handler.
74
                    modal.getRoot().on(ModalEvents.hidden, function() {
75
                        // Empty modal contents when it's hidden.
76
                        modal.setBody('');
77
 
78
                        // Fetch notifications.
79
                        notification.fetchNotifications();
80
                    });
81
                    return;
82
                }).catch(notification.exception);
83
            }
84
        };
85
 
86
        /**
87
         * Array of form fields for LTI tool configuration.
88
         */
89
        var ltiFormFields = [
90
            new FormField('selectcontentstatus', FormField.TYPES.TEXT, true, ''),
91
            new FormField('name', FormField.TYPES.TEXT, false, ''),
92
            new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),
93
            new FormField('toolurl', FormField.TYPES.TEXT, true, ''),
94
            new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''),
95
            new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true),
96
            new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true),
97
            new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true),
98
            new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''),
99
            new FormField('icon', FormField.TYPES.TEXT, true, ''),
100
            new FormField('secureicon', FormField.TYPES.TEXT, true, ''),
101
            new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0),
102
            new FormField('grade_modgrade_point', FormField.TYPES.TEXT, false, ''),
103
            new FormField('lineitemresourceid', FormField.TYPES.TEXT, true, ''),
104
            new FormField('lineitemtag', FormField.TYPES.TEXT, true, ''),
105
            new FormField('lineitemsubreviewurl', FormField.TYPES.TEXT, true, ''),
106
            new FormField('lineitemsubreviewparams', FormField.TYPES.TEXT, true, '')
107
        ];
108
 
109
        /**
110
         * Hide the element, including aria and tab index.
111
         * @param {HTMLElement} e the element to be hidden.
112
         */
113
        const hideElement = (e) => {
114
            e.setAttribute('hidden', 'true');
115
            e.setAttribute('aria-hidden', 'true');
116
            e.setAttribute('tab-index', '-1');
117
        };
118
 
119
        /**
120
         * Show the element, including aria and tab index (set to 1).
121
         * @param {HTMLElement} e the element to be shown.
122
         */
123
        const showElement = (e) => {
124
            e.removeAttribute('hidden');
125
            e.setAttribute('aria-hidden', 'false');
126
            e.setAttribute('tab-index', '1');
127
        };
128
 
129
        /**
130
         * When more than one item needs to be added, the UI is simplified
131
         * to just list the items to be added. Form is hidden and the only
132
         * options is (save and return to course) or cancel.
133
         * This function injects the summary to the form page, and hides
134
         * the unneeded elements.
135
         * @param {Object[]} items items to be added to the course.
136
         */
137
        const showMultipleSummaryAndHideForm = async function(items) {
138
            const form = document.querySelector('#region-main-box form');
139
            const toolArea = form.querySelector('[data-attribute="dynamic-import"]');
140
            const buttonGroup = form.querySelector('#fgroup_id_buttonar');
141
            const submitAndLaunch = form.querySelector('#id_submitbutton');
142
            Array.from(form.children).forEach(hideElement);
143
            hideElement(submitAndLaunch);
144
            const {html, js} = await templates.renderForPromise('mod_lti/tool_deeplinking_results',
145
                {items: items});
146
 
147
            await templates.replaceNodeContents(toolArea, html, js);
148
            showElement(toolArea);
149
            showElement(buttonGroup);
150
        };
151
 
152
        /**
153
         * Transforms config values aimed at populating the lti mod form to JSON variant
154
         * which are used to insert more than one activity modules in one submit
155
         * by applying variation to the submitted form.
156
         * See /course/modedit.php.
157
         * @private
158
         * @param {Object} config transforms a config to an actual form data to be posted.
159
         * @return {Object} variant that will be used to modify form values on submit.
160
         */
161
        var configToVariant = (config) => {
162
            const variant = {};
163
            ['name', 'toolurl', 'securetoolurl', 'instructorcustomparameters', 'icon', 'secureicon',
164
                'launchcontainer', 'lineitemresourceid', 'lineitemtag', 'lineitemsubreviewurl',
165
                'lineitemsubreviewparams', 'selectcontentstatus'].forEach(
166
                function(name) {
167
                    variant[name] = config[name] || '';
168
                }
169
            );
170
            variant['introeditor[text]'] = config.introeditor ? config.introeditor.text : '';
171
            variant['introeditor[format]'] = config.introeditor ? config.introeditor.format : '';
172
            if (config.instructorchoiceacceptgrades === 1) {
173
                variant.instructorchoiceacceptgrades = '1';
174
                variant['grade[modgrade_point]'] = config.grade_modgrade_point || '100';
175
            } else {
176
                variant.instructorchoiceacceptgrades = '0';
177
            }
178
            return variant;
179
        };
180
 
181
        /**
182
         * Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.
183
         * If the return data contains more than one item, the form will not be populated with item data
184
         * but rather hidden, and the item data will be added to a single input field used to create multiple
185
         * instances in one request.
186
         *
187
         * @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.
188
         */
189
        window.processContentItemReturnData = function(returnData) {
190
            if (dialogue) {
191
                dialogue.hide();
192
            }
193
            var index;
194
            if (returnData.multiple) {
195
                for (index in ltiFormFields) {
196
                    // Name is required, so putting a placeholder as it will not be used
197
                    // in multi-items add.
198
                    ltiFormFields[index].setFieldValue(ltiFormFields[index].name === 'name' ? 'item' : null);
199
                }
200
                var variants = [];
201
                returnData.multiple.forEach(function(v) {
202
                    variants.push(configToVariant(v));
203
                });
204
                showMultipleSummaryAndHideForm(returnData.multiple);
205
                const submitAndCourse = document.querySelector('#id_submitbutton2');
206
                submitAndCourse.onclick = (e) => {
207
                    e.preventDefault();
208
                    submitAndCourse.disabled = true;
209
                    const fd = new FormData(document.querySelector('#region-main-box form'));
210
                    const postVariant = (promise, variant) => {
211
                        Object.entries(variant).forEach((entry) => fd.set(entry[0], entry[1]));
212
                        const body = new URLSearchParams(fd);
213
                        const doPost = () => fetch(document.location.pathname, {method: 'post', body});
214
                        return promise.then(doPost).catch(doPost);
215
                    };
216
                    const backToCourse = () => {
217
                        document.querySelector("#id_cancel").click();
218
                    };
219
                    variants.reduce(postVariant, Promise.resolve()).then(backToCourse).catch(backToCourse);
220
                };
221
            } else {
222
                // Populate LTI configuration fields from return data.
223
                for (index in ltiFormFields) {
224
                    var field = ltiFormFields[index];
225
                    var value = null;
226
                    if (typeof returnData[field.name] !== 'undefined') {
227
                        value = returnData[field.name];
228
                    }
229
                    field.setFieldValue(value);
230
                }
231
                field.setFieldValue(value);
232
 
233
                // Update the UI element which signifies content has been selected.
234
                document.querySelector("#id_selectcontentindicator").innerHTML = returnData.selectcontentindicator;
235
            }
236
 
237
            if (doneCallback) {
238
                doneCallback(returnData);
239
            }
240
        };
241
 
242
        return contentItem;
243
    }
244
);