Merged from HEAD
[moodle.git] / mod / quiz / question.php
bloba01836c9eda637953ab747d6c76cfebe3e3b8075
1 <?php // $Id$
2 /*
3 * Page for editing questions
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 * - delete question from quiz (delete, sesskey)
9 * - delete question (in two steps)
10 * - if question is in use: display this conflict (allow to hide the question?)
11 * - else: confirm deletion and delete from database (sesskey, id, delete, confirm)
12 * - cancel (cancel)
13 * @version $Id$
14 * @author Martin Dougiamas and many others. This has recently been extensively
15 * rewritten by members of the Serving Mathematics project
16 * {@link http://maths.york.ac.uk/serving_maths}
17 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
18 * @package quiz
21 require_once("../../config.php");
22 require_once("locallib.php");
23 require_once($CFG->libdir.'/filelib.php');
25 $id = optional_param('id'); // question id
27 $qtype = optional_param('qtype');
28 $category = optional_param('category');
30 // a qtype > 99 means a remote question
31 if ($qtype > 99) {
32 $typeid = $qtype - 100;
33 $qtype = RQP;
36 $contextquiz = optional_param('contextquiz'); // the quiz from which this question is being edited
38 if (isset($_REQUEST['cancel'])) {
39 redirect("edit.php");
42 if(!empty($id) && isset($_REQUEST['hide']) && confirm_sesskey()) {
43 if(!set_field('quiz_questions', 'hidden', $_REQUEST['hide'], 'id', $id)) {
44 error("Faild to hide the question.");
46 redirect('edit.php');
49 if ($id) {
50 if (! $question = get_record("quiz_questions", "id", $id)) {
51 error("This question doesn't exist");
53 if (!empty($category)) {
54 $question->category = $category;
56 if (! $category = get_record("quiz_categories", "id", $question->category)) {
57 error("This question doesn't belong to a valid category!");
59 if (! $course = get_record("course", "id", $category->course)) {
60 error("This question category doesn't belong to a valid course!");
63 $qtype = $question->qtype;
66 } else if ($category) { // only for creating new questions
67 if (! $category = get_record("quiz_categories", "id", $category)) {
68 error("This wasn't a valid category!");
70 if (! $course = get_record("course", "id", $category->course)) {
71 error("This category doesn't belong to a valid course!");
74 $question->category = $category->id;
75 $question->qtype = $qtype;
77 } else {
78 error("Must specify question id or category");
81 if (empty($qtype)) {
82 error("No question type was specified!");
83 } else if (!isset($QUIZ_QTYPES[$qtype])) {
84 error("Could not find question type: '$qtype'");
87 require_login($course->id, false);
89 if (!isteacheredit($course->id)) {
90 error("You can't modify these questions!");
93 $strquizzes = get_string('modulenameplural', 'quiz');
94 $streditingquestion = get_string('editingquestion', 'quiz');
95 if (isset($SESSION->modform->instance)) {
96 $strediting = '<a href="edit.php">'.get_string('editingquiz', 'quiz').'</a> -> '.
97 $streditingquestion;
98 } else {
99 $strediting = '<a href="edit.php?courseid='.$course->id.'">'.
100 get_string("editquestions", "quiz").'</a> -> '.$streditingquestion;
103 print_header_simple("$streditingquestion", "",
104 "<a href=\"$CFG->wwwroot/mod/quiz/index.php?id=$course->id\">$strquizzes</a>".
105 " -> ".$strediting);
107 if (isset($_REQUEST['delete'])) {
108 if (isset($confirm) and confirm_sesskey()) {
109 if ($confirm == md5($delete)) {
110 if (record_exists('quiz_question_instances', 'question', $question->id) or
111 record_exists('quiz_states', 'originalquestion', $question->id)) {
112 if (!set_field('quiz_questions', 'hidden', 1, 'id', $delete)) {
113 error('Was not able to hide question');
115 } else {
116 if (!delete_records("quiz_questions", "id", $question->id)) {
117 error("An error occurred trying to delete question (id $question->id)");
119 if (!delete_records("quiz_questions", "parent", $question->id)) {
120 error("An error occurred trying to delete question (id $question->id)");
123 redirect("edit.php");
124 } else {
125 error("Confirmation string was incorrect");
128 } else {
129 if ($quiznames = quizzes_question_used($id)) {
130 $a->questionname = $question->name;
131 $a->quiznames = implode(', ', $quiznames);
132 notify(get_string('questioninuse', 'quiz', $a));
135 notice_yesno(get_string("deletequestioncheck", "quiz", $question->name),
136 "question.php?sesskey=$USER->sesskey&amp;id=$question->id&amp;delete=$delete&amp;confirm=".md5($delete), "edit.php");
138 print_footer($course);
139 exit;
142 if ($form = data_submitted() and confirm_sesskey()) {
144 if (isset($form->versioning) && isset($question->id) and false) { // disable versioning until it is fixed.
145 // use new code that handles whether to overwrite or copy a question
146 // and keeps track of the versions in the quiz_question_version table
148 // $replaceinquiz is an array with the ids of all quizzes in which
149 // the teacher has chosen to replace the old version
150 $replaceinquiz = array();
151 foreach($form as $key => $val) {
152 if ($tmp = quiz_parse_fieldname($key, 'q')) {
153 if ($tmp['mode'] == 'replace') {
154 $replaceinquiz[$tmp['id']] = $tmp['id'];
155 unset($form->$key);
160 // $quizlist is an array with the ids of quizzes which use this question
161 $quizlist = array();
162 if ($instances = get_records('quiz_question_instances', 'question', $question->id)) {
163 foreach($instances as $instance) {
164 $quizlist[$instance->quiz] = $instance->quiz;
168 if (isset($form->makecopy)) { // explicitly requested copies should be unhidden
169 $question->hidden = 0;
172 // Logic to determine whether old version should be overwritten
173 $makecopy = isset($form->makecopy) || (!$form->id); unset($form->makecopy);
174 if ($makecopy) {
175 $replaceold = false;
176 } else {
177 // this should be improved to exclude teacher preview responses and empty responses
178 // the current code leaves many unneeded questions in the database
179 $hasresponses = record_exists('quiz_states', 'question', $form->id) or
180 record_exists('quiz_states', 'originalquestion', $form->id);
181 $replaceinall = ($quizlist == $replaceinquiz); // question is being replaced in all quizzes
182 $replaceold = !$hasresponses && $replaceinall;
185 if (!$replaceold) { // create a new question
186 $oldquestionid = $question->id;
187 if (!$makecopy) {
188 if (!set_field("quiz_questions", 'hidden', 1, 'id', $question->id)) {
189 error("Could not hide question!");
192 unset($question->id);
194 unset($makecopy, $hasresponses, $replaceinall, $replaceold);
195 $question = $QUIZ_QTYPES[$qtype]->save_question($question, $form, $course);
196 if(!isset($question->id)) {
197 error("Failed to save the question!");
200 if(isset($oldquestionid)) {
201 // create version entries for different quizzes
202 $version = new object();
203 $version->oldquestion = $oldquestionid;
204 $version->newquestion = $question->id;
205 $version->userid = $USER->id;
206 $version->timestamp = time();
208 foreach($replaceinquiz as $qid) {
209 $version->quiz = $qid;
210 if(!insert_record("quiz_question_versions", $version)) {
211 error("Could not store version information of question $oldquestionid in quiz $qid!");
215 /// now update the question references in the quizzes
216 if (!empty($replaceinquiz) and $quizzes = get_records_list("quiz", "id", implode(',', $replaceinquiz))) {
218 foreach($quizzes as $quiz) {
219 $questionlist = ",$quiz->questions,"; // a little hack with the commas here. not nice but effective
220 $questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist);
221 $questionlist = substr($questionlist, 1, -1); // and get rid of the surrounding commas again
222 if (!set_field("quiz", 'questions', $questionlist, 'id', $quiz->id)) {
223 error("Could not update questionlist in quiz $quiz->id!");
226 // the quiz_question_instances table needs to be updated too (aah, the joys of duplication :)
227 if (!set_field('quiz_question_instances', 'question', $question->id, 'quiz', $quiz->id, 'question', $oldquestionid)) {
228 error("Could not update question instance!");
230 if (isset($SESSION->modform) && (int)$SESSION->modform->instance === (int)$quiz->id) {
231 $SESSION->modform->questions = $questionlist;
232 $SESSION->modform->grades[$question->id] = $SESSION->modform->grades[$oldquestionid];
233 unset($SESSION->modform->grades[$oldquestionid]);
237 // change question in attempts
238 if ($attempts = get_records_list('quiz_attempts', 'quiz', implode(',', $replaceinquiz))) {
239 foreach ($attempts as $attempt) {
241 // replace question id in $attempt->layout
242 $questionlist = ",$attempt->layout,"; // a little hack with the commas here. not nice but effective
243 $questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist);
244 $questionlist = substr($questionlist, 1, -1); // and get rid of the surrounding commas again
245 if (!set_field('quiz_attempts', 'layout', $questionlist, 'id', $attempt->id)) {
246 error("Could not update layout in attempt $attempt->id!");
249 // set originalquestion in states
250 set_field('quiz_states', 'originalquestion', $oldquestionid, 'attempt', $attempt->id, 'question', $question->id, 'originalquestion', '0');
252 // replace question id in states
253 set_field('quiz_states', 'question', $question->id, 'attempt', $attempt->id, 'question', $oldquestionid);
255 // replace question id in newest_states
256 set_field('quiz_newest_states', 'questionid', $question->id, 'attemptid', $attempt->id, 'questionid', $oldquestionid);
260 // Now do anything question-type specific that is required to replace the question
261 // For example questions that use the quiz_answers table to hold part of their question will
262 // have to recode the answer ids in the states
263 $QUIZ_QTYPES[$question->qtype]->change_states_question($oldquestionid, $question, $attempts);
267 } else {
268 // use the old code which simply overwrites old versions
269 // it is also used for creating new questions
270 $question = $QUIZ_QTYPES[$qtype]->save_question($question, $form, $course);
271 $replaceinquiz = 'all';
274 if (empty($question->errors) && $QUIZ_QTYPES[$qtype]->finished_edit_wizard($form)) {
275 // DISABLED AUTOMATIC REGRADING
276 // Automagically regrade all attempts (and states) in the affected quizzes
277 //if (!empty($replaceinquiz)) {
278 // $QUIZ_QTYPES[$question->qtype]->get_question_options($question);
279 // quiz_regrade_question_in_quizzes($question, $replaceinquiz);
281 redirect("edit.php");
285 $grades = array(1,0.9,0.8,0.75,0.70,0.66666,0.60,0.50,0.40,0.33333,0.30,0.25,0.20,0.16666,0.142857,0.10,0.05,0);
286 foreach ($grades as $grade) {
287 $percentage = 100 * $grade;
288 $neggrade = -$grade;
289 $gradeoptions["$grade"] = "$percentage %";
290 $gradeoptionsfull["$grade"] = "$percentage %";
291 $gradeoptionsfull["$neggrade"] = -$percentage." %";
293 $gradeoptionsfull["0"] = $gradeoptions["0"] = get_string("none");
295 arsort($gradeoptions, SORT_NUMERIC);
296 arsort($gradeoptionsfull, SORT_NUMERIC);
298 if (!$categories = quiz_get_category_menu($course->id, false)) {
299 error("No categories!");
303 make_upload_directory("$course->id"); // Just in case
304 $coursefiles = get_directory_list("$CFG->dataroot/$course->id", $CFG->moddata);
305 foreach ($coursefiles as $filename) {
306 if (mimeinfo("icon", $filename) == "image.gif") {
307 $images["$filename"] = $filename;
311 // Print the question editing form
313 if (empty($question->id)) {
314 $question->id = "";
316 if (empty($question->name)) {
317 $question->name = "";
319 if (empty($question->questiontext)) {
320 $question->questiontext = "";
322 if (empty($question->image)) {
323 $question->image = "";
325 if (!isset($question->penalty)) {
326 $question->penalty = 0.1;
329 // Set up some Richtext editing if necessary
330 if ($usehtmleditor = can_use_richtext_editor()) {
331 $defaultformat = FORMAT_HTML;
332 } else {
333 $defaultformat = FORMAT_MOODLE;
336 if (isset($question->errors)) {
337 $err = $question->errors;
340 echo '<br />';
341 print_simple_box_start('center');
342 require('questiontypes/'.$QUIZ_QTYPES[$qtype]->name().'/editquestion.php');
343 print_simple_box_end();
345 if ($usehtmleditor) {
346 use_html_editor('questiontext');
349 print_footer($course);