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
/**
18
 * This page displays a preview of a question
19
 *
20
 * The preview uses the option settings from the activity within which the question
21
 * is previewed or the default settings if no activity is specified. The question session
22
 * information is stored in the session as an array of subsequent states rather
23
 * than in the database.
24
 *
25
 * @package    qbank_previewquestion
26
 * @copyright  Alex Smith {@link http://maths.york.ac.uk/serving_maths}
27
 * @author     2021 Safat Shahin <safatshahin@catalyst-au.net> and numerous contributors.
28
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29
 */
30
 
31
require_once(__DIR__ . '/../../../config.php');
32
require_once($CFG->libdir . '/questionlib.php');
33
 
34
use \core\notification;
35
use qbank_previewquestion\form\preview_options_form;
36
use qbank_previewquestion\question_preview_options;
37
use qbank_previewquestion\helper;
1441 ariadna 38
use core_question\local\bank\version_options;
1 efrain 39
 
40
/**
41
 * The maximum number of variants previewable. If there are more variants than this for a question
42
 * then we only allow the selection of the first x variants.
43
 *
44
 * @var integer
45
 */
46
define('QUESTION_PREVIEW_MAX_VARIANTS', 100);
47
 
48
\core_question\local\bank\helper::require_plugin_enabled('qbank_previewquestion');
49
 
50
// Get and validate question id.
51
$id = required_param('id', PARAM_INT);
52
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
53
$restartversion = optional_param('restartversion', question_preview_options::ALWAYS_LATEST, PARAM_INT);
54
 
55
$question = question_bank::load_question($id);
56
 
57
if ($returnurl) {
58
    $returnurl = new moodle_url($returnurl);
59
}
60
 
61
// Were we given a particular context to run the question in?
62
// This affects things like filter settings, or forced theme or language.
63
if ($cmid = optional_param('cmid', 0, PARAM_INT)) {
64
    $cm = get_coursemodule_from_id(false, $cmid);
65
    require_login($cm->course, false, $cm);
66
    $context = context_module::instance($cmid);
67
} else {
68
    require_login();
69
    $category = $DB->get_record('question_categories', ['id' => $question->category], '*', MUST_EXIST);
70
    $context = context::instance_by_id($category->contextid);
71
    $PAGE->set_context($context);
72
    // Note that in the other cases, require_login will set the correct page context.
73
}
74
question_require_capability_on($question, 'use');
75
$PAGE->set_pagelayout('popup');
76
 
77
// Get and validate display options.
78
$maxvariant = min($question->get_num_variants(), QUESTION_PREVIEW_MAX_VARIANTS);
79
$options = new question_preview_options($question);
80
$options->load_user_defaults();
81
$options->set_from_request();
82
$options->versioninfo = false;
83
$PAGE->set_url(helper::question_preview_url($id, $options->behaviour, $options->maxmark,
84
        $options, $options->variant, $context, null, $restartversion));
85
 
86
// Get and validate existing preview, or start a new one.
87
$previewid = optional_param('previewid', 0, PARAM_INT);
88
 
89
if ($previewid) {
90
    try {
91
        $quba = question_engine::load_questions_usage_by_activity($previewid);
92
 
93
    } catch (Exception $e) {
94
        // This may not seem like the right error message to display, but
95
        // actually from the user point of view, it makes sense.
96
        throw new moodle_exception('submissionoutofsequencefriendlymessage', 'question',
97
                helper::question_preview_url($question->id, $options->behaviour,
98
                        $options->maxmark, $options, $options->variant, $context, null, $restartversion), null, $e);
99
    }
100
 
101
    if ($quba->get_owning_context()->instanceid != $USER->id) {
102
        throw new moodle_exception('notyourpreview', 'question');
103
    }
104
 
105
    $slot = $quba->get_first_question_number();
106
    $usedquestion = $quba->get_question($slot, false);
107
    if ($usedquestion->id != $question->id) {
108
        throw new moodle_exception('questionidmismatch', 'question');
109
    }
110
    $question = $usedquestion;
111
    $options->variant = $quba->get_variant($slot);
112
 
113
} else {
114
    $quba = question_engine::make_questions_usage_by_activity(
115
            'core_question_preview', context_user::instance($USER->id));
116
    $quba->set_preferred_behaviour($options->behaviour);
117
    $slot = $quba->add_question($question, $options->maxmark);
118
 
119
    if ($options->variant) {
120
        $options->variant = min($maxvariant, max(1, $options->variant));
121
    } else {
122
        $options->variant = rand(1, $maxvariant);
123
    }
124
 
125
    $quba->start_question($slot, $options->variant);
126
 
127
    $transaction = $DB->start_delegated_transaction();
128
    question_engine::save_questions_usage_by_activity($quba);
129
    $transaction->allow_commit();
130
}
131
$options->behaviour = $quba->get_preferred_behaviour();
132
$options->maxmark = $quba->get_question_max_mark($slot);
133
 
1441 ariadna 134
// Load the question versions and convert to a simple array for select menu.
135
$versions = version_options::get_version_menu_options($question->id);
1 efrain 136
$versionids = helper::load_versions($question->questionbankentryid);
137
// Create the settings form, and initialise the fields.
138
$optionsform = new preview_options_form(helper::question_preview_form_url($question->id, $context, $previewid, $returnurl),
139
        [
140
            'quba' => $quba,
141
            'maxvariant' => $maxvariant,
1441 ariadna 142
            'versions' => $versions,
1 efrain 143
            'restartversion' => $restartversion,
144
        ]);
145
$optionsform->set_data($options);
146
 
147
// Process change of settings, if that was requested.
148
if ($newoptions = $optionsform->get_submitted_data()) {
149
    // Set user preferences.
150
    $options->save_user_preview_options($newoptions);
151
    if (!isset($newoptions->variant)) {
152
        $newoptions->variant = $options->variant;
153
    }
154
    $questionid = helper::get_restart_id($versionids, $restartversion);
155
    if (isset($newoptions->saverestart)) {
156
        helper::restart_preview($previewid, $questionid, $newoptions, $context, $returnurl, $newoptions->restartversion);
157
    }
158
}
159
 
160
// Prepare a URL that is used in various places.
161
$actionurl = helper::question_preview_action_url($question->id, $quba->get_id(), $options, $context, $returnurl, $restartversion);
162
 
163
// Process any actions from the buttons at the bottom of the form.
164
if (data_submitted() && confirm_sesskey()) {
165
 
166
    try {
167
 
168
        if (optional_param('restart', false, PARAM_BOOL)) {
169
            $questionid = helper::get_restart_id($versionids, $restartversion);
170
            helper::restart_preview($previewid, $questionid, $options, $context, $returnurl, $restartversion);
171
 
172
        } else if (optional_param('fill', null, PARAM_BOOL)) {
173
            $correctresponse = $quba->get_correct_response($slot);
174
            if (!is_null($correctresponse)) {
175
                $quba->process_action($slot, $correctresponse);
176
 
177
                $transaction = $DB->start_delegated_transaction();
178
                question_engine::save_questions_usage_by_activity($quba);
179
                $transaction->allow_commit();
180
            }
181
            redirect($actionurl);
182
 
183
        } else if (optional_param('finish', null, PARAM_BOOL)) {
184
            $quba->process_all_actions();
185
            $quba->finish_all_questions();
186
 
187
            $transaction = $DB->start_delegated_transaction();
188
            question_engine::save_questions_usage_by_activity($quba);
189
            $transaction->allow_commit();
190
            redirect($actionurl);
191
 
192
        } else {
193
            $quba->process_all_actions();
194
 
195
            $transaction = $DB->start_delegated_transaction();
196
            question_engine::save_questions_usage_by_activity($quba);
197
            $transaction->allow_commit();
198
 
199
            $mdlscrollto = optional_param('mdlscrollto', '', PARAM_RAW);
200
            if ($mdlscrollto !== '') {
201
                $actionurl->param('mdlscrollto', (int) $mdlscrollto);
202
            }
203
            redirect($actionurl);
204
        }
205
 
206
    } catch (question_out_of_sequence_exception $e) {
207
        throw new moodle_exception('submissionoutofsequencefriendlymessage', 'question', $actionurl);
208
 
209
    } catch (Exception $e) {
210
        // This sucks, if we display our own custom error message, there is no way
211
        // to display the original stack trace.
212
        $debuginfo = '';
213
        if (!empty($e->debuginfo)) {
214
            $debuginfo = $e->debuginfo;
215
        }
216
        throw new moodle_exception('errorprocessingresponses', 'question', $actionurl,
217
                $e->getMessage(), $debuginfo);
218
    }
219
}
220
 
221
if ($question->length) {
222
    $displaynumber = '1';
223
} else {
224
    $displaynumber = 'i';
225
}
226
$restartdisabled = [];
227
$finishdisabled = [];
228
$filldisabled = [];
229
if ($quba->get_question_state($slot)->is_finished()) {
230
    $finishdisabled = ['disabled' => 'disabled'];
231
    $filldisabled = ['disabled' => 'disabled'];
232
}
233
// If question type cannot give us a correct response, disable this button.
234
if (is_null($quba->get_correct_response($slot))) {
235
    $filldisabled = ['disabled' => 'disabled'];
236
}
237
if (!$previewid) {
238
    $restartdisabled = ['disabled' => 'disabled'];
239
}
240
 
241
// Prepare technical info to be output.
242
$qa = $quba->get_question_attempt($slot);
243
$technical = [];
244
$technical[] = get_string('behaviourbeingused', 'question',
245
        question_engine::get_behaviour_name($qa->get_behaviour_name()));
246
$technical[] = get_string('technicalinfominfraction', 'question', $qa->get_min_fraction());
247
$technical[] = get_string('technicalinfomaxfraction', 'question', $qa->get_max_fraction());
248
$technical[] = get_string('technicalinfovariant', 'question', $qa->get_variant());
249
$technical[] = get_string('technicalinfoquestionsummary', 'question', s($qa->get_question_summary()));
250
$technical[] = get_string('technicalinforightsummary', 'question', s($qa->get_right_answer_summary()));
251
$technical[] = get_string('technicalinforesponsesummary', 'question', s($qa->get_response_summary()));
252
$technical[] = get_string('technicalinfostate', 'question', '' . $qa->get_state());
253
 
254
// Start output.
255
$title = get_string('previewquestion', 'question', format_string($question->name));
256
$headtags = question_engine::initialise_js() . $quba->render_question_head_html($slot);
257
$PAGE->set_title($title);
258
$PAGE->set_heading($title);
259
echo $OUTPUT->header();
260
 
261
$previewdata = [];
262
 
263
$previewdata['questionicon'] = print_question_icon($question);
264
$previewdata['questionidumber'] = $question->idnumber;
265
$previewdata['questiontitle'] = $question->name;
266
$versioninfo = new \core_question\output\question_version_info($question);
267
$previewdata['versiontitle'] = $versioninfo->export_for_template($OUTPUT);
268
if ($versioninfo->version !== $versioninfo->latestversion) {
269
    if ($restartversion == question_preview_options::ALWAYS_LATEST) {
270
        $newerversionparams = (object) [
271
            'currentversion' => $question->version,
272
            'latestversion' => max($versionids),
273
            'restartbutton' => $OUTPUT->render_from_template('qbank_previewquestion/restartbutton', []),
274
        ];
275
        $newversionurl = clone $actionurl;
276
        $newversionurl->param('restart', 1);
277
        $previewdata['newerversionurl'] = $newversionurl;
278
        $previewdata['newerversion'] = get_string('newerversion', 'qbank_previewquestion', $newerversionparams);
279
    }
280
}
281
 
282
$previewdata['actionurl'] = $actionurl;
283
$previewdata['session'] = sesskey();
284
$previewdata['slot'] = $slot;
285
// Output of the question.
286
$previewdata['question'] = $quba->render_question($slot, $options, $displaynumber);
287
$previewdata['restartdisabled'] = html_writer::attributes($restartdisabled);
288
$previewdata['finishdisabled'] = html_writer::attributes($finishdisabled);
289
$previewdata['filldisabled'] = html_writer::attributes($filldisabled);
290
// Output the technical info.
291
$previewdata['techinfo'] = print_collapsible_region_start('', 'techinfo', get_string('technicalinfo', 'question'),
292
        'core_question_preview_techinfo_collapsed', true, true, $OUTPUT->help_icon('technicalinfo', 'question'));
293
foreach ($technical as $info) {
294
    $previewdata['techinfo'] .= html_writer::tag('p', $info, ['class' => 'notifytiny']);
295
}
296
$previewdata['techinfo'] .= print_collapsible_region_end(true);
297
 
298
// Display the settings form.
299
$previewdata['options'] = $optionsform->render();
300
 
301
list($comment, $extraelements) = helper::get_preview_extra_elements($question, $COURSE->id);
302
 
303
if (!empty($comment)) {
304
    $previewdata['comments'] = $comment;
305
}
306
 
307
if (!empty($extraelements)) {
308
    $elements = [];
309
    foreach ($extraelements as $extraelement) {
310
        $element = new stdClass();
311
        $element->extrapreviewelements = $extraelement;
312
        $elements[] = $element;
313
    }
314
    $previewdata['extrapreviewelements'] = $elements;
315
}
316
 
317
$previewdata['redirect'] = false;
318
if (!is_null($returnurl)) {
319
    $previewdata['redirect'] = true;
320
    $previewdata['redirecturl'] = $returnurl;
321
}
1441 ariadna 322
$closeurl = new moodle_url('/question/edit.php', ['cmid' => $context->instanceid]);
1 efrain 323
echo $PAGE->get_renderer('qbank_previewquestion')->render_preview_page($previewdata);
324
 
325
// Log the preview of this question.
326
$event = \core\event\question_viewed::create_from_question_instance($question, $context);
327
$event->trigger();
328
 
329
$PAGE->requires->js_call_amd('qbank_previewquestion/preview', 'init', [$previewdata['redirect'], $closeurl->__toString()]);
330
echo $OUTPUT->footer();