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 $courseid = required_param('courseid', PARAM_INT
);
30 // Test if we are continuing an attempt at a question
31 $continue = optional_param('continue', 0, PARAM_BOOL
);
32 // Check for any of the submit buttons
33 $fillcorrect = optional_param('fillcorrect', 0, PARAM_BOOL
);
34 $markall = optional_param('markall', 0, PARAM_BOOL
);
35 $finishattempt = optional_param('finishattempt', 0, PARAM_BOOL
);
36 $back = optional_param('back', 0, PARAM_BOOL
);
37 $startagain = optional_param('startagain', 0, PARAM_BOOL
);
38 // We are always continuing an attempt if a submit button was pressed with the
39 // exception of the start again button
40 if ($fillcorrect ||
$markall ||
$finishattempt ||
$back) {
42 } else if ($startagain) {
49 // Start a new attempt; delete the old session
50 unset($SESSION->quizpreview
);
51 // Redirect to ourselves but with continue=1; prevents refreshing the page
52 // from restarting an attempt (needed so that random questions don't change)
53 $url = $CFG->wwwroot
. '/question/preview.php?id=' . $id;
55 $url .= '&quizid=' . $quizid;
57 $url .= '&courseid=' . $courseid;
59 $url .= '&continue=1';
62 // Load the question information
63 if (!$questions = get_records('question', 'id', $id)) {
64 error('Could not load question');
67 $quiz = new cmoptions
;
69 $quiz->review
= $CFG->quiz_review
;
70 require_login($courseid, false);
71 $quiz->course
= $courseid;
72 } else if (!$quiz = get_record('quiz', 'id', $quizid)) {
73 error("Quiz id $quizid does not exist");
75 require_login($quiz->course
, false, get_coursemodule_from_instance('quiz', $quizid, $quiz->course
));
80 if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id
, 'question', $id)) {
81 $questions[$id]->maxgrade
= $maxgrade;
83 $questions[$id]->maxgrade
= $questions[$id]->defaultgrade
;
86 $quiz->id
= 0; // just for safety
87 $quiz->questions
= $id;
89 if (!$category = get_record("question_categories", "id", $questions[$id]->category
)) {
90 error("This question doesn't belong to a valid category!");
93 if (!question_has_capability_on($questions[$id], 'use', $questions[$id]->category
)){
94 error("You can't preview these questions!");
97 $quiz->course
= $COURSE->id
;
100 // Load the question type specific information
101 if (!get_question_options($questions)) {
102 error(get_string('newattemptfail', 'quiz'));
105 // Create a dummy quiz attempt
106 // TODO: find out what of the following we really need. What is $attempt
109 $attempt->quiz
= $quiz->id
;
110 $attempt->userid
= $USER->id
;
111 $attempt->attempt
= 0;
112 $attempt->sumgrades
= 0;
113 $attempt->timestart
= $timenow;
114 $attempt->timefinish
= 0;
115 $attempt->timemodified
= $timenow;
116 $attempt->uniqueid
= 0;
119 // Restore the history of question sessions from the moodle session or create
120 // new sessions. Make $states a reference to the states array in the moodle
122 if (isset($SESSION->quizpreview
->states
) and $SESSION->quizpreview
->questionid
124 // Reload the question session history from the moodle session
125 $states =& $SESSION->quizpreview
->states
;
126 $historylength = count($states) - 1;
127 if ($back && $historylength > 0) {
128 // Go back one step in the history
129 unset($states[$historylength]);
133 // Record the question id in the moodle session
134 $SESSION->quizpreview
->questionid
= $id;
135 // Create an empty session for the question
137 get_question_states($questions, $quiz, $attempt)) {
138 error(get_string('newattemptfail', 'quiz'));
140 $SESSION->quizpreview
->states
= array($newstates);
141 $states =& $SESSION->quizpreview
->states
;
145 if (!$fillcorrect && !$back && ($form = data_submitted())) {
146 $form = (array)$form;
149 // Create a new item in the history of question states (don't simplify!)
150 $states[$historylength +
1] = array();
151 $states[$historylength +
1][$id] = clone($states[$historylength][$id]);
153 $curstate =& $states[$historylength][$id];
154 $curstate->changed
= false;
156 // Process the responses
158 unset($form['quizid']);
159 unset($form['continue']);
160 unset($form['markall']);
161 unset($form['finishattempt']);
162 unset($form['back']);
163 unset($form['startagain']);
165 $event = $finishattempt ? QUESTION_EVENTCLOSE
: QUESTION_EVENTSUBMIT
;
166 if ($actions = question_extract_responses($questions, $form, $event)) {
167 $actions[$id]->timestamp
= 0; // We do not care about timelimits here
168 question_process_responses($questions[$id], $curstate, $actions[$id], $quiz, $attempt);
169 if (!$curstate->changed
) {
170 // Update the current state rather than creating a new one
172 unset($states[$historylength]);
173 $states = array_values($states);
174 $curstate =& $states[$historylength][$id];
179 $curstate =& $states[$historylength][$id];
182 // TODO: should not use quiz-specific function here
183 $options = quiz_get_renderoptions($quiz->review
, $curstate);
185 // Fill in the correct responses (unless the question is in readonly mode)
186 if ($fillcorrect && !$options->readonly
) {
187 $curstate->responses
= $QTYPES[$questions[$id]->qtype
]
188 ->get_correct_responses($questions[$id], $curstate);
191 $strpreview = get_string('preview', 'quiz').' '.format_string($questions[$id]->name
);
192 $questionlist = array($id);
193 $headtags = get_html_head_contributions($questionlist, $questions, $states[$historylength]);
194 print_header($strpreview, '', '', '', $headtags);
195 print_heading($strpreview);
197 if (!empty($quizid)) {
198 echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
203 echo '<form method="post" action="preview.php" enctype="multipart/form-data" id="responseform">', "\n";
204 print_question($questions[$id], $curstate, $number, $quiz, $options);
209 echo '<div class="controls">';
210 echo "<input type=\"hidden\" name=\"id\" value=\"$id\" />\n";
211 echo "<input type=\"hidden\" name=\"quizid\" value=\"$quizid\" />\n";
212 echo "<input type=\"hidden\" name=\"courseid\" value=\"$courseid\" />\n";
213 echo "<input type=\"hidden\" name=\"continue\" value=\"1\" />\n";
215 // Print the mark and finish attempt buttons
216 echo '<input name="markall" type="submit" value="' . get_string('markall',
218 echo '<input name="finishattempt" type="submit" value="' .
219 get_string('finishattempt', 'quiz') . "\" />\n";
222 // Print the fill correct button (unless the question is in readonly mode)
223 if (!$options->readonly
) {
224 echo '<input name="fillcorrect" type="submit" value="' .
225 get_string('fillcorrect', 'quiz') . "\" />\n";
227 // Print the navigation buttons
228 if ($historylength > 0) {
229 echo '<input name="back" type="submit" value="' . get_string('previous',
232 // Print the start again button
233 echo '<input name="startagain" type="submit" value="' .
234 get_string('startagain', 'quiz') . "\" />\n";
235 // Print the close window button
236 echo '<input type="button" onclick="window.close()" value="' .
237 get_string('closepreview', 'quiz') . "\" />";