Proyectos de Subversion Moodle

Rev

| 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
 * JavaScript for the random_question_form_preview of the
18
 * add_random_form class.
19
 *
20
 * @module    mod_quiz/random_question_form_preview
21
 * @copyright 2018 Ryan Wyllie <ryan@moodle.com>
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
define(
25
    [
26
        'jquery',
27
        'core/ajax',
28
        'core/str',
29
        'core/notification',
30
        'core/templates',
31
        'core/paged_content_factory'
32
    ],
33
    function(
34
        $,
35
        Ajax,
36
        Str,
37
        Notification,
38
        Templates,
39
        PagedContentFactory
40
    ) {
41
 
42
    var ITEMS_PER_PAGE = 5;
43
    var TEMPLATE_NAME = 'mod_quiz/random_question_form_preview_question_list';
44
    var SELECTORS = {
45
        LOADING_ICON_CONTAINER: '[data-region="overlay-icon-container"]',
46
        QUESTION_COUNT_CONTAINER: '[data-region="question-count-container"]',
47
        QUESTION_LIST_CONTAINER: '[data-region="question-list-container"]'
48
    };
49
 
50
    /**
51
     * Show the loading spinner over the preview section.
52
     *
53
     * @param  {jquery} root The root element.
54
     */
55
    var showLoadingIcon = function(root) {
56
        root.find(SELECTORS.LOADING_ICON_CONTAINER).removeClass('hidden');
57
    };
58
 
59
    /**
60
     * Hide the loading spinner.
61
     *
62
     * @param  {jquery} root The root element.
63
     */
64
    var hideLoadingIcon = function(root) {
65
        root.find(SELECTORS.LOADING_ICON_CONTAINER).addClass('hidden');
66
    };
67
 
68
    /**
69
     * Render the section of text to show the question count.
70
     *
71
     * @param  {jquery} root The root element.
72
     * @param  {int} questionCount The number of questions.
73
     */
74
    var renderQuestionCount = function(root, questionCount) {
75
        Str.get_string('questionsmatchingfilter', 'mod_quiz', questionCount)
76
            .then(function(string) {
77
                root.find(SELECTORS.QUESTION_COUNT_CONTAINER).html(string);
78
                return;
79
            })
80
            .fail(Notification.exception);
81
    };
82
 
83
    /**
84
     * Send a request to the server for more questions.
85
     *
86
     * @param  {int} categoryId A question category id.
87
     * @param  {bool} includeSubcategories If the results should include subcategory questions
88
     * @param  {int[]} tagIds The list of tag ids that each question must have.
89
     * @param  {int} contextId The context where the questions will be added.
90
     * @param  {int} limit How many questions to retrieve.
91
     * @param  {int} offset How many questions to skip from the start of the result set.
92
     * @return {promise} Resolved when the preview section has rendered.
93
     */
94
    var requestQuestions = function(
95
        categoryId,
96
        includeSubcategories,
97
        tagIds,
98
        contextId,
99
        limit,
100
        offset
101
    ) {
102
        var request = {
103
            methodname: 'core_question_get_random_question_summaries',
104
            args: {
105
                categoryid: categoryId,
106
                includesubcategories: includeSubcategories,
107
                tagids: tagIds,
108
                contextid: contextId,
109
                limit: limit,
110
                offset: offset
111
            }
112
        };
113
 
114
        return Ajax.call([request])[0];
115
    };
116
 
117
    /**
118
     * Build a paged content widget for questions with the given criteria. The
119
     * criteria is used to fetch more questions from the server as the user
120
     * requests new pages.
121
     *
122
     * @param  {int} categoryId A question category id.
123
     * @param  {bool} includeSubcategories If the results should include subcategory questions
124
     * @param  {int[]} tagIds The list of tag ids that each question must have.
125
     * @param  {int} contextId The context where the questions will be added.
126
     * @param  {int} totalQuestionCount How many questions match the criteria above.
127
     * @param  {object[]} firstPageQuestions List of questions for the first page.
128
     * @return {promise} A promise resolved with the HTML and JS for the paged content.
129
     */
130
    var renderQuestionsAsPagedContent = function(
131
        categoryId,
132
        includeSubcategories,
133
        tagIds,
134
        contextId,
135
        totalQuestionCount,
136
        firstPageQuestions
137
    ) {
138
        // Provide a callback, renderQuestionsPages,
139
        // to control how the questions on each page are rendered.
140
        return PagedContentFactory.createFromAjax(
141
            totalQuestionCount,
142
            ITEMS_PER_PAGE,
143
            // Callback function to render the requested pages.
144
            function(pagesData) {
145
                return pagesData.map(function(pageData) {
146
                    var limit = pageData.limit;
147
                    var offset = pageData.offset;
148
 
149
                    if (offset == 0) {
150
                        // The first page is being requested and we've already got
151
                        // that data so we can just render it immediately.
152
                        return Templates.render(TEMPLATE_NAME, {questions: firstPageQuestions});
153
                    } else {
154
                        // Otherwise we need to ask the server for the data.
155
                        return requestQuestions(
156
                            categoryId,
157
                            includeSubcategories,
158
                            tagIds,
159
                            contextId,
160
                            limit,
161
                            offset
162
                        )
163
                        .then(function(response) {
164
                            var questions = response.questions;
165
                            return Templates.render(TEMPLATE_NAME, {questions: questions});
166
                        })
167
                        .fail(Notification.exception);
168
                    }
169
                });
170
            }
171
        );
172
    };
173
 
174
    /**
175
     * Re-render the preview section based on the provided filter criteria.
176
     *
177
     * @param  {jquery} root The root element.
178
     * @param  {int} categoryId A question category id.
179
     * @param  {bool} includeSubcategories If the results should include subcategory questions
180
     * @param  {int[]} tagIds The list of tag ids that each question must have.
181
     * @param  {int} contextId The context where the questions will be added.
182
     * @return {promise} Resolved when the preview section has rendered.
183
     */
184
    var reload = function(root, categoryId, includeSubcategories, tagIds, contextId) {
185
        // Show the loading spinner to tell the user that something is happening.
186
        showLoadingIcon(root);
187
        // Load the first set of questions.
188
        return requestQuestions(categoryId, includeSubcategories, tagIds, contextId, ITEMS_PER_PAGE, 0)
189
            .then(function(response) {
190
                var totalCount = response.totalcount;
191
                // Show the help message for the user to indicate how many questions
192
                // match their filter criteria.
193
                renderQuestionCount(root, totalCount);
194
                return response;
195
            })
196
            .then(function(response) {
197
                var totalQuestionCount = response.totalcount;
198
                var questions = response.questions;
199
 
200
                if (questions.length) {
201
                    // We received some questions so render them as paged content
202
                    // with a paging bar.
203
                    return renderQuestionsAsPagedContent(
204
                        categoryId,
205
                        includeSubcategories,
206
                        tagIds,
207
                        contextId,
208
                        totalQuestionCount,
209
                        questions
210
                    );
211
                } else {
212
                    // If we didn't receive any questions then we can return empty
213
                    // HTML and JS to clear the preview section.
214
                    return $.Deferred().resolve('', '');
215
                }
216
            })
217
            .then(function(html, js) {
218
                // Show the user the question set.
219
                var container = root.find(SELECTORS.QUESTION_LIST_CONTAINER);
220
                Templates.replaceNodeContents(container, html, js);
221
                return;
222
            })
223
            .always(function() {
224
                hideLoadingIcon(root);
225
            })
226
            .fail(Notification.exception);
227
    };
228
 
229
    return {
230
        reload: reload,
231
        showLoadingIcon: showLoadingIcon,
232
        hideLoadingIcon: hideLoadingIcon
233
    };
234
});