MDL-9079 - Paging broken in quiz overview report. Merged from MOODLE_17_STABLE.
[moodle.git] / question / question.php
CommitLineData
4323d029 1<?php // $Id$
16750bcd 2/**
3 * Page for editing questions
4 *
5 * This page shows the question editing form or processes the following actions:
6 * - create new question (category, qtype)
7 * - edit question (id, contextquiz (optional))
8 * - cancel (cancel)
9 *
10 * TODO: currently this still treats the quiz as special
11 * TODO: question versioning is not currently enabled
12 *
16750bcd 13 * @author Martin Dougiamas and many others. This has recently been extensively
14 * rewritten by members of the Serving Mathematics project
15 * {@link http://maths.york.ac.uk/serving_maths}
16 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
4323d029 17 * @package questionbank
16750bcd 18 *//** */
87391656 19
16750bcd 20 require_once(dirname(__FILE__) . '/../config.php');
21 require_once('editlib.php'); // NOTE - is this correct? This is just about editing screens?
22 require_once($CFG->libdir . '/filelib.php');
516cf3eb 23
16750bcd 24 $id = optional_param('id', 0, PARAM_INT); // question id
516cf3eb 25
18cbc968 26 $qtype = optional_param('qtype', '', PARAM_FILE);
27 $category = optional_param('category', 0, PARAM_INT);
ee6c9355 28 $inpopup = optional_param('inpopup', 0, PARAM_BOOL);
87391656 29
180f4106 30 $CFG->pagepath = 'question/type/'.$qtype;
87391656 31
516cf3eb 32
18cbc968 33 // rqp questions set the type to rqp_nn where nn is the rqp_type id
34 if (substr($qtype, 0, 4) == 'rqp_') {
35 $typeid = (int) substr($qtype, 4);
36 $qtype = 'rqp';
516cf3eb 37 }
38
516cf3eb 39 if ($id) {
4f48fb42 40 if (! $question = get_record("question", "id", $id)) {
516cf3eb 41 error("This question doesn't exist");
42 }
43 if (!empty($category)) {
44 $question->category = $category;
45 }
dc1f00de 46 if (! $category = get_record("question_categories", "id", $question->category)) {
516cf3eb 47 error("This question doesn't belong to a valid category!");
48 }
49 if (! $course = get_record("course", "id", $category->course)) {
50 error("This question category doesn't belong to a valid course!");
51 }
52
53 $qtype = $question->qtype;
4eda4eec 54 if (!isset($QTYPES[$qtype])) {
55 $qtype = 'missingtype';
56 }
516cf3eb 57
516cf3eb 58 } else if ($category) { // only for creating new questions
dc1f00de 59 if (! $category = get_record("question_categories", "id", $category)) {
516cf3eb 60 error("This wasn't a valid category!");
61 }
62 if (! $course = get_record("course", "id", $category->course)) {
63 error("This category doesn't belong to a valid course!");
64 }
65
66 $question->category = $category->id;
67 $question->qtype = $qtype;
68
69 } else {
70 error("Must specify question id or category");
71 }
72
2662cf46 73 if (!isset($SESSION->returnurl)) {
74 $SESSION->returnurl = 'edit.php?courseid='.$course->id;
75 }
76
05054049 77 // TODO: generalise this so it works for any activity
2662cf46 78 $contextquiz = isset($SESSION->modform->instance) ? $SESSION->modform->instance : 0;
05054049 79
80 if (isset($_REQUEST['cancel'])) {
2662cf46 81 redirect($SESSION->returnurl);
05054049 82 }
83
516cf3eb 84 if (empty($qtype)) {
85 error("No question type was specified!");
f02c6f01 86 } else if (!isset($QTYPES[$qtype])) {
516cf3eb 87 error("Could not find question type: '$qtype'");
88 }
89
36703ed7 90 if (!file_exists("type/$qtype/editquestion.php")) {
91 redirect(str_ireplace('question.php', 'question2.php', me()));
92 }
93
516cf3eb 94 require_login($course->id, false);
1680ebe4 95 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
96 require_capability('moodle/question:manage', $coursecontext);
516cf3eb 97
516cf3eb 98
516cf3eb 99 if ($form = data_submitted() and confirm_sesskey()) {
100
101 if (isset($form->versioning) && isset($question->id) and false) { // disable versioning until it is fixed.
102 // use new code that handles whether to overwrite or copy a question
103 // and keeps track of the versions in the quiz_question_version table
104
105 // $replaceinquiz is an array with the ids of all quizzes in which
106 // the teacher has chosen to replace the old version
107 $replaceinquiz = array();
108 foreach($form as $key => $val) {
109 if ($tmp = quiz_parse_fieldname($key, 'q')) {
110 if ($tmp['mode'] == 'replace') {
111 $replaceinquiz[$tmp['id']] = $tmp['id'];
112 unset($form->$key);
113 }
114 }
115 }
116
117 // $quizlist is an array with the ids of quizzes which use this question
118 $quizlist = array();
119 if ($instances = get_records('quiz_question_instances', 'question', $question->id)) {
120 foreach($instances as $instance) {
121 $quizlist[$instance->quiz] = $instance->quiz;
122 }
123 }
124
125 if (isset($form->makecopy)) { // explicitly requested copies should be unhidden
126 $question->hidden = 0;
127 }
128
129 // Logic to determine whether old version should be overwritten
130 $makecopy = isset($form->makecopy) || (!$form->id); unset($form->makecopy);
131 if ($makecopy) {
132 $replaceold = false;
133 } else {
134 // this should be improved to exclude teacher preview responses and empty responses
135 // the current code leaves many unneeded questions in the database
4f48fb42 136 $hasresponses = record_exists('question_states', 'question', $form->id) or
137 record_exists('question_states', 'originalquestion', $form->id);
516cf3eb 138 $replaceinall = ($quizlist == $replaceinquiz); // question is being replaced in all quizzes
139 $replaceold = !$hasresponses && $replaceinall;
140 }
141
b01a2fcd 142 $oldquestionid = false;
516cf3eb 143 if (!$replaceold) { // create a new question
144 $oldquestionid = $question->id;
145 if (!$makecopy) {
4f48fb42 146 if (!set_field("question", 'hidden', 1, 'id', $question->id)) {
516cf3eb 147 error("Could not hide question!");
148 }
149 }
150 unset($question->id);
151 }
152 unset($makecopy, $hasresponses, $replaceinall, $replaceold);
f02c6f01 153 $question = $QTYPES[$qtype]->save_question($question, $form, $course);
516cf3eb 154 if(!isset($question->id)) {
155 error("Failed to save the question!");
156 }
157
b01a2fcd 158 if(!empty($oldquestionid)) {
516cf3eb 159 // create version entries for different quizzes
160 $version = new object();
161 $version->oldquestion = $oldquestionid;
162 $version->newquestion = $question->id;
163 $version->userid = $USER->id;
164 $version->timestamp = time();
165
166 foreach($replaceinquiz as $qid) {
167 $version->quiz = $qid;
168 if(!insert_record("quiz_question_versions", $version)) {
169 error("Could not store version information of question $oldquestionid in quiz $qid!");
170 }
171 }
172
173 /// now update the question references in the quizzes
174 if (!empty($replaceinquiz) and $quizzes = get_records_list("quiz", "id", implode(',', $replaceinquiz))) {
175
176 foreach($quizzes as $quiz) {
177 $questionlist = ",$quiz->questions,"; // a little hack with the commas here. not nice but effective
178 $questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist);
179 $questionlist = substr($questionlist, 1, -1); // and get rid of the surrounding commas again
180 if (!set_field("quiz", 'questions', $questionlist, 'id', $quiz->id)) {
181 error("Could not update questionlist in quiz $quiz->id!");
182 }
183
184 // the quiz_question_instances table needs to be updated too (aah, the joys of duplication :)
185 if (!set_field('quiz_question_instances', 'question', $question->id, 'quiz', $quiz->id, 'question', $oldquestionid)) {
186 error("Could not update question instance!");
187 }
188 if (isset($SESSION->modform) && (int)$SESSION->modform->instance === (int)$quiz->id) {
189 $SESSION->modform->questions = $questionlist;
190 $SESSION->modform->grades[$question->id] = $SESSION->modform->grades[$oldquestionid];
191 unset($SESSION->modform->grades[$oldquestionid]);
192 }
193 }
194
195 // change question in attempts
196 if ($attempts = get_records_list('quiz_attempts', 'quiz', implode(',', $replaceinquiz))) {
197 foreach ($attempts as $attempt) {
198
199 // replace question id in $attempt->layout
200 $questionlist = ",$attempt->layout,"; // a little hack with the commas here. not nice but effective
201 $questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist);
202 $questionlist = substr($questionlist, 1, -1); // and get rid of the surrounding commas again
203 if (!set_field('quiz_attempts', 'layout', $questionlist, 'id', $attempt->id)) {
204 error("Could not update layout in attempt $attempt->id!");
205 }
206
207 // set originalquestion in states
4f48fb42 208 set_field('question_states', 'originalquestion', $oldquestionid, 'attempt', $attempt->uniqueid, 'question', $question->id, 'originalquestion', '0');
516cf3eb 209
210 // replace question id in states
4f48fb42 211 set_field('question_states', 'question', $question->id, 'attempt', $attempt->uniqueid, 'question', $oldquestionid);
516cf3eb 212
213 // replace question id in sessions
214 set_field('question_sessions', 'questionid', $question->id, 'attemptid', $attempt->uniqueid, 'questionid', $oldquestionid);
215
216 }
217
218 // Now do anything question-type specific that is required to replace the question
dc1f00de 219 // For example questions that use the question_answers table to hold part of their question will
516cf3eb 220 // have to recode the answer ids in the states
f02c6f01 221 $QTYPES[$question->qtype]->change_states_question($oldquestionid, $question, $attempts);
516cf3eb 222 }
223 }
224 }
225 } else {
226 // use the old code which simply overwrites old versions
227 // it is also used for creating new questions
76de2cdf 228
229 if (isset($form->makecopy)) {
230 $question->hidden = 0; // explicitly requested copies should be unhidden
231 $question->id = 0; // This will prompt save_question to create a new question
232 }
f02c6f01 233 $question = $QTYPES[$qtype]->save_question($question, $form, $course);
516cf3eb 234 $replaceinquiz = 'all';
235 }
236
f02c6f01 237 if (empty($question->errors) && $QTYPES[$qtype]->finished_edit_wizard($form)) {
516cf3eb 238 // DISABLED AUTOMATIC REGRADING
239 // Automagically regrade all attempts (and states) in the affected quizzes
240 //if (!empty($replaceinquiz)) {
f02c6f01 241 // $QTYPES[$question->qtype]->get_question_options($question);
516cf3eb 242 // quiz_regrade_question_in_quizzes($question, $replaceinquiz);
243 //}
ee6c9355 244
245 $strsaved = get_string('changessaved');
246 if ($inpopup) {
247 notify($strsaved, '');
248 close_window(3);
249 } else {
79b9651e 250 echo '</div>';
87391656 251 redirect($SESSION->returnurl);
ee6c9355 252 }
516cf3eb 253 }
254 }
8c2550c8 255 // TODO: remove restriction to quiz
256 $streditingquestion = get_string('editingquestion', 'quiz');
257 if (isset($SESSION->modform->instance)) {
258 $strediting = '<a href="'.$SESSION->returnurl.'">'.get_string('editingquiz', 'quiz').'</a> -> '.
259 $streditingquestion;
260 } else {
261 $strediting = '<a href="edit.php?courseid='.$course->id.'">'.
262 get_string("editquestions", "quiz").'</a> -> '.$streditingquestion;
263 }
264
265 print_header_simple($streditingquestion, '', $strediting);
516cf3eb 266
e586cfb4 267 // prepare the grades selector drop-down used by many question types
b4db945e 268 $creategrades = get_grade_options();
269 $gradeoptions = $creategrades->gradeoptions;
270 $gradeoptionsfull = $creategrades->gradeoptionsfull;
516cf3eb 271
cc080913 272 // Initialise defaults if necessary.
516cf3eb 273 if (empty($question->id)) {
274 $question->id = "";
275 }
276 if (empty($question->name)) {
277 $question->name = "";
278 }
279 if (empty($question->questiontext)) {
280 $question->questiontext = "";
281 }
282 if (empty($question->image)) {
283 $question->image = "";
284 }
285 if (!isset($question->penalty)) {
286 $question->penalty = 0.1;
287 }
288 if (!isset($question->defaultgrade)) {
289 $question->defaultgrade = 1;
290 }
a4514d91 291 if (empty($question->generalfeedback)) {
292 $question->generalfeedback = "";
1b8a7434 293 }
516cf3eb 294
cc080913 295 // Set up some richtext editing if necessary
516cf3eb 296 if ($usehtmleditor = can_use_richtext_editor()) {
297 $defaultformat = FORMAT_HTML;
298 } else {
299 $defaultformat = FORMAT_MOODLE;
300 }
301
302 if (isset($question->errors)) {
303 $err = $question->errors;
304 }
305
cc080913 306 // Print the question editing form
516cf3eb 307 echo '<br />';
308 print_simple_box_start('center');
4eda4eec 309 require_once('type/'.$qtype.'/editquestion.php');
516cf3eb 310 print_simple_box_end();
311
312 if ($usehtmleditor) {
313 use_html_editor('questiontext');
314 }
315
316 print_footer($course);
317
318?>