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
/**
1441 ariadna 17
 * A configuration to provide to the modal.
18
 *
19
 * @typedef {Object} courseItem
20
 *
21
 * @property {String} type The type of element (section, cm).
22
 * @property {Number} id Element ID.
23
 * @property {String} url Element URL.
24
 */
25
 
26
/**
1 efrain 27
 * Module to export parts of the state and transform them to be used in templates
28
 * and as draggable data.
29
 *
30
 * @module     core_courseformat/local/courseeditor/exporter
31
 * @class      core_courseformat/local/courseeditor/exporter
32
 * @copyright  2021 Ferran Recio <ferran@moodle.com>
33
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
35
export default class {
36
 
37
    /**
38
     * Class constructor.
39
     *
40
     * @param {CourseEditor} reactive the course editor object
41
     */
42
    constructor(reactive) {
43
        this.reactive = reactive;
44
 
45
        // Completions states are defined in lib/completionlib.php. There are 4 different completion
46
        // state values, however, the course index uses the same state for complete and complete_pass.
47
        // This is the reason why completed appears twice in the array.
48
        this.COMPLETIONS = ['incomplete', 'complete', 'complete', 'fail'];
49
    }
50
 
51
    /**
52
     * Generate the course export data from the state.
53
     *
54
     * @param {Object} state the current state.
55
     * @returns {Object}
56
     */
57
    course(state) {
58
        // Collect section information from the state.
59
        const data = {
60
            sections: [],
61
            editmode: this.reactive.isEditing,
62
            highlighted: state.course.highlighted ?? '',
63
        };
64
        const sectionlist = this.listedSectionIds(state);
65
        sectionlist.forEach(sectionid => {
66
            const sectioninfo = state.section.get(sectionid) ?? {};
67
            const section = this.section(state, sectioninfo);
68
            data.sections.push(section);
69
        });
70
        data.hassections = (data.sections.length != 0);
71
 
72
        return data;
73
    }
74
 
75
    /**
76
     * Get the IDs of the sections that are listed as regular sections.
77
     * @param {Object} state the current state.
78
     * @returns {Number[]} the list of section ids that are listed.
79
     */
80
    listedSectionIds(state) {
81
        const fullSectionList = state.course.sectionlist ?? [];
82
        return fullSectionList.filter(sectionid => {
83
            const sectioninfo = state.section.get(sectionid) ?? {};
84
            // Delegated sections (controlled by a component) are not listed in course.
85
            return sectioninfo.component === null;
86
        });
87
    }
88
 
89
    /**
90
     * Generate a section export data from the state.
91
     *
92
     * @param {Object} state the current state.
93
     * @param {Object} sectioninfo the section state data.
94
     * @returns {Object}
95
     */
96
    section(state, sectioninfo) {
97
        const section = {
98
            ...sectioninfo,
99
            highlighted: state.course.highlighted ?? '',
100
            cms: [],
101
        };
102
        const cmlist = sectioninfo.cmlist ?? [];
103
        cmlist.forEach(cmid => {
104
            const cminfo = state.cm.get(cmid);
105
            const cm = this.cm(state, cminfo);
106
            section.cms.push(cm);
107
        });
108
        section.hascms = (section.cms.length != 0);
109
 
110
        return section;
111
    }
112
 
113
    /**
114
     * Generate a cm export data from the state.
115
     *
116
     * @param {Object} state the current state.
117
     * @param {Object} cminfo the course module state data.
118
     * @returns {Object}
119
     */
120
    cm(state, cminfo) {
121
        const cm = {
122
            ...cminfo,
123
            isactive: false,
1441 ariadna 124
            sectioninfo: false, // Init to false to prevent mustache recursion loops.
1 efrain 125
        };
1441 ariadna 126
        if (cminfo.hasdelegatedsection) {
127
            const sectioninfo = state.section.get(cminfo.delegatesectionid);
128
            cm.sectioninfo = this.section(state, sectioninfo);
129
        }
1 efrain 130
        return cm;
131
    }
132
 
133
    /**
134
     * Generate a dragable cm data structure.
135
     *
136
     * This method is used by any draggable course module element to generate drop data
137
     * for its reactive/dragdrop instance.
138
     *
139
     * @param {*} state the state object
140
     * @param {*} cmid the cours emodule id
141
     * @returns {Object|null}
142
     */
143
    cmDraggableData(state, cmid) {
144
        const cminfo = state.cm.get(cmid);
145
        if (!cminfo) {
146
            return null;
147
        }
148
 
149
        // Drop an activity over the next activity is the same as doing anything.
150
        let nextcmid;
151
        const section = state.section.get(cminfo.sectionid);
152
        const currentindex = section?.cmlist.indexOf(cminfo.id);
153
        if (currentindex !== undefined) {
154
            nextcmid = section?.cmlist[currentindex + 1];
155
        }
156
 
157
        return {
158
            type: 'cm',
159
            id: cminfo.id,
160
            name: cminfo.name,
161
            sectionid: cminfo.sectionid,
1441 ariadna 162
            hasdelegatedsection: cminfo.hasdelegatedsection,
1 efrain 163
            nextcmid,
164
        };
165
    }
166
 
167
    /**
168
     * Generate a dragable cm data structure.
169
     *
170
     * This method is used by any draggable section element to generate drop data
171
     * for its reactive/dragdrop instance.
172
     *
173
     * @param {*} state the state object
174
     * @param {*} sectionid the cours section id
175
     * @returns {Object|null}
176
     */
177
    sectionDraggableData(state, sectionid) {
178
        const sectioninfo = state.section.get(sectionid);
179
        if (!sectioninfo) {
180
            return null;
181
        }
182
        return {
183
            type: 'section',
184
            id: sectioninfo.id,
185
            name: sectioninfo.name,
186
            number: sectioninfo.number,
187
        };
188
    }
189
 
190
    /**
191
     * Generate a file draggable structure.
192
     *
193
     * This method is used when files are dragged on the browser.
194
     *
195
     * @param {*} state the state object
196
     * @param {*} dataTransfer the current data tranfer data
197
     * @returns {Object|null}
198
     */
199
    fileDraggableData(state, dataTransfer) {
200
        const files = [];
201
        // Browsers do not provide the file list until the drop event.
202
        if (dataTransfer.files?.length > 0) {
203
            dataTransfer.files.forEach(file => {
204
                files.push(file);
205
            });
206
        }
207
        return {
208
            type: 'files',
209
            files,
210
        };
211
    }
212
 
213
    /**
214
     * Generate a completion export data from the cm element.
215
     *
216
     * @param {Object} state the current state.
217
     * @param {Object} cminfo the course module state data.
218
     * @returns {Object}
219
     */
220
    cmCompletion(state, cminfo) {
221
        const data = {
222
            statename: '',
223
            state: 'NaN',
224
        };
225
        if (cminfo.completionstate !== undefined) {
226
            data.state = cminfo.completionstate;
227
            data.hasstate = true;
228
            let statename = this.COMPLETIONS[cminfo.completionstate] ?? 'NaN';
229
            if (cminfo.isoverallcomplete !== undefined && cminfo.isoverallcomplete === true) {
230
                statename = 'complete';
231
            }
232
            data[`is${statename}`] = true;
233
        }
234
        return data;
235
    }
236
 
237
    /**
238
     * Return a sorted list of all sections and cms items in the state.
239
     *
240
     * @param {Object} state the current state.
1441 ariadna 241
     * @returns {courseItem[]} all sections and cms items in the state.
1 efrain 242
     */
243
    allItemsArray(state) {
244
        const items = [];
245
        const sectionlist = state.course.sectionlist ?? [];
246
        // Add sections.
247
        sectionlist.forEach(sectionid => {
248
            const sectioninfo = state.section.get(sectionid);
1441 ariadna 249
            // Skip delegated sections because components are responsible for them.
250
            if (sectioninfo.component !== null) {
251
                return;
252
            }
253
 
254
            items.push({
255
                type: 'section',
256
                id: sectioninfo.id,
257
                url: sectioninfo.sectionurl
258
            });
1 efrain 259
            // Add cms.
260
            const cmlist = sectioninfo.cmlist ?? [];
261
            cmlist.forEach(cmid => {
1441 ariadna 262
                const cmInfo = state.cm.get(cmid);
263
                items.push(...this.cmItemsArray(state, cmInfo));
1 efrain 264
            });
265
        });
266
        return items;
267
    }
268
 
269
    /**
1441 ariadna 270
     * Return a list of all items associated with an activity.
271
     *
272
     * @private
273
     * @param {Object} state the full current state.
274
     * @param {Object} cmInfo the course module state data.
275
     * @return {courseItem[]} the items array associated with that cm.
276
     */
277
    cmItemsArray(state, cmInfo) {
278
        // Activities with delegated sections are exported as sections.
279
        if (cmInfo.hasdelegatedsection) {
280
            const items = [];
281
            const delegatedsection = state.section.get(cmInfo.delegatesectionid);
282
            items.push({
283
                type: 'section',
284
                id: delegatedsection.id,
285
                url: delegatedsection.sectionurl
286
            });
287
            const delegatedCmlist = delegatedsection.cmlist ?? [];
288
            delegatedCmlist.forEach(cmid => {
289
                const cmInfo = state.cm.get(cmid);
290
                items.push({
291
                    type: 'cm',
292
                    id: cmInfo.id,
293
                    url: cmInfo.url
294
                });
295
            });
296
            return items;
297
        }
298
 
299
        return [
300
            {type: 'cm', id: cmInfo.id, url: cmInfo.url},
301
        ];
302
    }
303
 
304
    /**
1 efrain 305
     * Check is some activities of a list can be stealth.
306
     *
307
     * @param {Object} state the current state.
308
     * @param {Number[]} cmIds the module ids to check
309
     * @returns {Boolean} if any of the activities can be stealth.
310
     */
311
    canUseStealth(state, cmIds) {
312
        return cmIds.some(cmId => {
313
            const cminfo = state.cm.get(cmId);
314
            return cminfo?.allowstealth ?? false;
315
        });
316
    }
317
}