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
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace qbank_previewquestion;
18
 
19
defined('MOODLE_INTERNAL') || die();
20
 
21
require_once($CFG->dirroot . '/question/editlib.php');
22
 
23
use action_menu;
24
use comment;
25
use context_module;
26
use context;
27
use core\plugininfo\qbank;
28
use core_question\local\bank\edit_menu_column;
29
use core_question\local\bank\view;
30
use core_question\local\bank\question_edit_contexts;
31
use moodle_url;
32
use question_bank;
33
use question_definition;
34
use question_display_options;
35
use question_engine;
36
use stdClass;
37
 
38
/**
39
 * Class helper contains all the helper functions.
40
 *
41
 * @package    qbank_previewquestion
42
 * @copyright  2010 The Open University
43
 * @author     2021 Safat Shahin <safatshahin@catalyst-au.net>
44
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45
 */
46
class helper {
47
 
48
    /**
49
     * Called via pluginfile.php -> question_pluginfile to serve files belonging to
50
     * a question in a question_attempt when that attempt is a preview.
51
     *
52
     * @param stdClass $course course settings object
53
     * @param stdClass $context context object
54
     * @param string $component the name of the component we are serving files for.
55
     * @param string $filearea the name of the file area.
56
     * @param int $qubaid the question_usage this image belongs to.
57
     * @param int $slot the relevant slot within the usage.
58
     * @param array $args the remaining bits of the file path.
59
     * @param bool $forcedownload whether the user must be forced to download the file.
60
     * @param array $fileoptions options for the stored files
61
     * @return void false if file not found, does not return if found - justsend the file
62
     */
63
    public static function question_preview_question_pluginfile($course, $context, $component,
64
            $filearea, $qubaid, $slot, $args, $forcedownload, $fileoptions): void {
65
        global $USER, $DB, $CFG;
66
 
67
        list($context, $course, $cm) = get_context_info_array($context->id);
68
        require_login($course, false, $cm);
69
 
70
        $quba = question_engine::load_questions_usage_by_activity($qubaid);
71
 
72
        if (!question_has_capability_on($quba->get_question($slot, false), 'use')) {
73
            send_file_not_found();
74
        }
75
 
76
        $options = new question_display_options();
77
        $options->feedback = question_display_options::VISIBLE;
78
        $options->numpartscorrect = question_display_options::VISIBLE;
79
        $options->generalfeedback = question_display_options::VISIBLE;
80
        $options->rightanswer = question_display_options::VISIBLE;
81
        $options->manualcomment = question_display_options::VISIBLE;
82
        $options->history = question_display_options::VISIBLE;
83
        if (!$quba->check_file_access($slot, $options, $component,
84
                $filearea, $args, $forcedownload)) {
85
            send_file_not_found();
86
        }
87
 
88
        $fs = get_file_storage();
89
        $relativepath = implode('/', $args);
90
        $fullpath = "/{$context->id}/{$component}/{$filearea}/{$relativepath}";
91
        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
92
            send_file_not_found();
93
        }
94
 
95
        send_stored_file($file, 0, 0, $forcedownload, $fileoptions);
96
    }
97
 
98
    /**
99
     * The the URL to use for actions relating to this preview.
100
     *
101
     * @param int $questionid the question being previewed
102
     * @param int $qubaid the id of the question usage for this preview
103
     * @param question_preview_options $options the options in use
1441 ariadna 104
     * @param context $context module context for the question preview
1 efrain 105
     * @param moodle_url $returnurl url of the page to return to
106
     * @param int|null $restartversion version of the question to use when next restarting the preview.
107
     * @return moodle_url
108
     */
109
    public static function question_preview_action_url($questionid, $qubaid,
110
            question_preview_options $options, $context, $returnurl = null, $restartversion = null): moodle_url {
111
        $params = [
112
                'id' => $questionid,
113
                'previewid' => $qubaid,
114
        ];
1441 ariadna 115
 
116
        if ($context->contextlevel !== CONTEXT_MODULE) {
117
            debugging("Invalid contextlevel: {$context->contextlevel} must be CONTEXT_MODULE");
1 efrain 118
        }
1441 ariadna 119
 
120
        $params['cmid'] = $context->instanceid;
121
 
1 efrain 122
        if ($returnurl !== null) {
123
            $params['returnurl'] = $returnurl;
124
        }
125
        if ($restartversion !== null) {
126
            $params['restartversion'] = $restartversion;
127
        }
128
        $params = array_merge($params, $options->get_url_params());
129
        return new moodle_url('/question/bank/previewquestion/preview.php', $params);
130
    }
131
 
132
    /**
133
     * The the URL to use for actions relating to this preview.
134
     *
135
     * @param int $questionid the question being previewed
1441 ariadna 136
     * @param context $context the current moodle module context
1 efrain 137
     * @param int $previewid optional previewid to sign post saved previewed answers
138
     * @param moodle_url $returnurl url of the page to return to
139
     * @return moodle_url
140
     */
141
    public static function question_preview_form_url($questionid, $context, $previewid = null, $returnurl = null): moodle_url {
142
        $params = [
143
                'id' => $questionid,
144
        ];
1441 ariadna 145
 
146
        if ($context->contextlevel !== CONTEXT_MODULE) {
147
            debugging("Invalid contextlevel: {$context->contextlevel} must be CONTEXT_MODULE");
1 efrain 148
        }
1441 ariadna 149
 
150
        $params['cmid'] = $context->instanceid;
151
 
1 efrain 152
        if ($previewid) {
153
            $params['previewid'] = $previewid;
154
        }
155
        if ($returnurl !== null) {
156
            $params['returnurl'] = $returnurl;
157
        }
158
        return new moodle_url('/question/bank/previewquestion/preview.php', $params);
159
    }
160
 
161
    /**
162
     * Delete the current preview, if any, and redirect to start a new preview.
163
     *
164
     * @param int $previewid id of the preview while restarting it
165
     * @param int $questionid id of the question in preview
166
     * @param object $displayoptions display options for the question in preview
167
     * @param object $context context of the question for preview
168
     * @param moodle_url $returnurl url of the page to return to
169
     * @param int|null $restartversion version of the question to use when next restarting the preview.
170
     * @return void
171
     */
172
    public static function restart_preview($previewid, $questionid, $displayoptions, $context,
173
        $returnurl = null, $restartversion = null): void {
174
        global $DB;
175
 
176
        if ($previewid) {
177
            $transaction = $DB->start_delegated_transaction();
178
            question_engine::delete_questions_usage_by_activity($previewid);
179
            $transaction->allow_commit();
180
        }
181
        redirect(self::question_preview_url($questionid, $displayoptions->behaviour,
182
                $displayoptions->maxmark, $displayoptions, $displayoptions->variant,
183
                $context, $returnurl, $restartversion));
184
    }
185
 
186
    /**
187
     * Generate the URL for starting a new preview of a given question with the given options.
188
     *
189
     * @param integer $questionid the question to preview
190
     * @param string $preferredbehaviour the behaviour to use for the preview
191
     * @param float $maxmark the maximum to mark the question out of
192
     * @param question_display_options $displayoptions the display options to use
193
     * @param int $variant the variant of the question to preview. If null, one will
194
     *      be picked randomly
195
     * @param object $context context to run the preview in (affects things like
196
     *      filter settings, theme, lang, etc.) Defaults to $PAGE->context
197
     * @param moodle_url $returnurl url of the page to return to
198
     * @param int $restartversion The version of the question to use when restarting the preview.
199
     * @return moodle_url the URL
200
     */
201
    public static function question_preview_url($questionid, $preferredbehaviour = null,
202
            $maxmark = null, $displayoptions = null, $variant = null, $context = null, $returnurl = null,
203
            $restartversion = null): moodle_url {
204
 
205
        $params = ['id' => $questionid];
206
 
207
        if (!is_null($restartversion)) {
208
            $params['restartversion'] = $restartversion;
209
        }
210
        if (is_null($context)) {
211
            global $PAGE;
212
            $context = $PAGE->context;
213
        }
214
        if ($context->contextlevel == CONTEXT_MODULE) {
215
            $params['cmid'] = $context->instanceid;
216
        } else if ($context->contextlevel == CONTEXT_COURSE) {
217
            $params['courseid'] = $context->instanceid;
218
        }
219
 
220
        if (!is_null($preferredbehaviour)) {
221
            $params['behaviour'] = $preferredbehaviour;
222
        }
223
 
224
        if (!is_null($maxmark)) {
225
            $params['maxmark'] = format_float($maxmark, -1);
226
        }
227
 
228
        if (!is_null($displayoptions)) {
229
            $params['correctness']     = $displayoptions->correctness;
230
            $params['marks']           = $displayoptions->marks;
231
            $params['markdp']          = $displayoptions->markdp;
232
            $params['feedback']        = (bool) $displayoptions->feedback;
233
            $params['generalfeedback'] = (bool) $displayoptions->generalfeedback;
234
            $params['rightanswer']     = (bool) $displayoptions->rightanswer;
235
            $params['history']         = (bool) $displayoptions->history;
236
        }
237
 
238
        if (!is_null($returnurl)) {
239
            $params['returnurl'] = $returnurl;
240
        }
241
 
242
        if ($variant) {
243
            $params['variant'] = $variant;
244
        }
245
 
246
        return new moodle_url('/question/bank/previewquestion/preview.php', $params);
247
    }
248
 
249
    /**
250
     * Popup params for the question preview.
251
     *
252
     * @return array that can be passed as $params to the {@see popup_action} constructor.
253
     */
254
    public static function question_preview_popup_params(): array {
255
        return [
256
                'height' => 600,
257
                'width' => 800,
258
        ];
259
    }
260
 
261
    /**
262
     * Get the extra elements for preview from qbank plugins.
263
     *
264
     * @param  question_definition $question question definition object
265
     * @param  int $courseid id of the course
266
     * @return array
267
     */
268
    public static function get_preview_extra_elements(question_definition $question, int $courseid): array {
269
        $plugins = get_plugin_list_with_function('qbank', 'preview_display');
270
 
271
        $comment = '';
272
        $extrahtml = [];
273
        foreach ($plugins as $componentname => $plugin) {
274
            $pluginhtml = component_callback($componentname, 'preview_display', [$question, $courseid]);
275
            if ($componentname === 'qbank_comment') {
276
                $comment = $pluginhtml;
277
                continue;
278
            }
279
            $extrahtml[] = $pluginhtml;
280
        }
281
        return [$comment, $extrahtml];
282
    }
283
 
284
    /**
285
     * Checks if question is the latest version.
286
     *
287
     * @param string $version Question version to check
288
     * @param string $questionbankentryid Entry to check against
289
     * @return bool
290
     */
291
    public static function is_latest(string $version, string $questionbankentryid): bool {
292
        global $DB;
293
 
294
        $sql = 'SELECT MAX(version) AS max
295
                  FROM {question_versions}
296
                 WHERE questionbankentryid = ?';
297
        $latestversion = $DB->get_record_sql($sql, [$questionbankentryid]);
298
 
299
        if (isset($latestversion->max)) {
300
            return ($version === $latestversion->max) ? true : false;
301
        }
302
        return false;
303
    }
304
 
305
    /**
306
     * Loads question version ids for current question.
307
     *
308
     * @param  string $questionbankentryid Question bank entry id
309
     * @return array  $questionids Array containing question id as key and version as value.
310
     */
311
    public static function load_versions(string $questionbankentryid): array {
312
        global $DB;
313
 
314
        $questionids = [];
315
        $sql = 'SELECT version, questionid
316
                  FROM {question_versions}
317
                 WHERE questionbankentryid = ?
318
              ORDER BY version';
319
 
320
        $versions = $DB->get_records_sql($sql, [$questionbankentryid]);
321
        foreach ($versions as $key => $version) {
322
            $questionids[$version->questionid] = $key;
323
        }
324
        return $questionids;
325
    }
326
 
327
    /**
328
     * Return the question ID from the array of id => version that corresponds to the requested version.
329
     *
330
     * If the requested version is question_preview_options::ALWAYS_LATEST, this will return the latest version.
331
     *
332
     * @param array $versions
333
     * @param int $restartversion
334
     * @return ?int
335
     */
336
    public static function get_restart_id(array $versions, int $restartversion): ?int {
337
        if ($restartversion === question_preview_options::ALWAYS_LATEST) {
338
            return array_key_last($versions);
339
        } else {
340
            return array_search($restartversion, $versions) ?: null;
341
        }
342
    }
343
}