MDL-19976 Changing redirect($url->out()) to redirect($url). NEVER, NEVER use redirect...
[moodle.git] / question / preview.php
1 <?php // $Id$
2 /**
3  * This page displays a preview of a question
4  *
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.
9  *
10  * TODO: make this work with activities other than quiz
11  *
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
16  */
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
26     if (empty($quizid)) {
27         $courseid = required_param('courseid', PARAM_INT);
28     }
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) {
41         $continue = true;
42     } else if ($startagain) {
43         $continue = false;
44     }
46     $url = new moodle_url($CFG->wwwroot . '/question/preview.php');
47     $url->param('id', $id);
48     if ($quizid) {
49         $url->param('quizid', $quizid);
50     } else {
51         $url->param('courseid', $courseid);
52     }
53     $url->param('continue', 1);
54     if (!$continue) {
55         // Start a new attempt; delete the old session
56         unset($SESSION->quizpreview);
57         // Redirect to ourselves but with continue=1; prevents refreshing the page
58         // from restarting an attempt (needed so that random questions don't change)
59         redirect($url);
60     }
61     // Load the question information
62     if (!$questions = $DB->get_records('question', array('id' =>  $id))) {
63         print_error('cannotloadquestion', 'question');
64     }
65     if (empty($quizid)) {
66         $quiz = new cmoptions;
67         $quiz->id = 0;
68         $quiz->review = get_config('quiz', 'review');
69         require_login($courseid, false);
70         $quiz->course = $courseid;
71         $quiz->decimalpoints = get_config('quiz', 'decimalpoints');
72         $context = get_context_instance(CONTEXT_COURSE, $courseid);
73     } else if (!$quiz = $DB->get_record('quiz', array('id' => $quizid))) {
74         print_error('invalidquizid', 'quiz', '', $quizid);
75     } else {
76         $cm = get_coursemodule_from_instance('quiz', $quizid, $quiz->course);
77         require_login($quiz->course, false, $cm);
78         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
79     }
81     if ($maxgrade = $DB->get_field('quiz_question_instances', 'grade', array('quiz' => $quiz->id, 'question' => $id))) {
82         $questions[$id]->maxgrade = $maxgrade;
83     } else {
84         $questions[$id]->maxgrade = $questions[$id]->defaultgrade;
85     }
87     $quiz->id = 0; // just for safety
88     $quiz->questions = $id;
90     if (!$category = $DB->get_record("question_categories", array("id" => $questions[$id]->category))) {
91         print_error('invalidquestionid', 'quiz');
92     }
94     if (!question_has_capability_on($questions[$id], 'use', $questions[$id]->category)){
95         print_error('cannotpreview', 'question');
96     }
97     if (isset($COURSE)){
98         $quiz->course = $COURSE->id;
99     }
101     // Load the question type specific information
102     if (!get_question_options($questions)) {
103         print_error('newattemptfail', 'quiz');
104     }
106     // Create a dummy quiz attempt
107     // TODO: find out what of the following we really need. What is $attempt
108     //       really used for?
109     $timenow = time();
110     $attempt->quiz = $quiz->id;
111     $attempt->userid = $USER->id;
112     $attempt->attempt = 0;
113     $attempt->sumgrades = 0;
114     $attempt->timestart = $timenow;
115     $attempt->timefinish = 0;
116     $attempt->timemodified = $timenow;
117     $attempt->uniqueid = 0;
118     $attempt->id = 0;
119     $attempt->layout = $id;
121     // Restore the history of question sessions from the moodle session or create
122     // new sessions. Make $states a reference to the states array in the moodle
123     // session.
124     if (isset($SESSION->quizpreview->states) and $SESSION->quizpreview->questionid == $id) {
125         // Reload the question session history from the moodle session
126         $states =& $SESSION->quizpreview->states;
127         $historylength = count($states) - 1;
128         if ($back && $historylength > 0) {
129             // Go back one step in the history
130             unset($states[$historylength]);
131             $historylength--;
132         }
133     } else {
134         // Record the question id in the moodle session
135         $SESSION->quizpreview->questionid = $id;
136         // Create an empty session for the question
137         if (!$newstates = get_question_states($questions, $quiz, $attempt)) {
138             print_error('newattemptfail', 'quiz');
139         }
140         $newstates[$id]->questionsessionid = 0;
141         $SESSION->quizpreview->states = array($newstates);
142         $states =& $SESSION->quizpreview->states;
143         $historylength = 0;
144     }
146     if (!$fillcorrect && !$back && ($form = data_submitted())) {
147         $form = (array)$form;
148         $submitted = true;
150         // Create a new item in the history of question states (don't simplify!)
151         $states[$historylength + 1] = array();
152         $states[$historylength + 1][$id] = clone($states[$historylength][$id]);
153         $historylength++;
154         $curstate =& $states[$historylength][$id];
155         $curstate->changed = false;
157         // Process the responses
158         unset($form['id']);
159         unset($form['quizid']);
160         unset($form['continue']);
161         unset($form['markall']);
162         unset($form['finishattempt']);
163         unset($form['back']);
164         unset($form['startagain']);
166         if ($finishattempt) {
167             $event = QUESTION_EVENTCLOSE;
168         } else if ($markall) {
169             $event = QUESTION_EVENTSUBMIT;
170         } else {
171             $event = QUESTION_EVENTSAVE;
172         }
173         if ($actions = question_extract_responses($questions, $form, $event)) {
174             $actions[$id]->timestamp = 0; // We do not care about timelimits here
175             if (!question_process_responses($questions[$id], $curstate, $actions[$id], $quiz, $attempt)) {
176                 unset($SESSION->quizpreview);
177                 print_error('errorprocessingresponses', 'question', $url->out());
178             }
179             if (!$curstate->changed) {
180                 // Update the current state rather than creating a new one
181                 $historylength--;
182                 unset($states[$historylength]);
183                 $states = array_values($states);
184                 $curstate =& $states[$historylength][$id];
185             }
186         }
187     } else {
188         $submitted = false;
189         $curstate =& $states[$historylength][$id];
190     }
192     // TODO: should not use quiz-specific function here
193     $options = quiz_get_renderoptions($quiz, $attempt, $context, $curstate);
194     $options->noeditlink = true;
196     // Fill in the correct responses (unless the question is in readonly mode)
197     if ($fillcorrect && !$options->readonly) {
198         $curstate->responses = $QTYPES[$questions[$id]->qtype]->get_correct_responses($questions[$id], $curstate);
199     }
201     $strpreview = get_string('preview', 'quiz').' '.format_string($questions[$id]->name);
202     $questionlist = array($id);
203     $headtags = get_html_head_contributions($questionlist, $questions, $states[$historylength]);
204     print_header($strpreview, '', '', '', $headtags);
205     print_heading($strpreview);
207     if (!empty($quizid)) {
208         echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
209         p(format_string($quiz->name));
210         echo "</p>\n";
211     }
212     $number = 1;
213     echo '<form method="post" action="'.$url->out(true).'" enctype="multipart/form-data" id="responseform">', "\n";
214     print_question($questions[$id], $curstate, $number, $quiz, $options);
216     echo '<div class="controls">';
217     echo $url->hidden_params_out();
219     // Print the mark and finish attempt buttons
220     echo '<input name="markall" type="submit" value="' . get_string('markall',
221      'quiz') . "\" />\n";
222     echo '<input name="finishattempt" type="submit" value="' .
223      get_string('finishattempt', 'quiz') . "\" />\n";
224     echo '<br />';
225     echo '<br />';
226     // Print the fill correct button (unless the question is in readonly mode)
227     if (!$options->readonly) {
228         echo '<input name="fillcorrect" type="submit" value="' .
229          get_string('fillcorrect', 'quiz') . "\" />\n";
230     }
231     // Print the navigation buttons
232     if ($historylength > 0) {
233         echo '<input name="back" type="submit" value="' . get_string('previous',
234          'quiz') . "\" />\n";
235     }
236     // Print the start again button
237     echo '<input name="startagain" type="submit" value="' .
238      get_string('startagain', 'quiz') . "\" />\n";
239     // Print the close window button
240     echo '<input type="button" onclick="window.close()" value="' .
241      get_string('closepreview', 'quiz') . "\" />";
242     echo '</div>';
243     echo '</form>';
244     print_footer();
245 ?>