MDL-24355 tags - fix correlation sql to be cross-db
[moodle.git] / question / preview.php
blob856b3cb43833c25aeebf7548004151513e0b9295
1 <?php
2 /**
3 * This page displays a preview of a question
5 * The preview uses the option settings from the activity within which the question
6 * is previewed or the default settings if no activity is specified. The question session
7 * information is stored in the session as an array of subsequent states rather
8 * than in the database.
10 * TODO: make this work with activities other than quiz
12 * @author Alex Smith as part of the Serving Mathematics project
13 * {@link http://maths.york.ac.uk/serving_maths}
14 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
15 * @package questionbank
18 require_once("../config.php");
19 require_once($CFG->libdir.'/questionlib.php');
20 require_once($CFG->dirroot.'/mod/quiz/locallib.php'); // We really want to get rid of this
22 $id = required_param('id', PARAM_INT); // question id
23 // if no quiz id is specified then a dummy quiz with default options is used
24 $quizid = optional_param('quizid', 0, PARAM_INT);
25 // if no quiz id is specified then tell us the course
27 $pageurl = new moodle_url('/question/preview.php', array('id' => $id, 'continue' => 1));
28 if (empty($quizid)) {
29 $courseid = required_param('courseid', PARAM_INT);
30 $pageurl->param('courseid', $courseid);
31 } else {
32 $pageurl->param('quizid', $quizid);
34 $PAGE->set_url($pageurl);
35 $PAGE->set_pagelayout('popup');
37 // Test if we are continuing an attempt at a question
38 $continue = optional_param('continue', 0, PARAM_BOOL);
39 // Check for any of the submit buttons
40 $fillcorrect = optional_param('fillcorrect', 0, PARAM_BOOL);
41 $markall = optional_param('markall', 0, PARAM_BOOL);
42 $finishattempt = optional_param('finishattempt', 0, PARAM_BOOL);
43 $back = optional_param('back', 0, PARAM_BOOL);
44 $startagain = optional_param('startagain', 0, PARAM_BOOL);
45 // We are always continuing an attempt if a submit button was pressed with the
46 // exception of the start again button
47 if ($fillcorrect || $markall || $finishattempt || $back) {
48 $continue = true;
49 } else if ($startagain) {
50 $continue = false;
53 $url = new moodle_url('/question/preview.php');
54 $url->param('id', $id);
55 if ($quizid) {
56 $url->param('quizid', $quizid);
57 } else {
58 $url->param('courseid', $courseid);
60 $url->param('continue', 1);
61 if (!$continue) {
62 // Start a new attempt; delete the old session
63 unset($SESSION->quizpreview);
64 // Redirect to ourselves but with continue=1; prevents refreshing the page
65 // from restarting an attempt (needed so that random questions don't change)
66 redirect($url);
68 // Load the question information
69 if (!$questions = $DB->get_records('question', array('id' => $id))) {
70 print_error('cannotloadquestion', 'question');
72 if (empty($quizid)) {
73 $quiz = new cmoptions;
74 $quiz->id = 0;
75 $quiz->review = get_config('quiz', 'review');
76 require_login($courseid, false);
77 $quiz->course = $courseid;
78 $quiz->decimalpoints = get_config('quiz', 'decimalpoints');
79 $context = get_context_instance(CONTEXT_COURSE, $courseid);
80 } else if (!$quiz = $DB->get_record('quiz', array('id' => $quizid))) {
81 print_error('invalidquizid', 'quiz', '', $quizid);
82 } else {
83 $cm = get_coursemodule_from_instance('quiz', $quizid, $quiz->course);
84 require_login($quiz->course, false, $cm);
85 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
88 if ($maxgrade = $DB->get_field('quiz_question_instances', 'grade', array('quiz' => $quiz->id, 'question' => $id))) {
89 $questions[$id]->maxgrade = $maxgrade;
90 } else {
91 $questions[$id]->maxgrade = $questions[$id]->defaultgrade;
94 $quiz->id = 0; // just for safety
95 $quiz->questions = $id;
97 if (!$category = $DB->get_record("question_categories", array("id" => $questions[$id]->category))) {
98 print_error('invalidquestionid', 'quiz');
101 if (!question_has_capability_on($questions[$id], 'use', $questions[$id]->category)){
102 print_error('cannotpreview', 'question');
104 if (isset($COURSE)){
105 $quiz->course = $COURSE->id;
108 // Load the question type specific information
109 if (!get_question_options($questions)) {
110 print_error('newattemptfail', 'quiz');
113 // Create a dummy quiz attempt
114 // TODO: find out what of the following we really need. What is $attempt
115 // really used for?
116 $timenow = time();
117 $attempt->quiz = $quiz->id;
118 $attempt->userid = $USER->id;
119 $attempt->attempt = 0;
120 $attempt->sumgrades = 0;
121 $attempt->timestart = $timenow;
122 $attempt->timefinish = 0;
123 $attempt->timemodified = $timenow;
124 $attempt->uniqueid = 0;
125 $attempt->id = 0;
126 $attempt->layout = $id;
128 // Restore the history of question sessions from the moodle session or create
129 // new sessions. Make $states a reference to the states array in the moodle
130 // session.
131 if (isset($SESSION->quizpreview->states) and $SESSION->quizpreview->questionid == $id) {
132 // Reload the question session history from the moodle session
133 $states =& $SESSION->quizpreview->states;
134 $historylength = count($states) - 1;
135 if ($back && $historylength > 0) {
136 // Go back one step in the history
137 unset($states[$historylength]);
138 $historylength--;
140 } else {
141 // Record the question id in the moodle session
142 $SESSION->quizpreview->questionid = $id;
143 // Create an empty session for the question
144 if (!$newstates = get_question_states($questions, $quiz, $attempt)) {
145 print_error('newattemptfail', 'quiz');
147 $newstates[$id]->questionsessionid = 0;
148 $SESSION->quizpreview->states = array($newstates);
149 $states =& $SESSION->quizpreview->states;
150 $historylength = 0;
153 if (!$fillcorrect && !$back && ($form = data_submitted())) {
154 $form = (array)$form;
155 $submitted = true;
157 // Create a new item in the history of question states (don't simplify!)
158 $states[$historylength + 1] = array();
159 $states[$historylength + 1][$id] = clone($states[$historylength][$id]);
160 $historylength++;
161 $curstate =& $states[$historylength][$id];
162 $curstate->changed = false;
164 // Process the responses
165 unset($form['id']);
166 unset($form['quizid']);
167 unset($form['continue']);
168 unset($form['markall']);
169 unset($form['finishattempt']);
170 unset($form['back']);
171 unset($form['startagain']);
173 if ($finishattempt) {
174 $event = QUESTION_EVENTCLOSE;
175 } else if ($markall) {
176 $event = QUESTION_EVENTSUBMIT;
177 } else {
178 $event = QUESTION_EVENTSAVE;
180 if ($actions = question_extract_responses($questions, $form, $event)) {
181 $actions[$id]->timestamp = 0; // We do not care about timelimits here
182 if (!question_process_responses($questions[$id], $curstate, $actions[$id], $quiz, $attempt)) {
183 unset($SESSION->quizpreview);
184 print_error('errorprocessingresponses', 'question', $url->out());
186 if (!$curstate->changed) {
187 // Update the current state rather than creating a new one
188 $historylength--;
189 unset($states[$historylength]);
190 $states = array_values($states);
191 $curstate =& $states[$historylength][$id];
194 } else {
195 $submitted = false;
196 $curstate =& $states[$historylength][$id];
199 // TODO: should not use quiz-specific function here
200 $options = quiz_get_renderoptions($quiz, $attempt, $context, $curstate);
201 $options->noeditlink = true;
203 // Fill in the correct responses (unless the question is in readonly mode)
204 if ($fillcorrect && !$options->readonly) {
205 $curstate->responses = $QTYPES[$questions[$id]->qtype]->get_correct_responses($questions[$id], $curstate);
208 $strpreview = get_string('preview', 'quiz').' '.format_string($questions[$id]->name);
209 $questionlist = array($id);
210 question_get_html_head_contributions($questionlist, $questions, $states[$historylength]);
211 $PAGE->set_title($strpreview);
212 $PAGE->set_heading($COURSE->fullname);
213 echo $OUTPUT->header();
215 if (!empty($quizid)) {
216 echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
217 p(format_string($quiz->name));
218 echo "</p>\n";
220 $number = 1;
221 echo '<form method="post" action="'.$url->out_omit_querystring().'" enctype="multipart/form-data" id="responseform">', "\n";
222 $PAGE->requires->js_init_call('M.core_question_engine.init_form', array('#responseform'));
224 print_question($questions[$id], $curstate, $number, $quiz, $options, $context);
226 echo '<div class="controls">';
227 echo html_writer::input_hidden_params($url);
229 // Print the mark and finish attempt buttons
230 echo '<input name="markall" type="submit" value="' . get_string('markall',
231 'quiz') . "\" />\n";
232 echo '<input name="finishattempt" type="submit" value="' .
233 get_string('submitallandfinish', 'quiz') . "\" />\n";
234 echo '<br />';
235 echo '<br />';
236 // Print the fill correct button (unless the question is in readonly mode)
237 if (!$options->readonly) {
238 echo '<input name="fillcorrect" type="submit" value="' .
239 get_string('fillcorrect', 'quiz') . "\" />\n";
241 // Print the navigation buttons
242 if ($historylength > 0) {
243 echo '<input name="back" type="submit" value="' . get_string('previous',
244 'quiz') . "\" />\n";
246 // Print the start again button
247 echo '<input name="startagain" type="submit" value="' .
248 get_string('startagain', 'quiz') . "\" />\n";
249 // Print the close window button
250 echo '<input type="button" onclick="window.close()" value="' .
251 get_string('closepreview', 'quiz') . "\" />";
252 echo '</div>';
253 echo '</form>';
254 echo $OUTPUT->footer();