123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- <?php
- // This file is part of Moodle - http://moodle.org/
- //
- // Moodle is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // Moodle is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
- /**
- * This page displays a preview of a question
- *
- * The preview uses the option settings from the activity within which the question
- * is previewed or the default settings if no activity is specified. The question session
- * information is stored in the session as an array of subsequent states rather
- * than in the database.
- *
- * @package moodlecore
- * @subpackage questionengine
- * @copyright Alex Smith {@link http://maths.york.ac.uk/serving_maths} and
- * numerous contributors.
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
- require_once(__DIR__ . '/../config.php');
- require_once($CFG->libdir . '/questionlib.php');
- require_once(__DIR__ . '/previewlib.php');
- /**
- * The maximum number of variants previewable. If there are more variants than this for a question
- * then we only allow the selection of the first x variants.
- * @var integer
- */
- define('QUESTION_PREVIEW_MAX_VARIANTS', 100);
- // Get and validate question id.
- $id = required_param('id', PARAM_INT);
- $question = question_bank::load_question($id);
- // Were we given a particular context to run the question in?
- // This affects things like filter settings, or forced theme or language.
- if ($cmid = optional_param('cmid', 0, PARAM_INT)) {
- $cm = get_coursemodule_from_id(false, $cmid);
- require_login($cm->course, false, $cm);
- $context = context_module::instance($cmid);
- } else if ($courseid = optional_param('courseid', 0, PARAM_INT)) {
- require_login($courseid);
- $context = context_course::instance($courseid);
- } else {
- require_login();
- $category = $DB->get_record('question_categories',
- array('id' => $question->category), '*', MUST_EXIST);
- $context = context::instance_by_id($category->contextid);
- $PAGE->set_context($context);
- // Note that in the other cases, require_login will set the correct page context.
- }
- question_require_capability_on($question, 'use');
- $PAGE->set_pagelayout('popup');
- // Get and validate display options.
- $maxvariant = min($question->get_num_variants(), QUESTION_PREVIEW_MAX_VARIANTS);
- $options = new question_preview_options($question);
- $options->load_user_defaults();
- $options->set_from_request();
- $PAGE->set_url(question_preview_url($id, $options->behaviour, $options->maxmark,
- $options, $options->variant, $context));
- // Get and validate existing preview, or start a new one.
- $previewid = optional_param('previewid', 0, PARAM_INT);
- if ($previewid) {
- try {
- $quba = question_engine::load_questions_usage_by_activity($previewid);
- } catch (Exception $e) {
- // This may not seem like the right error message to display, but
- // actually from the user point of view, it makes sense.
- print_error('submissionoutofsequencefriendlymessage', 'question',
- question_preview_url($question->id, $options->behaviour,
- $options->maxmark, $options, $options->variant, $context), null, $e);
- }
- if ($quba->get_owning_context()->instanceid != $USER->id) {
- print_error('notyourpreview', 'question');
- }
- $slot = $quba->get_first_question_number();
- $usedquestion = $quba->get_question($slot);
- if ($usedquestion->id != $question->id) {
- print_error('questionidmismatch', 'question');
- }
- $question = $usedquestion;
- $options->variant = $quba->get_variant($slot);
- } else {
- $quba = question_engine::make_questions_usage_by_activity(
- 'core_question_preview', context_user::instance($USER->id));
- $quba->set_preferred_behaviour($options->behaviour);
- $slot = $quba->add_question($question, $options->maxmark);
- if ($options->variant) {
- $options->variant = min($maxvariant, max(1, $options->variant));
- } else {
- $options->variant = rand(1, $maxvariant);
- }
- $quba->start_question($slot, $options->variant);
- $transaction = $DB->start_delegated_transaction();
- question_engine::save_questions_usage_by_activity($quba);
- $transaction->allow_commit();
- }
- $options->behaviour = $quba->get_preferred_behaviour();
- $options->maxmark = $quba->get_question_max_mark($slot);
- // Create the settings form, and initialise the fields.
- $optionsform = new preview_options_form(question_preview_form_url($question->id, $context, $previewid),
- array('quba' => $quba, 'maxvariant' => $maxvariant));
- $optionsform->set_data($options);
- // Process change of settings, if that was requested.
- if ($newoptions = $optionsform->get_submitted_data()) {
- // Set user preferences.
- $options->save_user_preview_options($newoptions);
- if (!isset($newoptions->variant)) {
- $newoptions->variant = $options->variant;
- }
- if (isset($newoptions->saverestart)) {
- restart_preview($previewid, $question->id, $newoptions, $context);
- }
- }
- // Prepare a URL that is used in various places.
- $actionurl = question_preview_action_url($question->id, $quba->get_id(), $options, $context);
- // Process any actions from the buttons at the bottom of the form.
- if (data_submitted() && confirm_sesskey()) {
- try {
- if (optional_param('restart', false, PARAM_BOOL)) {
- restart_preview($previewid, $question->id, $options, $context);
- } else if (optional_param('fill', null, PARAM_BOOL)) {
- $correctresponse = $quba->get_correct_response($slot);
- if (!is_null($correctresponse)) {
- $quba->process_action($slot, $correctresponse);
- $transaction = $DB->start_delegated_transaction();
- question_engine::save_questions_usage_by_activity($quba);
- $transaction->allow_commit();
- }
- redirect($actionurl);
- } else if (optional_param('finish', null, PARAM_BOOL)) {
- $quba->process_all_actions();
- $quba->finish_all_questions();
- $transaction = $DB->start_delegated_transaction();
- question_engine::save_questions_usage_by_activity($quba);
- $transaction->allow_commit();
- redirect($actionurl);
- } else {
- $quba->process_all_actions();
- $transaction = $DB->start_delegated_transaction();
- question_engine::save_questions_usage_by_activity($quba);
- $transaction->allow_commit();
- $scrollpos = optional_param('scrollpos', '', PARAM_RAW);
- if ($scrollpos !== '') {
- $actionurl->param('scrollpos', (int) $scrollpos);
- }
- redirect($actionurl);
- }
- } catch (question_out_of_sequence_exception $e) {
- print_error('submissionoutofsequencefriendlymessage', 'question', $actionurl);
- } catch (Exception $e) {
- // This sucks, if we display our own custom error message, there is no way
- // to display the original stack trace.
- $debuginfo = '';
- if (!empty($e->debuginfo)) {
- $debuginfo = $e->debuginfo;
- }
- print_error('errorprocessingresponses', 'question', $actionurl,
- $e->getMessage(), $debuginfo);
- }
- }
- if ($question->length) {
- $displaynumber = '1';
- } else {
- $displaynumber = 'i';
- }
- $restartdisabled = array();
- $finishdisabled = array();
- $filldisabled = array();
- if ($quba->get_question_state($slot)->is_finished()) {
- $finishdisabled = array('disabled' => 'disabled');
- $filldisabled = array('disabled' => 'disabled');
- }
- // If question type cannot give us a correct response, disable this button.
- if (is_null($quba->get_correct_response($slot))) {
- $filldisabled = array('disabled' => 'disabled');
- }
- if (!$previewid) {
- $restartdisabled = array('disabled' => 'disabled');
- }
- // Prepare technical info to be output.
- $qa = $quba->get_question_attempt($slot);
- $technical = array();
- $technical[] = get_string('behaviourbeingused', 'question',
- question_engine::get_behaviour_name($qa->get_behaviour_name()));
- $technical[] = get_string('technicalinfominfraction', 'question', $qa->get_min_fraction());
- $technical[] = get_string('technicalinfomaxfraction', 'question', $qa->get_max_fraction());
- $technical[] = get_string('technicalinfovariant', 'question', $qa->get_variant());
- $technical[] = get_string('technicalinfoquestionsummary', 'question', s($qa->get_question_summary()));
- $technical[] = get_string('technicalinforightsummary', 'question', s($qa->get_right_answer_summary()));
- $technical[] = get_string('technicalinforesponsesummary', 'question', s($qa->get_response_summary()));
- $technical[] = get_string('technicalinfostate', 'question', '' . $qa->get_state());
- // Start output.
- $title = get_string('previewquestion', 'question', format_string($question->name));
- $headtags = question_engine::initialise_js() . $quba->render_question_head_html($slot);
- $PAGE->set_title($title);
- $PAGE->set_heading($title);
- echo $OUTPUT->header();
- // Start the question form.
- echo html_writer::start_tag('form', array('method' => 'post', 'action' => $actionurl,
- 'enctype' => 'multipart/form-data', 'id' => 'responseform'));
- echo html_writer::start_tag('div');
- echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
- echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'slots', 'value' => $slot));
- echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'scrollpos', 'value' => '', 'id' => 'scrollpos'));
- echo html_writer::end_tag('div');
- // Output the question.
- echo $quba->render_question($slot, $options, $displaynumber);
- // Finish the question form.
- echo html_writer::start_tag('div', array('id' => 'previewcontrols', 'class' => 'controls'));
- echo html_writer::empty_tag('input', $restartdisabled + array('type' => 'submit',
- 'name' => 'restart', 'value' => get_string('restart', 'question')));
- echo html_writer::empty_tag('input', $finishdisabled + array('type' => 'submit',
- 'name' => 'save', 'value' => get_string('save', 'question')));
- echo html_writer::empty_tag('input', $filldisabled + array('type' => 'submit',
- 'name' => 'fill', 'value' => get_string('fillincorrect', 'question')));
- echo html_writer::empty_tag('input', $finishdisabled + array('type' => 'submit',
- 'name' => 'finish', 'value' => get_string('submitandfinish', 'question')));
- echo html_writer::end_tag('div');
- echo html_writer::end_tag('form');
- // Output the technical info.
- print_collapsible_region_start('', 'techinfo', get_string('technicalinfo', 'question') .
- $OUTPUT->help_icon('technicalinfo', 'question'),
- 'core_question_preview_techinfo_collapsed', true);
- foreach ($technical as $info) {
- echo html_writer::tag('p', $info, array('class' => 'notifytiny'));
- }
- print_collapsible_region_end();
- // Display the settings form.
- $optionsform->display();
- $PAGE->requires->js_module('core_question_engine');
- $PAGE->requires->strings_for_js(array(
- 'closepreview',
- ), 'question');
- $PAGE->requires->yui_module('moodle-question-preview', 'M.question.preview.init');
- echo $OUTPUT->footer();
|