In remove_course_contents() look for a module function called
[moodle.git] / mod / quiz / lib.php
CommitLineData
730fd187 1<?PHP // $Id$
2
a5e1f35c 3/// Library of function for module quiz
730fd187 4
a5e1f35c 5/// CONSTANTS ///////////////////////////////////////////////////////////////////
730fd187 6
a5e1f35c 7define("GRADEHIGHEST", "1");
8define("GRADEAVERAGE", "2");
9define("ATTEMPTFIRST", "3");
10define("ATTEMPTLAST", "4");
11$QUIZ_GRADE_METHOD = array ( GRADEHIGHEST => get_string("gradehighest", "quiz"),
12 GRADEAVERAGE => get_string("gradeaverage", "quiz"),
13 ATTEMPTFIRST => get_string("attemptfirst", "quiz"),
14 ATTEMPTLAST => get_string("attemptlast", "quiz"));
15
54a67a59 16define("SHORTANSWER", "1");
17define("TRUEFALSE", "2");
18define("MULTICHOICE", "3");
19define("RANDOM", "4");
20define("MATCH", "5");
21define("RANDOMSAMATCH", "6");
401c8de6 22define("DESCRIPTION", "7");
361f649d 23define("NUMERICAL", "8");
8b439f8c 24define("MULTIANSWER", "9");
54a67a59 25
26$QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"),
27 TRUEFALSE => get_string("truefalse", "quiz"),
28 SHORTANSWER => get_string("shortanswer", "quiz"),
361f649d 29 NUMERICAL => get_string("numerical", "quiz"),
54a67a59 30 MATCH => get_string("match", "quiz"),
361f649d 31 DESCRIPTION => get_string("description", "quiz"),
34d52ad7 32 RANDOM => get_string("random", "quiz"),
8b439f8c 33 RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
34 MULTIANSWER => get_string("multianswer", "quiz")
361f649d 35 );
a5e1f35c 36
49220fa7 37
5325f8b8 38define("QUIZ_PICTURE_MAX_HEIGHT", "600"); // Not currently implemented
39define("QUIZ_PICTURE_MAX_WIDTH", "600"); // Not currently implemented
a5e1f35c 40
29fb5974 41define("QUIZ_MAX_NUMBER_ANSWERS", "10");
54a67a59 42
a5e1f35c 43/// FUNCTIONS ///////////////////////////////////////////////////////////////////
730fd187 44
45function quiz_add_instance($quiz) {
a5e1f35c 46/// Given an object containing all the necessary data,
47/// (defined by the form in mod.html) this function
48/// will create a new instance and return the id number
49/// of the new instance.
730fd187 50
49dcdd18 51 $quiz->created = time();
730fd187 52 $quiz->timemodified = time();
49dcdd18 53 $quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday,
c04c41c7 54 $quiz->openhour, $quiz->openminute, 0);
49dcdd18 55 $quiz->timeclose = make_timestamp($quiz->closeyear, $quiz->closemonth, $quiz->closeday,
c04c41c7 56 $quiz->closehour, $quiz->closeminute, 0);
730fd187 57
7bd1aa1d 58 if (!$quiz->id = insert_record("quiz", $quiz)) {
59 return false; // some error occurred
60 }
61
10b9291c 62 // The grades for every question in this quiz are stored in an array
7bd1aa1d 63 if ($quiz->grades) {
64 foreach ($quiz->grades as $question => $grade) {
401c8de6 65 if ($question) {
8d94f5a0 66 unset($questiongrade);
67 $questiongrade->quiz = $quiz->id;
68 $questiongrade->question = $question;
69 $questiongrade->grade = $grade;
70 if (!insert_record("quiz_question_grades", $questiongrade)) {
71 return false;
72 }
7bd1aa1d 73 }
74 }
75 }
730fd187 76
7bd1aa1d 77 return $quiz->id;
730fd187 78}
79
80
81function quiz_update_instance($quiz) {
a5e1f35c 82/// Given an object containing all the necessary data,
83/// (defined by the form in mod.html) this function
84/// will update an existing instance with new data.
730fd187 85
86 $quiz->timemodified = time();
49dcdd18 87 $quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday,
c04c41c7 88 $quiz->openhour, $quiz->openminute, 0);
49dcdd18 89 $quiz->timeclose = make_timestamp($quiz->closeyear, $quiz->closemonth, $quiz->closeday,
c04c41c7 90 $quiz->closehour, $quiz->closeminute, 0);
730fd187 91 $quiz->id = $quiz->instance;
92
7bd1aa1d 93 if (!update_record("quiz", $quiz)) {
94 return false; // some error occurred
95 }
730fd187 96
7bd1aa1d 97
10b9291c 98 // The grades for every question in this quiz are stored in an array
7bd1aa1d 99 // Insert or update records as appropriate
100
101 $existing = get_records("quiz_question_grades", "quiz", $quiz->id, "", "question,grade,id");
102
103 if ($quiz->grades) {
104 foreach ($quiz->grades as $question => $grade) {
401c8de6 105 if ($question) {
8d94f5a0 106 unset($questiongrade);
107 $questiongrade->quiz = $quiz->id;
108 $questiongrade->question = $question;
109 $questiongrade->grade = $grade;
110 if (isset($existing[$question])) {
111 if ($existing[$question]->grade != $grade) {
112 $questiongrade->id = $existing[$question]->id;
113 if (!update_record("quiz_question_grades", $questiongrade)) {
114 return false;
115 }
116 }
117 } else {
118 if (!insert_record("quiz_question_grades", $questiongrade)) {
7bd1aa1d 119 return false;
120 }
121 }
7bd1aa1d 122 }
123 }
124 }
125
126 return true;
730fd187 127}
128
129
130function quiz_delete_instance($id) {
a5e1f35c 131/// Given an ID of an instance of this module,
132/// this function will permanently delete the instance
133/// and any data that depends on it.
730fd187 134
135 if (! $quiz = get_record("quiz", "id", "$id")) {
136 return false;
137 }
138
139 $result = true;
140
10b9291c 141 if ($attempts = get_record("quiz_attempts", "quiz", "$quiz->id")) {
142 foreach ($attempts as $attempt) {
143 if (! delete_records("quiz_responses", "attempt", "$attempt->id")) {
144 $result = false;
145 }
146 }
147 }
148
149 if (! delete_records("quiz_attempts", "quiz", "$quiz->id")) {
150 $result = false;
151 }
152
153 if (! delete_records("quiz_grades", "quiz", "$quiz->id")) {
154 $result = false;
155 }
156
157 if (! delete_records("quiz_question_grades", "quiz", "$quiz->id")) {
158 $result = false;
159 }
730fd187 160
161 if (! delete_records("quiz", "id", "$quiz->id")) {
162 $result = false;
163 }
164
165 return $result;
166}
167
168function quiz_user_outline($course, $user, $mod, $quiz) {
a5e1f35c 169/// Return a small object with summary information about what a
170/// user has done with a given particular instance of this module
171/// Used for user activity reports.
172/// $return->time = the time they did it
173/// $return->info = a short text description
ebc3bd2b 174 if ($grade = get_record("quiz_grades", "userid", $user->id, "quiz", $quiz->id)) {
98092498 175
176 if ($grade->grade) {
177 $result->info = get_string("grade").": $grade->grade";
178 }
179 $result->time = $grade->timemodified;
180 return $result;
181 }
182 return NULL;
730fd187 183
184 return $return;
185}
186
187function quiz_user_complete($course, $user, $mod, $quiz) {
a5e1f35c 188/// Print a detailed representation of what a user has done with
189/// a given particular instance of this module, for user activity reports.
730fd187 190
191 return true;
192}
193
730fd187 194function quiz_cron () {
a5e1f35c 195/// Function to be run periodically according to the moodle cron
196/// This function searches for things that need to be done, such
197/// as sending out mail, toggling flags etc ...
730fd187 198
199 global $CFG;
200
201 return true;
202}
203
d0ac6bc2 204function quiz_grades($quizid) {
858deff0 205/// Must return an array of grades, indexed by user, and a max grade.
206
ed1daaa9 207 $quiz = get_record("quiz", "id", $quizid);
208 if (empty($quiz) or empty($quiz->grade)) {
209 return NULL;
210 }
211
ebc3bd2b 212 $return->grades = get_records_menu("quiz_grades", "quiz", $quizid, "", "userid,grade");
858deff0 213 $return->maxgrade = get_field("quiz", "grade", "id", "$quizid");
214 return $return;
d0ac6bc2 215}
216
d061d883 217function quiz_get_participants($quizid) {
218/// Returns an array of users who have data in a given quiz
219/// (users with records in quiz_attempts, students)
220
221 global $CFG;
222
223 return get_records_sql("SELECT DISTINCT u.*
224 FROM {$CFG->prefix}user u,
225 {$CFG->prefix}quiz_attempts a
226 WHERE a.quiz = '$quizid' and
227 u.id = a.userid");
228}
730fd187 229
bdc23be0 230/// SQL FUNCTIONS ////////////////////////////////////////////////////////////////////
231
232function quiz_move_questions($category1, $category2) {
233 global $CFG;
234 return execute_sql("UPDATE {$CFG->prefix}quiz_questions
235 SET category = '$category2'
236 WHERE category = '$category1'",
237 false);
238}
239
240function quiz_get_question_grades($quizid, $questionlist) {
241 global $CFG;
242
243 return get_records_sql("SELECT question,grade
244 FROM {$CFG->prefix}quiz_question_grades
245 WHERE quiz = '$quizid'
246 AND question IN ($questionlist)");
247}
248
34d52ad7 249function quiz_get_random_categories($questionlist) {
250/// Given an array of questions, this function looks for random
251/// questions among them and returns a list of categories with
252/// an associated count of random questions for each.
253
254 global $CFG;
255
256 return get_records_sql_menu("SELECT category,count(*)
257 FROM {$CFG->prefix}quiz_questions
258 WHERE id IN ($questionlist)
259 AND qtype = '".RANDOM."'
260 GROUP BY category ");
261}
262
bdc23be0 263function quiz_get_grade_records($quiz) {
264/// Gets all info required to display the table of quiz results
265/// for report.php
266 global $CFG;
267
268 return get_records_sql("SELECT qg.*, u.firstname, u.lastname, u.picture
269 FROM {$CFG->prefix}quiz_grades qg,
270 {$CFG->prefix}user u
271 WHERE qg.quiz = '$quiz->id'
ebc3bd2b 272 AND qg.userid = u.id");
bdc23be0 273}
274
8b439f8c 275function quiz_get_answers($question, $answerids=NULL) {
34d52ad7 276// Given a question, returns the correct answers for a given question
bdc23be0 277 global $CFG;
95dbc030 278
8b439f8c 279 if (empty($answerids)) {
280 $answeridconstraint = '';
281 } else {
282 $answeridconstraint = " AND a.id IN ($answerids) ";
283 }
284
a2fe7cc0 285 switch ($question->qtype) {
95dbc030 286 case SHORTANSWER: // Could be multiple answers
34d52ad7 287 return get_records_sql("SELECT a.*, sa.usecase
bdc23be0 288 FROM {$CFG->prefix}quiz_shortanswer sa,
34d52ad7 289 {$CFG->prefix}quiz_answers a
bdc23be0 290 WHERE sa.question = '$question->id'
8b439f8c 291 AND sa.question = a.question "
292 . $answeridconstraint);
bdc23be0 293
95dbc030 294 case TRUEFALSE: // Should be always two answers
34d52ad7 295 return get_records("quiz_answers", "question", $question->id);
bdc23be0 296
95dbc030 297 case MULTICHOICE: // Should be multiple answers
34d52ad7 298 return get_records_sql("SELECT a.*, mc.single
bdc23be0 299 FROM {$CFG->prefix}quiz_multichoice mc,
34d52ad7 300 {$CFG->prefix}quiz_answers a
bdc23be0 301 WHERE mc.question = '$question->id'
8b439f8c 302 AND mc.question = a.question "
303 . $answeridconstraint);
bdc23be0 304
54a67a59 305 case MATCH:
34d52ad7 306 return get_records("quiz_match_sub", "question", $question->id);
54a67a59 307
308 case RANDOMSAMATCH: // Could be any of many answers, return them all
34d52ad7 309 return get_records_sql("SELECT a.*
95dbc030 310 FROM {$CFG->prefix}quiz_questions q,
34d52ad7 311 {$CFG->prefix}quiz_answers a
95dbc030 312 WHERE q.category = '$question->category'
313 AND q.qtype = ".SHORTANSWER."
34d52ad7 314 AND q.id = a.question ");
95dbc030 315
361f649d 316 case NUMERICAL: // Logical support for multiple answers
317 return get_records_sql("SELECT a.*, n.min, n.max
318 FROM {$CFG->prefix}quiz_numerical n,
319 {$CFG->prefix}quiz_answers a
320 WHERE a.question = '$question->id'
8b439f8c 321 AND n.answer = a.id "
322 . $answeridconstraint);
361f649d 323
44fc346f 324 case DESCRIPTION:
325 return true; // there are no answers for description
326
327 case RANDOM:
328 return quiz_get_answers
329 (get_record('quiz_questions', 'id', $question->random));
361f649d 330
8b439f8c 331 case MULTIANSWER: // Includes subanswers
332 $multianswers = get_records('quiz_multianswers',
333 'question', $question->id);
334 $virtualquestion->id = $question->id;
335
336 $answers = array();
337 foreach ($multianswers as $multianswer) {
338 $virtualquestion->qtype = $multianswer->answertype;
339 // Recursive call for subanswers
340 $multianswer->subanswers = quiz_get_answers
341 ($virtualquestion, $multianswer->answers);
342 $answers[] = $multianswer;
343 }
344 return $answers;
345
bdc23be0 346 default:
347 return false;
348 }
349}
350
351
586b2c82 352function quiz_get_attempt_responses($attempt) {
bdc23be0 353// Given an attempt object, this function gets all the
354// stored responses and returns them in a format suitable
355// for regrading using quiz_grade_attempt_results()
356 global $CFG;
357
5ca03851 358 if (!$responses = get_records_sql("SELECT q.id, q.qtype, q.category, q.questiontext,
359 q.defaultgrade, q.image, r.answer
bdc23be0 360 FROM {$CFG->prefix}quiz_responses r,
361 {$CFG->prefix}quiz_questions q
362 WHERE r.attempt = '$attempt->id'
34d52ad7 363 AND q.id = r.question")) {
bdc23be0 364 notify("Could not find any responses for that attempt!");
365 return false;
366 }
367
34d52ad7 368
369 foreach ($responses as $key => $response) {
370 if ($response->qtype == RANDOM) {
371 $responses[$key]->random = $response->answer;
34d52ad7 372 $responses[$response->answer]->delete = true;
0a660b03 373
374 $realanswer = $responses[$response->answer]->answer;
375
376 if (is_array($realanswer)) {
377 $responses[$key]->answer = $realanswer;
378 } else {
379 $responses[$key]->answer = explode(",", $realanswer);
380 }
381
3960a44b 382 } else if ($response->qtype == NUMERICAL or $response->qtype == SHORTANSWER) {
383 $responses[$key]->answer = array($response->answer);
34d52ad7 384 } else {
385 $responses[$key]->answer = explode(",",$response->answer);
386 }
387 }
bdc23be0 388 foreach ($responses as $key => $response) {
34d52ad7 389 if (!empty($response->delete)) {
390 unset($responses[$key]);
391 }
bdc23be0 392 }
393
394 return $responses;
395}
396
397
398
730fd187 399//////////////////////////////////////////////////////////////////////////////////////
a5e1f35c 400/// Any other quiz functions go here. Each of them must have a name that
401/// starts with quiz_
730fd187 402
a8a372cc 403function quiz_print_comment($text) {
404 global $THEME;
405
d7512435 406 echo "<span class=\"feedbacktext\">".text_to_html($text, true, false)."</span>";
a8a372cc 407}
408
8db3eadd 409function quiz_print_correctanswer($text) {
410 global $THEME;
411
d7512435 412 echo "<p align=\"right\"><span class=\"highlight\">$text</span></p>";
8db3eadd 413}
414
34d52ad7 415function quiz_print_question_icon($question, $editlink=true) {
c74a0ca5 416// Prints a question icon
cc3b8c75 417
418 global $QUIZ_QUESTION_TYPE;
419
34d52ad7 420 if ($editlink) {
361f649d 421 echo "<a href=\"question.php?id=$question->id\" title=\"".$QUIZ_QUESTION_TYPE[$question->qtype]."\">";
34d52ad7 422 }
a2fe7cc0 423 switch ($question->qtype) {
c74a0ca5 424 case SHORTANSWER:
89b8d337 425 echo '<img border="0" height="16" width="16" src="pix/sa.gif">';
c74a0ca5 426 break;
427 case TRUEFALSE:
89b8d337 428 echo '<img border="0" height="16" width="16" src="pix/tf.gif">';
c74a0ca5 429 break;
430 case MULTICHOICE:
89b8d337 431 echo '<img border="0" height="16" width="16" src="pix/mc.gif">';
c74a0ca5 432 break;
433 case RANDOM:
89b8d337 434 echo '<img border="0" height="16" width="16" src="pix/rs.gif">';
c74a0ca5 435 break;
54a67a59 436 case MATCH:
89b8d337 437 echo '<img border="0" height="16" width="16" src="pix/ma.gif">';
54a67a59 438 break;
439 case RANDOMSAMATCH:
89b8d337 440 echo '<img border="0" height="16" width="16" src="pix/rm.gif">';
95dbc030 441 break;
401c8de6 442 case DESCRIPTION:
89b8d337 443 echo '<img border="0" height="16" width="16" src="pix/de.gif">';
361f649d 444 break;
445 case NUMERICAL:
89b8d337 446 echo '<img border="0" height="16" width="16" src="pix/nu.gif">';
401c8de6 447 break;
8b439f8c 448 case MULTIANSWER:
89b8d337 449 echo '<img border="0" height="16" width="16" src="pix/mu.gif">';
8b439f8c 450 break;
c74a0ca5 451 }
34d52ad7 452 if ($editlink) {
361f649d 453 echo "</a>\n";
34d52ad7 454 }
c74a0ca5 455}
456
fe98ea90 457function quiz_print_possible_question_image($quizid, $question) {
458// Includes the question image is there is one
a8a372cc 459
fe98ea90 460 global $CFG;
461
462 if ($question->image) {
89b8d337 463 echo '<img border="0" src="';
fe98ea90 464
89b8d337 465 if (substr(strtolower($question->image), 0, 7) == 'http://') {
fe98ea90 466 echo $question->image;
467
468 } else if ($CFG->slasharguments) { // Use this method if possible for better caching
469 echo "$CFG->wwwroot/mod/quiz/quizfile.php/$quizid/$question->id/$question->image";
470
471 } else {
472 echo "$CFG->wwwroot/mod/quiz/quizfile.php?file=/$quizid/$question->id/$question->image";
473 }
89b8d337 474 echo '" />';
fe98ea90 475
476 }
477}
34d52ad7 478
fe98ea90 479function quiz_print_question($number, $question, $grade, $quizid,
34d52ad7 480 $feedback=NULL, $response=NULL, $actualgrade=NULL, $correct=NULL,
081bf74f 481 $realquestion=NULL, $shuffleanswers=false, $showgrades=true) {
34d52ad7 482
483/// Prints a quiz question, any format
484/// $question is provided as an object
14d8c0b4 485
263cff8b 486
401c8de6 487 if ($question->qtype == DESCRIPTION) { // Special case question - has no answers etc
488 echo '<p align="center">';
489 echo text_to_html($question->questiontext);
fe98ea90 490 quiz_print_possible_question_image($quizid, $question);
401c8de6 491 echo '</p>';
492 return true;
493 }
494
a2fe7cc0 495 if (empty($actualgrade)) {
496 $actualgrade = 0;
497 }
498
14d8c0b4 499 $stranswer = get_string("answer", "quiz");
500 $strmarks = get_string("marks", "quiz");
501
89b8d337 502 echo '<table width="100%" cellspacing="10">';
503 echo '<tr><td nowrap="nowrap" width="100" valign="top">';
e0971655 504 echo '<p align="center"><b>' . $number . '</b></p>';
081bf74f 505 if ($showgrades) {
506 if ($feedback or $response) {
d7512435 507 echo "<p align=\"center\"><font size=\"1\">$strmarks: $actualgrade/$grade</font></p>";
081bf74f 508 } else {
d7512435 509 echo "<p align=\"center\"><font size=\"1\">$grade $strmarks</font></p>";
081bf74f 510 }
a8a372cc 511 }
14d8c0b4 512 print_spacer(1,100);
5a24a018 513
fe98ea90 514 if (isset($question->recentlyadded) and $question->recentlyadded) {
89b8d337 515 echo '</td><td valign="top" align="right">';
5a24a018 516 // Notify the user of this recently added question
517 echo '<font color="red">';
518 echo get_string('recentlyaddedquestion', 'quiz');
519 echo '</font>';
89b8d337 520 echo '</td></tr><tr><td></td><td valign="top">';
5a24a018 521
522 } else { // The normal case
89b8d337 523 echo '</td><td valign="top">';
5a24a018 524 }
525
34d52ad7 526
527 if (empty($realquestion)) {
528 $realquestion->id = $question->id;
529 } else { // Add a marker to connect this question to the actual random parent
d7512435 530 echo "<input type=\"hidden\" name=\"q{$realquestion->id}rq$question->id\" value=\"x\" />\n";
34d52ad7 531 }
14d8c0b4 532
a2fe7cc0 533 switch ($question->qtype) {
34d52ad7 534
a5e1f35c 535 case SHORTANSWER:
361f649d 536 case NUMERICAL:
2a2c9725 537 echo text_to_html($question->questiontext);
fe98ea90 538 quiz_print_possible_question_image($quizid, $question);
a8a372cc 539 if ($response) {
d7512435 540 $value = "value=\"$response[0]\"";
a2fe7cc0 541 } else {
542 $value = "";
a8a372cc 543 }
d7512435 544 echo "<p align=\"right\">$stranswer: <input type=\"text\" name=\"q$realquestion->id\" size=\"20\" $value /></p>";
a8a372cc 545 if ($feedback) {
d7512435 546 quiz_print_comment("<p align=\"right\">$feedback[0]</p>");
a8a372cc 547 }
8db3eadd 548 if ($correct) {
c74a0ca5 549 $correctanswers = implode(", ", $correct);
8db3eadd 550 quiz_print_correctanswer($correctanswers);
551 }
14d8c0b4 552 break;
553
a5e1f35c 554 case TRUEFALSE:
14d8c0b4 555 if (!$options = get_record("quiz_truefalse", "question", $question->id)) {
556 notify("Error: Missing question options!");
557 }
a2fe7cc0 558 if (!$true = get_record("quiz_answers", "id", $options->trueanswer)) {
14d8c0b4 559 notify("Error: Missing question answers!");
560 }
a2fe7cc0 561 if (!$false = get_record("quiz_answers", "id", $options->falseanswer)) {
14d8c0b4 562 notify("Error: Missing question answers!");
563 }
564 if (!$true->answer) {
565 $true->answer = get_string("true", "quiz");
566 }
567 if (!$false->answer) {
568 $false->answer = get_string("false", "quiz");
569 }
2a2c9725 570 echo text_to_html($question->questiontext);
fe98ea90 571 quiz_print_possible_question_image($quizid, $question);
a8a372cc 572
a2fe7cc0 573 $truechecked = "";
574 $falsechecked = "";
575
576 if (!empty($response[$true->id])) {
89b8d337 577 $truechecked = 'checked="checked"';
a8a372cc 578 $feedbackid = $true->id;
a2fe7cc0 579 } else if (!empty($response[$false->id])) {
89b8d337 580 $falsechecked = 'checked="checked"';
a8a372cc 581 $feedbackid = $false->id;
582 }
a2fe7cc0 583
584 $truecorrect = "";
585 $falsecorrect = "";
8db3eadd 586 if ($correct) {
a2fe7cc0 587 if (!empty($correct[$true->id])) {
89b8d337 588 $truecorrect = 'class="highlight"';
8db3eadd 589 }
a2fe7cc0 590 if (!empty($correct[$false->id])) {
89b8d337 591 $falsecorrect = 'class="highlight"';
8db3eadd 592 }
593 }
d7512435 594 echo "<table align=\"right\" cellpadding=\"5\"><tr><td align=\"right\">$stranswer:&nbsp;&nbsp;";
595 echo "<td $truecorrect>";
596 echo "<input $truechecked type=\"radio\" name=\"q$realquestion->id\" value=\"$true->id\" />$true->answer";
597 echo "</td><td $falsecorrect>";
598 echo "<input $falsechecked type=\"radio\" name=\"q$realquestion->id\" value=\"$false->id\" />$false->answer";
599 echo "</td></tr></table><br clear=\"all\">";// changed from CLEAR=ALL jm
a8a372cc 600 if ($feedback) {
d7512435 601 quiz_print_comment("<p align=\"right\">$feedback[$feedbackid]</p>");
a8a372cc 602 }
603
14d8c0b4 604 break;
605
a5e1f35c 606 case MULTICHOICE:
14d8c0b4 607 if (!$options = get_record("quiz_multichoice", "question", $question->id)) {
608 notify("Error: Missing question options!");
609 }
a5e1f35c 610 if (!$answers = get_records_list("quiz_answers", "id", $options->answers)) {
14d8c0b4 611 notify("Error: Missing question answers!");
612 }
2a2c9725 613 echo text_to_html($question->questiontext);
fe98ea90 614 quiz_print_possible_question_image($quizid, $question);
d7512435 615 echo "<table align=\"right\">";
616 echo "<tr><td valign=\"top\">$stranswer:&nbsp;&nbsp;</td><td>";
617 echo "<table>";
14d8c0b4 618 $answerids = explode(",", $options->answers);
a8a372cc 619
4b85b717 620 if ($shuffleanswers) {
621 $answerids = swapshuffle($answerids);
622 }
623
14d8c0b4 624 foreach ($answerids as $key => $answerid) {
625 $answer = $answers[$answerid];
68fefdbe 626 $qnumchar = chr(ord('a') + $key);
a8a372cc 627
586b2c82 628 if (empty($response[$answerid])) {
a8a372cc 629 $checked = "";
8e6c87cc 630 } else {
89b8d337 631 $checked = 'checked="checked"';
a8a372cc 632 }
89b8d337 633 echo '<tr><td valign="top">';
a5e1f35c 634 if ($options->single) {
d7512435 635 echo "<input $checked type=\"radio\" name=\"q$realquestion->id\" value=\"$answer->id\" />";
14d8c0b4 636 } else {
d7512435 637 echo "<input $checked type=\"checkbox\" name=\"q$realquestion->id"."a$answer->id\" value=\"$answer->id\" />";
14d8c0b4 638 }
d7512435 639 echo "</td>";
8e6c87cc 640 if (empty($feedback) or empty($correct[$answer->id])) {
d7512435 641 echo "<td valign=\"top\">$qnumchar. $answer->answer</td>";
8e6c87cc 642 } else {
d7512435 643 echo "<td valign=\"top\" class=\"highlight\">$qnumchar. $answer->answer</td>";
8db3eadd 644 }
8e6c87cc 645 if (!empty($feedback)) {
d7512435 646 echo "<td valign=\"top\">&nbsp;";
8e6c87cc 647 if (!empty($response[$answerid])) {
a8a372cc 648 quiz_print_comment($feedback[$answerid]);
649 }
d7512435 650 echo "</td>";
a8a372cc 651 }
d7512435 652 echo "</tr>";
14d8c0b4 653 }
d7512435 654 echo "</table>";
655 echo "</td></tr></table>";
14d8c0b4 656 break;
657
54a67a59 658 case MATCH:
659 if (!$options = get_record("quiz_match", "question", $question->id)) {
660 notify("Error: Missing question options!");
661 }
662 if (!$subquestions = get_records_list("quiz_match_sub", "id", $options->subquestions)) {
663 notify("Error: Missing subquestions for this question!");
664 }
96192c44 665 if (!empty($question->questiontext)) {
666 echo text_to_html($question->questiontext);
4b85b717 667 }
fe98ea90 668 quiz_print_possible_question_image($quizid, $question);
54a67a59 669
96192c44 670 if ($shuffleanswers) {
671 $subquestions = draw_rand_array($subquestions, count($subquestions));
672 }
54a67a59 673 foreach ($subquestions as $subquestion) {
674 $answers[$subquestion->id] = $subquestion->answertext;
675 }
676
677 $answers = draw_rand_array($answers, count($answers));
678
89b8d337 679 echo '<table border="0" cellpadding="10" align="right">';
54a67a59 680 foreach ($subquestions as $key => $subquestion) {
89b8d337 681 echo '<tr><td align="left" valign="top">';
54a67a59 682 echo $subquestion->questiontext;
89b8d337 683 echo '</td>';
54a67a59 684 if (empty($response)) {
89b8d337 685 echo '<td align="right" valign="top">';
34d52ad7 686 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id");
54a67a59 687 } else {
688 if (empty($response[$key])) {
89b8d337 689 echo '<td align="right" valign="top">';
34d52ad7 690 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id");
54a67a59 691 } else {
692 if ($response[$key] == $correct[$key]) {
89b8d337 693 echo '<td align="right" valign="top" class="highlight">';
34d52ad7 694 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id", $response[$key]);
54a67a59 695 } else {
89b8d337 696 echo '<td align="right" valign="top">';
34d52ad7 697 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id", $response[$key]);
54a67a59 698 }
699 }
700
701 if (!empty($feedback[$key])) {
702 quiz_print_comment($feedback[$key]);
703 }
704 }
89b8d337 705 echo '</td></tr>';
54a67a59 706 }
89b8d337 707 echo '</table>';
54a67a59 708
709 break;
710
711 case RANDOMSAMATCH:
712 if (!$options = get_record("quiz_randomsamatch", "question", $question->id)) {
95dbc030 713 notify("Error: Missing question options!");
714 }
715 echo text_to_html($question->questiontext);
fe98ea90 716 quiz_print_possible_question_image($quizid, $question);
95dbc030 717
718 /// First, get all the questions available
719
720 $allquestions = get_records_select("quiz_questions",
721 "category = $question->category AND qtype = ".SHORTANSWER);
722 if (count($allquestions) < $options->choose) {
723 notify("Error: could not find enough Short Answer questions in the database!");
724 notify("Found ".count($allquestions).", need $options->choose.");
725 break;
726 }
727
728 if (empty($response)) { // Randomly pick the questions
729 if (!$randomquestions = draw_rand_array($allquestions, $options->choose)) {
730 notify("Error choosing $options->choose random questions");
731 break;
732 }
733 } else { // Use existing questions
734 $randomquestions = array();
735 foreach ($response as $key => $rrr) {
736 $rrr = explode("-", $rrr);
737 $randomquestions[$key] = $allquestions[$key];
738 $responseanswer[$key] = $rrr[1];
739 }
740 }
741
742 /// For each selected, find the best matching answers
743
744 foreach ($randomquestions as $randomquestion) {
745 $shortanswerquestion = get_record("quiz_shortanswer", "question", $randomquestion->id);
746 $questionanswers = get_records_list("quiz_answers", "id", $shortanswerquestion->answers);
747 $bestfraction = 0;
748 $bestanswer = NULL;
749 foreach ($questionanswers as $questionanswer) {
750 if ($questionanswer->fraction > $bestfraction) {
751 $bestanswer = $questionanswer;
752 }
753 }
754 if (empty($bestanswer)) {
755 notify("Error: Could not find the best answer for question: ".$randomquestions->name);
756 break;
757 }
758 $randomanswers[$bestanswer->id] = trim($bestanswer->answer);
759 }
760
761 if (!$randomanswers = draw_rand_array($randomanswers, $options->choose)) { // Mix them up
762 notify("Error randomising answers!");
763 break;
764 }
765
89b8d337 766 echo '<table border="0" cellpadding="10">';
95dbc030 767 foreach ($randomquestions as $key => $randomquestion) {
89b8d337 768 echo '<tr><td align="left" valign="top">';
95dbc030 769 echo $randomquestion->questiontext;
89b8d337 770 echo '</td>';
771 echo '<td align="right" valign="top">';
95dbc030 772 if (empty($response)) {
34d52ad7 773 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id");
95dbc030 774 } else {
775 if (!empty($correct[$key])) {
776 if ($randomanswers[$responseanswer[$key]] == $correct[$key]) {
89b8d337 777 echo '<span="highlight">';
34d52ad7 778 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]);
89b8d337 779 echo '</span><br \>';
95dbc030 780 } else {
34d52ad7 781 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]);
95dbc030 782 quiz_print_correctanswer($correct[$key]);
783 }
784 } else {
34d52ad7 785 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]);
95dbc030 786 }
787 if (!empty($feedback[$key])) {
788 quiz_print_comment($feedback[$key]);
789 }
790 }
89b8d337 791 echo '</td></tr>';
95dbc030 792 }
89b8d337 793 echo '</table>';
95dbc030 794 break;
795
8b439f8c 796 case MULTIANSWER:
797 // For this question type, we better print the image on top:
fe98ea90 798 quiz_print_possible_question_image($quizid, $question);
8b439f8c 799
800 $qtextremaining = text_to_html($question->questiontext);
801 // The regex will recognize text snippets of type {#X} where the X can be any text not containg } or white-space characters.
802 while (ereg('\{#([^[:space:]}]*)}', $qtextremaining, $regs)) {
803
804 $qtextsplits = explode($regs[0], $qtextremaining, 2);
805 echo $qtextsplits[0];
806 $qtextremaining = $qtextsplits[1];
807
808 $multianswer = get_record('quiz_multianswers',
809 'question', $question->id,
810 'positionkey', $regs[1]);
811
812 $inputname= " name=\"q{$realquestion->id}ma$multianswer->id\" ";
813
814 if (!empty($response)
7c9c2a8d 815 && ereg('(.[^-]*)-(.+)', array_shift($response), $responseitems))
8b439f8c 816 {
7c9c2a8d 817 $responsefractiongrade = (float)$responseitems[1];
818 $actualresponse = $responseitems[2];
8b439f8c 819
820 if (1.0 == $responsefractiongrade) {
bdb63d64 821 $style = 'style="background-color:lime"';
8b439f8c 822 } else if (0.0 < $responsefractiongrade) {
bdb63d64 823 $style = 'style="background-color:yellow"';
824 } else if ('' != $actualresponse) {
825 // The response must have been totally wrong:
826 $style = 'style="background-color:red"';
827 } else {
828 // There was no response given
829 $style = '';
8b439f8c 830 }
831 } else {
832 $responsefractiongrade = 0.0;
833 $actualresponse = '';
bdb63d64 834 $style = '';
8b439f8c 835 }
836
837 switch ($multianswer->answertype) {
838 case SHORTANSWER:
839 case NUMERICAL:
d7512435 840 echo " <input $style $inputname value=\"$actualresponse\" type=\"text\" size=\"8\" /> ";
8b439f8c 841 break;
842 case MULTICHOICE:
bdb63d64 843 echo (" <select $style $inputname>");
8b439f8c 844 $answers = get_records_list("quiz_answers", "id", $multianswer->answers);
845 echo ('<option></option>'); // Default empty option
846 foreach ($answers as $answer) {
847 if ($answer->id == $actualresponse) {
848 $selected = 'selected';
849 } else {
850 $selected = '';
851 }
852 echo "<option value=\"$answer->id\" $selected>$answer->answer</option>";
853 }
854 echo ("</select> ");
855 break;
856 default:
857 error("Unable to recognized answertype $answer->answertype");
858 break;
859 }
860 }
861 // Print the final piece of question text:
862 echo $qtextremaining;
863 break;
864
34d52ad7 865 case RANDOM:
5a24a018 866 // This can only happen if it is a recently added question
867
d7512435 868 echo '<p>' . get_string('random', 'quiz') . '</p>';
34d52ad7 869 break;
8db3eadd 870
14d8c0b4 871 default:
872 notify("Error: Unknown question type!");
873 }
874
d7512435 875 echo "</td></tr></table>";
3a506ca2 876}
877
34d52ad7 878
879
96192c44 880function quiz_print_quiz_questions($quiz, $results=NULL, $questions=NULL, $shuffleorder=NULL) {
a5e1f35c 881// Prints a whole quiz on one page.
882
34d52ad7 883 /// Get the questions
884
34d52ad7 885 if (!$questions) {
434802d5 886 if (empty($quiz->questions)) {
887 notify("No questions have been defined!");
888 return false;
889 }
890
34d52ad7 891 if (!$questions = get_records_list("quiz_questions", "id", $quiz->questions, "")) {
434802d5 892 notify("Error when reading questions from the database!");
34d52ad7 893 return false;
894 }
d288fa52 895 }
4b85b717 896
d288fa52 897 if (!$shuffleorder) {
898 if (!empty($quiz->shufflequestions)) { // Mix everything up
4b85b717 899 $questions = swapshuffle_assoc($questions);
ada728e2 900 } else {
d288fa52 901 $shuffleorder = explode(",", $quiz->questions); // Use originally defined order
4b85b717 902 }
34d52ad7 903 }
904
e1122620 905 if ($shuffleorder) { // Order has been defined, so reorder questions
906 $oldquestions = $questions;
907 $questions = array();
5a24a018 908 foreach ($shuffleorder as $key) {
909 if (empty($oldquestions[$key])) { // Check for recently added questions
910 if ($recentlyaddedquestion =
911 get_record("quiz_questions", "id", $key)) {
912 $recentlyaddedquestion->recentlyadded = true;
913 $questions[] = $recentlyaddedquestion;
914 }
915 } else {
916 $questions[] = $oldquestions[$key]; // This loses the index key, but doesn't matter
917 }
e1122620 918 }
919 }
920
434802d5 921 if (!$grades = get_records_list("quiz_question_grades", "question", $quiz->questions, "", "question,grade")) {
922 notify("No grades were found for these questions!");
923 return false;
924 }
925
34d52ad7 926
927 /// Examine the set of questions for random questions, and retrieve them
928
434802d5 929 if (empty($results)) { // Choose some new random questions
34d52ad7 930 if ($randomcats = quiz_get_random_categories($quiz->questions)) {
931 foreach ($randomcats as $randomcat => $randomdraw) {
932 /// Get the appropriate amount of random questions from this category
7bfa4fad 933 if (!$catquestions[$randomcat] = quiz_choose_random_questions($randomcat, $randomdraw, $quiz->questions)) {
434802d5 934 notify(get_string("toomanyrandom", "quiz", $randomcat));
34d52ad7 935 return false;
936 }
937 }
938 }
434802d5 939 } else { // Get the previously chosen questions
940 $chosen = array();
941 foreach ($questions as $question) {
942 if (isset($question->random)) {
943 $chosen[] = $question->random;
944 }
945 }
946 if ($chosen) {
947 $chosenlist = implode(",", $chosen);
948 if (!$chosen = get_records_list("quiz_questions", "id", $chosenlist, "")) {
949 notify("Error when reading questions from the database!");
950 return false;
951 }
952 }
a5e1f35c 953 }
954
9307692f 955 $strconfirmattempt = addslashes(get_string("readytosend", "quiz"));
956
45376a51 957 if (empty($quiz->grade)) {
958 $onsubmit = "";
959 } else {
960 $onsubmit = "onsubmit=\"return confirm('$strconfirmattempt');\"";
961 }
962
d7512435 963 echo "<form method=\"post\" action=\"attempt.php\" $onsubmit>\n";
964 echo "<input type=\"hidden\" name=\"q\" value=\"$quiz->id\" />\n";
a8a372cc 965
34d52ad7 966 $count = 0;
96192c44 967 $questionorder = array();
968
34d52ad7 969 foreach ($questions as $question) {
401c8de6 970
971 if ($question->qtype != DESCRIPTION) { // Description questions are not counted
972 $count++;
973 }
34d52ad7 974
e1122620 975 $questionorder[] = $question->id;
976
8db3eadd 977 $feedback = NULL;
978 $response = NULL;
979 $actualgrades = NULL;
980 $correct = NULL;
34d52ad7 981 $randomquestion = NULL;
982
983 if (empty($results)) {
984 if ($question->qtype == RANDOM ) { // Set up random questions
985 $randomquestion = $question;
986 $question = array_pop($catquestions[$randomquestion->category]);
987 $grades[$question->id]->grade = $grades[$randomquestion->id]->grade;
8e6c87cc 988 }
34d52ad7 989 } else {
990 if (!empty($results->feedback[$question->id])) {
991 $feedback = $results->feedback[$question->id];
8e6c87cc 992 }
34d52ad7 993 if (!empty($results->response[$question->id])) {
994 $response = $results->response[$question->id];
995 }
996 if (!empty($results->grades[$question->id])) {
997 $actualgrades = $results->grades[$question->id];
8e6c87cc 998 }
8db3eadd 999 if ($quiz->correctanswers) {
34d52ad7 1000 if (!empty($results->correct[$question->id])) {
1001 $correct = $results->correct[$question->id];
8e6c87cc 1002 }
8db3eadd 1003 }
34d52ad7 1004 if (!empty($question->random)) {
1005 $randomquestion = $question;
434802d5 1006 $question = $chosen[$question->random];
34d52ad7 1007 $grades[$question->id]->grade = $grades[$randomquestion->id]->grade;
1008 }
a8a372cc 1009 }
8db3eadd 1010
2408867e 1011
d7512435 1012 print_simple_box_start("center", "90%");
fe98ea90 1013 quiz_print_question($count, $question, $grades[$question->id]->grade, $quiz->id,
4b85b717 1014 $feedback, $response, $actualgrades, $correct,
081bf74f 1015 $randomquestion, $quiz->shuffleanswers, $quiz->grade);
a5e1f35c 1016 print_simple_box_end();
d7512435 1017 echo "<br />";
a5e1f35c 1018 }
a8a372cc 1019
586b2c82 1020 if (empty($results) || $results->attemptbuildsonthelast) {
96192c44 1021 if (!empty($quiz->shufflequestions)) { // Things have been mixed up, so pass the question order
1022 $shuffleorder = implode(',', $questionorder);
d7512435 1023 echo "<input type=\"hidden\" name=\"shuffleorder\" value=\"$shuffleorder\" />\n";
96192c44 1024 }
d7512435 1025 echo "<center>\n<input type=\"submit\" value=\"".get_string("savemyanswers", "quiz")."\" />\n</center>";
a8a372cc 1026 }
96192c44 1027 echo "</form>";
10b9291c 1028
1029 return true;
a5e1f35c 1030}
34d52ad7 1031
1032
6a952ce7 1033
1034function quiz_get_default_category($courseid) {
34d52ad7 1035/// Returns the current category
1036
6a952ce7 1037 if ($categories = get_records("quiz_categories", "course", $courseid, "id")) {
1038 foreach ($categories as $category) {
1039 return $category; // Return the first one (lowest id)
1040 }
1041 }
1042
1043 // Otherwise, we need to make one
10b9291c 1044 $category->name = get_string("default", "quiz");
1045 $category->info = get_string("defaultinfo", "quiz");
6a952ce7 1046 $category->course = $courseid;
1047 $category->publish = 0;
25b6ff9d 1048 $category->stamp = make_unique_id_code();
6a952ce7 1049
1050 if (!$category->id = insert_record("quiz_categories", $category)) {
1051 notify("Error creating a default category!");
1052 return false;
1053 }
1054 return $category;
1055}
1056
c74a0ca5 1057function quiz_get_category_menu($courseid, $published=false) {
da5fb074 1058/// Returns the list of categories
1059 $publish = "";
c74a0ca5 1060 if ($published) {
1061 $publish = "OR publish = '1'";
1062 }
bdc23be0 1063 return get_records_select_menu("quiz_categories", "course='$courseid' $publish", "name ASC", "id,name");
c74a0ca5 1064}
1065
6a952ce7 1066function quiz_print_category_form($course, $current) {
1067// Prints a form to choose categories
1068
facbf40f 1069 if (!$categories = get_records_select("quiz_categories", "course = '$course->id' OR publish = '1'", "name ASC")) {
6a952ce7 1070 if (!$category = quiz_get_default_category($course->id)) {
1071 notify("Error creating a default category!");
1072 return false;
1073 }
cb62c00a 1074 $categories[$category->id] = $category;
6a952ce7 1075 }
8d94f5a0 1076 foreach ($categories as $key => $category) {
facbf40f 1077 if ($catcourse = get_record("course", "id", $category->course)) {
1078 if ($category->publish) {
b55a466b 1079 $category->name .= " ($catcourse->shortname)";
8d94f5a0 1080 }
facbf40f 1081 $catmenu[$category->id] = $category->name;
8d94f5a0 1082 }
1083 }
6a952ce7 1084 $strcategory = get_string("category", "quiz");
1085 $strshow = get_string("show", "quiz");
6b069ece 1086 $streditcats = get_string("editcategories", "quiz");
6a952ce7 1087
2d1c22bb 1088 echo "<table width=\"100%\"><tr><td width=\"20\" nowrap=\"nowrap\">";
d7512435 1089 echo "<b>$strcategory:</b>&nbsp;";
2d1c22bb 1090 echo "</td><td>";
22592150 1091 popup_form ("edit.php?cat=", $catmenu, "catmenu", $current, "choose", "", "", false, "self");
d7512435 1092 echo "</td><td align=\"right\">";
1093 echo "<form method=\"get\" action=\"category.php\">";
1094 echo "<input type=\"hidden\" name=\"id\" value=\"$course->id\" />";
1095 echo "<input type=\"submit\" value=\"$streditcats\" />";
1096 echo "</form>";
1097 echo "</td></tr></table>";
6a952ce7 1098}
1099
1100
34d52ad7 1101
7bfa4fad 1102function quiz_choose_random_questions($category, $draws, $excluded=0) {
34d52ad7 1103/// Given a question category and a number of draws, this function
1104/// creates a random subset of that size - returned as an array of questions
1105
1106 if (!$pool = get_records_select_menu("quiz_questions",
575de48f 1107 "category = '$category' AND id NOT IN ($excluded)
1108 AND qtype <> ".RANDOM."
1109 AND qtype <> ".DESCRIPTION,
1110 "", "id,qtype")) {
34d52ad7 1111 return false;
1112 }
1113
1114 $countpool = count($pool);
1115
1116 if ($countpool == $draws) {
1117 $chosen = $pool;
1118 } else if ($countpool < $draws) {
1119 return false;
1120 } else {
1121 $chosen = draw_rand_array($pool, $draws);
1122 }
1123
1124 $chosenlist = implode(",", array_keys($chosen));
1125 return get_records_list("quiz_questions", "id", $chosenlist);
1126}
1127
1128
7bd1aa1d 1129function quiz_get_all_question_grades($questionlist, $quizid) {
1130// Given a list of question IDs, finds grades or invents them to
1131// create an array of matching grades
1132
5a25f84d 1133 if (empty($questionlist)) {
1134 return array();
1135 }
1136
bdc23be0 1137 $questions = quiz_get_question_grades($quizid, $questionlist);
7bd1aa1d 1138
1139 $list = explode(",", $questionlist);
1140 $grades = array();
1141
1142 foreach ($list as $qid) {
1143 if (isset($questions[$qid])) {
1144 $grades[$qid] = $questions[$qid]->grade;
1145 } else {
1146 $grades[$qid] = 1;
1147 }
1148 }
1149 return $grades;
1150}
1151
004c02e0 1152function quiz_gradesmenu_options($defaultgrade) {
1153// Especially for multianswer questions it is often
1154// desirable to have the grade of the question in a quiz
1155// larger than the earlier maximum of 10 points.
1156// This function makes quiz question list grade selector drop-down
1157// have the maximum grade option set to the highest value between 10
1158// and the defaultgrade of the question.
1159
1160 if ($defaultgrade && $defaultgrade>10) {
1161 $maxgrade = $defaultgrade;
1162 } else {
1163 $maxgrade = 10;
1164 }
1165
1166 unset($gradesmenu);
1167 for ($i=$maxgrade ; $i>=0 ; --$i) {
1168 $gradesmenu[$i] = $i;
1169 }
1170 return $gradesmenu;
1171}
7bd1aa1d 1172
1173function quiz_print_question_list($questionlist, $grades) {
6a952ce7 1174// Prints a list of quiz questions in a small layout form with knobs
7bd1aa1d 1175// $questionlist is comma-separated list
1176// $grades is an array of corresponding grades
6a952ce7 1177
1178 global $THEME;
1179
1180 if (!$questionlist) {
d7512435 1181 echo "<p align=\"center\">";
6a952ce7 1182 print_string("noquestions", "quiz");
d7512435 1183 echo "</p>";
6a952ce7 1184 return;
1185 }
1186
1187 $order = explode(",", $questionlist);
1188
7bd1aa1d 1189 if (!$questions = get_records_list("quiz_questions", "id", $questionlist)) {
d7512435 1190 echo "<p align=\"center\">";
9a652ddb 1191 print_string("noquestions", "quiz");
d7512435 1192 echo "</p>";
9a652ddb 1193 return;
1194
6a952ce7 1195 }
1196
1197 $strorder = get_string("order");
1198 $strquestionname = get_string("questionname", "quiz");
1199 $strgrade = get_string("grade");
1200 $strdelete = get_string("delete");
1201 $stredit = get_string("edit");
1202 $strmoveup = get_string("moveup");
1203 $strmovedown = get_string("movedown");
1204 $strsavegrades = get_string("savegrades", "quiz");
c74a0ca5 1205 $strtype = get_string("type", "quiz");
6a952ce7 1206
6a952ce7 1207 $count = 0;
1208 $sumgrade = 0;
1209 $total = count($order);
d7512435 1210 echo "<form method=\"post\" action=\"edit.php\">";
1211 echo "<table border=\"0\" cellpadding=\"5\" cellspacing=\"2\" width=\"100%\">\n";
1212 echo "<tr><th width=\"*\" colspan=\"3\" nowrap=\"nowrap\">$strorder</th><th align=\"left\" width=\"100%\" nowrap=\"nowrap\">$strquestionname</th><th width=\"*\" nowrap=\"nowrap\">$strtype</th><th width=\"*\" nowrap=\"nowrap\">$strgrade</th><th width=\"*\" nowrap=\"nowrap\">$stredit</th></tr>\n";
6a952ce7 1213 foreach ($order as $qnum) {
95dbc030 1214 if (empty($questions[$qnum])) {
1215 continue;
1216 }
bca64a12 1217 $question = $questions[$qnum];
6a952ce7 1218 $count++;
d7512435 1219 echo "<tr bgcolor=\"$THEME->cellcontent\">";
1220 echo "<td>$count</td>";
1221 echo "<td>";
6a952ce7 1222 if ($count != 1) {
d7512435 1223 echo "<a title=\"$strmoveup\" href=\"edit.php?up=$qnum\"><img
1224 src=\"../../pix/t/up.gif\" border=\"0\"></a>";
6a952ce7 1225 }
d7512435 1226 echo "</td>";
1227 echo "<td>";
6a952ce7 1228 if ($count != $total) {
d7512435 1229 echo "<a title=\"$strmovedown\" href=\"edit.php?down=$qnum\"><img
1230 src=\"../../pix/t/down.gif\" border=\"0\"></a>";
6a952ce7 1231 }
d7512435 1232 echo "</td>";
1233 echo "<td>$question->name</td>";
1234 echo "<td align=\"center\">";
bca64a12 1235 quiz_print_question_icon($question);
d7512435 1236 echo "</td>";
1237 echo "<td>";
bca64a12 1238 if ($question->qtype == DESCRIPTION) {
d7512435 1239 echo "<input type=\"hidden\" name=\"q$qnum\" value=\"0\" /> \n";
bca64a12 1240 } else {
004c02e0 1241 choose_from_menu(quiz_gradesmenu_options($question->defaultgrade),
1242 "q$qnum", (string)$grades[$qnum], "");
bca64a12 1243 }
d7512435 1244 echo "<td>";
1245 echo "<a title=\"$strdelete\" href=\"edit.php?delete=$qnum\"><img
1246 src=\"../../pix/t/delete.gif\" border=\"0\"></a>&nbsp;";
1247 echo "<a title=\"$stredit\" href=\"question.php?id=$qnum\"><img
1248 src=\"../../pix/t/edit.gif\" border=\"0\"></a>\n";
1249 echo "</td>";
6a952ce7 1250
7bd1aa1d 1251 $sumgrade += $grades[$qnum];
6a952ce7 1252 }
d7512435 1253 echo "<tr><td colspan=5 align=\"right\">\n";
1254 echo "<input type=\"submit\" value=\"$strsavegrades:\" />\n";
1255 echo "<input type=\"hidden\" name=\"setgrades\" value=\"save\" />\n";
1256 echo "<td align=\"left\" bgcolor=\"$THEME->cellcontent\">\n";
1257 echo "<b>$sumgrade</b>";
1258 echo "</td><td>\n</td></tr>\n";
1259 echo "</table>\n";
1260 echo "</form>\n";
10b9291c 1261
1262 return $sumgrade;
6a952ce7 1263}
1264
1265
1e085edc 1266function quiz_print_cat_question_list($categoryid, $quizselected=true) {
6a952ce7 1267// Prints a form to choose categories
1268
1269 global $THEME, $QUIZ_QUESTION_TYPE;
1270
10b9291c 1271 $strcategory = get_string("category", "quiz");
6a952ce7 1272 $strquestion = get_string("question", "quiz");
49220fa7 1273 $straddquestions = get_string("addquestions", "quiz");
1274 $strimportquestions = get_string("importquestions", "quiz");
6a952ce7 1275 $strnoquestions = get_string("noquestions", "quiz");
1276 $strselect = get_string("select", "quiz");
a01b2571 1277 $strselectall = get_string("selectall", "quiz");
6a952ce7 1278 $strcreatenewquestion = get_string("createnewquestion", "quiz");
1279 $strquestionname = get_string("questionname", "quiz");
1280 $strdelete = get_string("delete");
1281 $stredit = get_string("edit");
1282 $straddselectedtoquiz = get_string("addselectedtoquiz", "quiz");
c74a0ca5 1283 $strtype = get_string("type", "quiz");
c6eed097 1284 $strcreatemultiple = get_string("createmultiple", "quiz");
6a952ce7 1285
1286 if (!$categoryid) {
d7512435 1287 echo "<p align=\"center\"><b>";
6a952ce7 1288 print_string("selectcategoryabove", "quiz");
5325f8b8 1289 echo "</b></p>";
1e085edc 1290 if ($quizselected) {
1291 echo "<p>";
1292 print_string("addingquestions", "quiz");
1293 echo "</p>";
1294 }
6a952ce7 1295 return;
1296 }
a5e1f35c 1297
6a952ce7 1298 if (!$category = get_record("quiz_categories", "id", "$categoryid")) {
1299 notify("Category not found!");
1300 return;
1301 }
d7512435 1302 echo "<center>";
2d1c22bb 1303 echo format_text($category->info, FORMAT_MOODLE);
1304
1305 echo '<table><tr>';
1306 echo "<td valign=\"top\"><b>$strcreatenewquestion:</b></td>";
1307 echo '<td valign="top" align="right">';
1308 popup_form ("question.php?category=$category->id&qtype=", $QUIZ_QUESTION_TYPE, "addquestion",
22592150 1309 "", "choose", "", "", false, "self");
2d1c22bb 1310 echo '<td width="10" valign="top" align="right">';
cd63d77e 1311 helpbutton("questiontypes", $strcreatenewquestion, "quiz");
2d1c22bb 1312 echo '</td></tr>';
49220fa7 1313
2d1c22bb 1314 echo '<tr><td colspan="3" align="right">';
1315 echo '<form method="get" action="import.php">';
d7512435 1316 echo "<input type=\"hidden\" name=\"category\" value=\"$category->id\" />";
1317 echo "<input type=\"submit\" value=\"$strimportquestions\" />";
49220fa7 1318 helpbutton("import", $strimportquestions, "quiz");
2d1c22bb 1319 echo '</form>';
1320 echo '</td></tr>';
49220fa7 1321
2d1c22bb 1322 echo '<tr><td colspan="3" align="right">';
1323 echo '<form method="get" action="multiple.php">';
d7512435 1324 echo "<input type=\"hidden\" name=\"category\" value=\"$category->id\" />";
1325 echo "<input type=\"submit\" value=\"$strcreatemultiple\" />";
c6eed097 1326 helpbutton("createmultiple", $strcreatemultiple, "quiz");
2d1c22bb 1327 echo '</form>';
1328 echo '</td></tr>';
c6eed097 1329
2d1c22bb 1330 echo '</table>';
49220fa7 1331
2d1c22bb 1332 echo '</center>';
6a952ce7 1333
14bdb238 1334 if (!$questions = get_records("quiz_questions", "category", $category->id, "qtype ASC")) {
d7512435 1335 echo "<p align=\"center\">";
6a952ce7 1336 print_string("noquestions", "quiz");
d7512435 1337 echo "</p>";
6a952ce7 1338 return;
1339 }
1340
1e085edc 1341 $canedit = isteacheredit($category->course);
10b9291c 1342
d7512435 1343 echo "<form method=\"post\" action=\"edit.php\">";
1344 echo "<table border=\"0\" cellpadding=\"5\" cellspacing=\"2\" width=\"100%\">";
1345 echo "<tr>";
1e085edc 1346 if ($quizselected) {
d7512435 1347 echo "<th width=\"*\" nowrap=\"nowrap\">$strselect</th>";
1e085edc 1348 }
d7512435 1349 echo "<th width=\"100%\" align=\"left\" nowrap=\"nowrap\">$strquestionname</th><th width=\"*\" nowrap=\"nowrap\">$strtype</th>";
10b9291c 1350 if ($canedit) {
d7512435 1351 echo "<th width=\"*\" nowrap=\"nowrap\">$stredit</th>";
10b9291c 1352 }
d7512435 1353 echo "</tr>\n";
6a952ce7 1354 foreach ($questions as $question) {
d7512435 1355 echo "<tr bgcolor=\"$THEME->cellcontent\">\n";
1e085edc 1356 if ($quizselected) {
d7512435 1357 echo "<td align=\"center\">";
1358 echo "<input type=\"checkbox\" name=\"q$question->id\" value=\"1\" />\n";
1359 echo "</td>";
1e085edc 1360 }
d7512435 1361 echo "<td>".$question->name."</td>\n";
1362 echo "<td align=\"center\">\n";
c74a0ca5 1363 quiz_print_question_icon($question);
d7512435 1364 echo "</td>\n";
10b9291c 1365 if ($canedit) {
d7512435 1366 echo "<td>\n";
1367 echo "<a title=\"$strdelete\" href=\"question.php?id=$question->id&delete=$question->id\">\n<img
1368 src=\"../../pix/t/delete.gif\" border=0></a>&nbsp;";
1369 echo "<a title=\"$stredit\" href=\"question.php?id=$question->id\"><img
1370 src=\"../../pix/t/edit.gif\" border=0></a>";
1371 echo "</td>\n";// deleted </tr> jm
10b9291c 1372 }
d7512435 1373 echo "</tr>\n";
6a952ce7 1374 }
1e085edc 1375 if ($quizselected) {
d7512435 1376 echo "<tr>\n<td colspan=\"3\">";
1377 echo "<input type=\"submit\" name=\"add\" value=\"<< $straddselectedtoquiz\" />\n";
1378 //echo "<input type=submit name=\"delete\" value=\"XX Delete selected\">";
89b8d337 1379 echo "<input type=\"button\" onclick=\"checkall()\" value=\"$strselectall\" />\n";
d7512435 1380 echo "</td></tr>";
1381 }
1382 echo "</table>\n";
1383 echo "</form>\n";
6a952ce7 1384}
a5e1f35c 1385
3a506ca2 1386
958aafe2 1387function quiz_start_attempt($quizid, $userid, $numattempt) {
1388 $attempt->quiz = $quizid;
ebc3bd2b 1389 $attempt->userid = $userid;
958aafe2 1390 $attempt->attempt = $numattempt;
1391 $attempt->timestart = time();
1392 $attempt->timefinish = 0;
1393 $attempt->timemodified = time();
1394
1395 return insert_record("quiz_attempts", $attempt);
1396}
1397
1398function quiz_get_user_attempt_unfinished($quizid, $userid) {
1399// Returns an object containing an unfinished attempt (if there is one)
ebc3bd2b 1400 return get_record("quiz_attempts", "quiz", $quizid, "userid", $userid, "timefinish", 0);
958aafe2 1401}
1402
3a506ca2 1403function quiz_get_user_attempts($quizid, $userid) {
a5e1f35c 1404// Returns a list of all attempts by a user
ebc3bd2b 1405 return get_records_select("quiz_attempts", "quiz = '$quizid' AND userid = '$userid' AND timefinish > 0",
bdc23be0 1406 "attempt ASC");
3a506ca2 1407}
1408
8d94f5a0 1409
1410function quiz_get_user_attempts_string($quiz, $attempts, $bestgrade) {
1411/// Returns a simple little comma-separated list of all attempts,
6d86b5dc 1412/// with each grade linked to the feedback report and with the best grade highlighted
8d94f5a0 1413
1414 $bestgrade = format_float($bestgrade);
1415 foreach ($attempts as $attempt) {
1416 $attemptgrade = format_float(($attempt->sumgrades / $quiz->sumgrades) * $quiz->grade);
1417 if ($attemptgrade == $bestgrade) {
d7512435 1418 $userattempts[] = "<span class=\"highlight\"><a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</a></span>";
8d94f5a0 1419 } else {
29d5d0b4 1420 $userattempts[] = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</a>";
8d94f5a0 1421 }
1422 }
1423 return implode(",", $userattempts);
1424}
1425
a5e1f35c 1426function quiz_get_best_grade($quizid, $userid) {
1427/// Get the best current grade for a particular user in a quiz
ebc3bd2b 1428 if (!$grade = get_record("quiz_grades", "quiz", $quizid, "userid", $userid)) {
b0be6c79 1429 return "";
3a506ca2 1430 }
1431
2383cadb 1432 return (round($grade->grade,0));
3a506ca2 1433}
1434
e331eb06 1435function quiz_save_best_grade($quiz, $userid) {
a5e1f35c 1436/// Calculates the best grade out of all attempts at a quiz for a user,
1437/// and then saves that grade in the quiz_grades table.
1438
e331eb06 1439 if (!$attempts = quiz_get_user_attempts($quiz->id, $userid)) {
e909c8d0 1440 notify("Could not find any user attempts");
a5e1f35c 1441 return false;
1442 }
1443
1444 $bestgrade = quiz_calculate_best_grade($quiz, $attempts);
1445 $bestgrade = (($bestgrade / $quiz->sumgrades) * $quiz->grade);
1446
ebc3bd2b 1447 if ($grade = get_record("quiz_grades", "quiz", $quiz->id, "userid", $userid)) {
1d2603b1 1448 $grade->grade = round($bestgrade, 2);
a5e1f35c 1449 $grade->timemodified = time();
1450 if (!update_record("quiz_grades", $grade)) {
e909c8d0 1451 notify("Could not update best grade");
a5e1f35c 1452 return false;
1453 }
1454 } else {
1455 $grade->quiz = $quiz->id;
ebc3bd2b 1456 $grade->userid = $userid;
38f03e5a 1457 $grade->grade = round($bestgrade, 2);
a5e1f35c 1458 $grade->timemodified = time();
1459 if (!insert_record("quiz_grades", $grade)) {
e909c8d0 1460 notify("Could not insert new best grade");
a5e1f35c 1461 return false;
1462 }
1463 }
1464 return true;
1465}
1466
1467
3a506ca2 1468function quiz_calculate_best_grade($quiz, $attempts) {
a5e1f35c 1469/// Calculate the best grade for a quiz given a number of attempts by a particular user.
3a506ca2 1470
1471 switch ($quiz->grademethod) {
a5e1f35c 1472
1473 case ATTEMPTFIRST:
3a506ca2 1474 foreach ($attempts as $attempt) {
a5e1f35c 1475 return $attempt->sumgrades;
3a506ca2 1476 }
a5e1f35c 1477 break;
1478
1479 case ATTEMPTLAST:
1480 foreach ($attempts as $attempt) {
1481 $final = $attempt->sumgrades;
1482 }
1483 return $final;
3a506ca2 1484
a5e1f35c 1485 case GRADEAVERAGE:
3a506ca2 1486 $sum = 0;
1487 $count = 0;
1488 foreach ($attempts as $attempt) {
a5e1f35c 1489 $sum += $attempt->sumgrades;
3a506ca2 1490 $count++;
1491 }
1492 return (float)$sum/$count;
1493
3a506ca2 1494 default:
a5e1f35c 1495 case GRADEHIGHEST:
1496 $max = 0;
3a506ca2 1497 foreach ($attempts as $attempt) {
a5e1f35c 1498 if ($attempt->sumgrades > $max) {
1499 $max = $attempt->sumgrades;
1500 }
3a506ca2 1501 }
a5e1f35c 1502 return $max;
1503 }
1504}
1505
34d52ad7 1506
40b1a221 1507function quiz_calculate_best_attempt($quiz, $attempts) {
1508/// Return the attempt with the best grade for a quiz
1509
1510 switch ($quiz->grademethod) {
1511
1512 case ATTEMPTFIRST:
1513 foreach ($attempts as $attempt) {
1514 return $attempt;
1515 }
1516 break;
1517
1518 case GRADEAVERAGE: // need to do something with it :-)
1519 case ATTEMPTLAST:
1520 foreach ($attempts as $attempt) {
1521 $final = $attempt;
1522 }
1523 return $final;
1524
1525 default:
1526 case GRADEHIGHEST:
1527 $max = -1;
1528 foreach ($attempts as $attempt) {
1529 if ($attempt->sumgrades > $max) {
1530 $max = $attempt->sumgrades;
1531 $maxattempt = $attempt;
1532 }
1533 }
1534 return $maxattempt;
1535 }
1536}
1537
1538
a5e1f35c 1539function quiz_save_attempt($quiz, $questions, $result, $attemptnum) {
1540/// Given a quiz, a list of attempted questions and a total grade
1541/// this function saves EVERYTHING so it can be reconstructed later
1542/// if necessary.
1543
1544 global $USER;
1545
958aafe2 1546 // First find the attempt in the database (start of attempt)
1547
1548 if (!$attempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
1549 notify("Trying to save an attempt that was not started!");
1550 return false;
1551 }
1552
1553 if ($attempt->attempt != $attemptnum) { // Double check.
1554 notify("Number of this attempt is different to the unfinished one!");
1555 return false;
1556 }
1557
1558 // Now let's complete this record and save it
a5e1f35c 1559
a5e1f35c 1560 $attempt->sumgrades = $result->sumgrades;
958aafe2 1561 $attempt->timefinish = time();
a5e1f35c 1562 $attempt->timemodified = time();
1563
958aafe2 1564 if (! update_record("quiz_attempts", $attempt)) {
7520988b 1565 notify("Error while saving attempt");
a5e1f35c 1566 return false;
1567 }
1568
1569 // Now let's save all the questions for this attempt
1570
1571 foreach ($questions as $question) {
1572 $response->attempt = $attempt->id;
1573 $response->question = $question->id;
1574 $response->grade = $result->grades[$question->id];
34d52ad7 1575
77cff589 1576 if (!empty($question->random)) {
34d52ad7 1577 // First save the response of the random question
1578 // the answer is the id of the REAL response
1579 $response->answer = $question->random;
1580 if (!insert_record("quiz_responses", $response)) {
1581 notify("Error while saving response");
1582 return false;
1583 }
1584 $response->question = $question->random;
1585 }
1586
54d0590b 1587 if (!empty($question->answer)) {
a5e1f35c 1588 $response->answer = implode(",",$question->answer);
1589 } else {
1590 $response->answer = "";
1591 }
1592 if (!insert_record("quiz_responses", $response)) {
7520988b 1593 notify("Error while saving response");
a5e1f35c 1594 return false;
1595 }
3a506ca2 1596 }
a5e1f35c 1597 return true;
3a506ca2 1598}
730fd187 1599
7c9c2a8d 1600function quiz_grade_attempt_question_result($question,
1601 $answers,
1602 $gradecanbenegative= false)
1603{
44fc346f 1604 $grade = 0; // default
1605 $correct = array();
1606 $feedback = array();
1607 $response = array();
1608
1609 switch ($question->qtype) {
1610 case SHORTANSWER:
1611 if ($question->answer) {
1612 $question->answer = trim(stripslashes($question->answer[0]));
1613 } else {
1614 $question->answer = "";
1615 }
1616 $response[0] = $question->answer;
5276dc48 1617 $feedback[0] = ''; // Default
44fc346f 1618 foreach ($answers as $answer) { // There might be multiple right answers
d7512435 1619
1620 $answer->answer = trim($answer->answer); // Just in case
1621
5276dc48 1622 if ($answer->fraction >= 1.0) {
1623 $correct[] = $answer->answer;
44fc346f 1624 }
1625 if (!$answer->usecase) { // Don't compare case
1626 $answer->answer = strtolower($answer->answer);
1627 $question->answer = strtolower($question->answer);
1628 }
0ce4aa1a 1629
5276dc48 1630 $potentialgrade = (float)$answer->fraction * $question->grade;
1631
1632 if ($potentialgrade >= $grade
1633 and (strpos(' '.$answer->answer, '*'))) {
0ce4aa1a 1634 $answer->answer = eregi_replace('\*','.*',$answer->answer);
1635 if (eregi('^'.$answer->answer.'$', $question->answer)) {
1636 $feedback[0] = $answer->feedback;
5276dc48 1637 $grade = $potentialgrade;
0ce4aa1a 1638 }
5276dc48 1639
1640 } else if ($answer->answer == $question->answer) {
1641 $feedback[0] = $answer->feedback;
1642 $grade = $potentialgrade;
44fc346f 1643 }
1644 }
1645 break;
1646
1647 case NUMERICAL:
1648 if ($question->answer) {
1649 $question->answer = trim(stripslashes($question->answer[0]));
1650 } else {
1651 $question->answer = "";
1652 }
1653 $response[0] = $question->answer;
1654 $bestshortanswer = 0;
1655 foreach ($answers as $answer) { // There might be multiple right answers
1656 if ($answer->fraction > $bestshortanswer) {
1657 $correct[$answer->id] = $answer->answer;
1658 $bestshortanswer = $answer->fraction;
16a19172 1659 $feedback[0] = $answer->feedback; // Show feedback for best answer
44fc346f 1660 }
bdb63d64 1661 if ('' != $question->answer // Must not be mixed up with zero!
1662 && (float)$answer->fraction > (float)$grade // Do we need to bother?
1663 and // and has lower procedence than && and ||.
1664 strtolower($question->answer) == strtolower($answer->answer)
1665 || '' != trim($answer->min)
1666 && ((float)$question->answer >= (float)$answer->min)
1667 && ((float)$question->answer <= (float)$answer->max))
1668 {
16a19172 1669 //$feedback[0] = $answer->feedback; No feedback was shown for wrong answers
bdb63d64 1670 $grade = (float)$answer->fraction;
44fc346f 1671 }
1672 }
bdb63d64 1673 $grade *= $question->grade; // Normalize to correct weight
44fc346f 1674 break;
1675
1676 case TRUEFALSE:
1677 if ($question->answer) {
1678 $question->answer = $question->answer[0];
1679 } else {
1680 $question->answer = NULL;
1681 }
1682 foreach($answers as $answer) { // There should be two answers (true and false)
1683 $feedback[$answer->id] = $answer->feedback;
1684 if ($answer->fraction > 0) {
1685 $correct[$answer->id] = true;
1686 }
1687 if ($question->answer == $answer->id) {
1688 $grade = (float)$answer->fraction * $question->grade;
1689 $response[$answer->id] = true;
1690 }
1691 }
1692 break;
1693
1694
1695 case MULTICHOICE:
1696 foreach($answers as $answer) { // There will be multiple answers, perhaps more than one is right
1697 $feedback[$answer->id] = $answer->feedback;
1698 if ($answer->fraction > 0) {
1699 $correct[$answer->id] = true;
1700 }
1701 if (!empty($question->answer)) {
1702 foreach ($question->answer as $questionanswer) {
1703 if ($questionanswer == $answer->id) {
1704 $response[$answer->id] = true;
1705 if ($answer->single) {
1706 $grade = (float)$answer->fraction * $question->grade;
1707 continue;
1708 } else {
1709 $grade += (float)$answer->fraction * $question->grade;
1710 }
1711 }
1712 }
1713 }
1714 }
1715 break;
1716
1717 case MATCH:
1718 $matchcount = $totalcount = 0;
1719
1720 foreach ($question->answer as $questionanswer) { // Each answer is "subquestionid-answerid"
1721 $totalcount++;
1722 $qarr = explode('-', $questionanswer); // Extract subquestion/answer.
1723 $subquestionid = $qarr[0];
1724 $subanswerid = $qarr[1];
1725 if ($subquestionid and $subanswerid and (($subquestionid == $subanswerid) or
1726 ($answers[$subquestionid]->answertext == $answers[$subanswerid]->answertext))) {
1727 // Either the ids match exactly, or the answertexts match exactly
1728 // (in case two subquestions had the same answer)
1729 $matchcount++;
1730 $correct[$subquestionid] = true;
1731 } else {
1732 $correct[$subquestionid] = false;
1733 }
1734 $response[$subquestionid] = $subanswerid;
1735 }
1736
1737 $grade = $question->grade * $matchcount / $totalcount;
1738
1739 break;
1740
1741 case RANDOMSAMATCH:
1742 $bestanswer = array();
1743 foreach ($answers as $answer) { // Loop through them all looking for correct answers
1744 if (empty($bestanswer[$answer->question])) {
1745 $bestanswer[$answer->question] = 0;
1746 $correct[$answer->question] = "";
1747 }
1748 if ($answer->fraction > $bestanswer[$answer->question]) {
1749 $bestanswer[$answer->question] = $answer->fraction;
1750 $correct[$answer->question] = $answer->answer;
1751 }
1752 }
1753 $answerfraction = 1.0 / (float) count($question->answer);
1754 foreach ($question->answer as $questionanswer) { // For each random answered question
1755 $rqarr = explode('-', $questionanswer); // Extract question/answer.
1756 $rquestion = $rqarr[0];
1757 $ranswer = $rqarr[1];
1758 $response[$rquestion] = $questionanswer;
1759 if (isset($answers[$ranswer])) { // If the answer exists in the list
1760 $answer = $answers[$ranswer];
1761 $feedback[$rquestion] = $answer->feedback;
1762 if ($answer->question == $rquestion) { // Check that this answer matches the question
1763 $grade += (float)$answer->fraction * $question->grade * $answerfraction;
1764 }
1765 }
1766 }
1767 break;
1768
8b439f8c 1769 case MULTIANSWER:
1770 // Default setting that avoids a possible divide by zero:
1771 $subquestion->grade = 1.0;
1772
1773 foreach ($question->answer as $questionanswer) {
1774
1775 // Resetting default values for subresult:
1776 $subresult->grade = 0.0;
1777 $subresult->correct = array();
1778 $subresult->feedback = array();
1779
1780 // Resetting subquestion responses:
1781 $subquestion->answer = array();
1782
1783 $qarr = explode('-', $questionanswer, 2);
1784 $subquestion->answer[] = $qarr[1]; // Always single answer for subquestions
1785 foreach ($answers as $multianswer) {
1786 if ($multianswer->id == $qarr[0]) {
1787 $subquestion->qtype = $multianswer->answertype;
1788 $subquestion->grade = $multianswer->norm;
1789 $subresult = quiz_grade_attempt_question_result
7c9c2a8d 1790 ($subquestion, $multianswer->subanswers, true);
8b439f8c 1791 break;
1792 }
1793 }
1794
1795 // Summarize subquestion results:
1796 $grade += $subresult->grade;
1797 $feedback[] = $subresult->feedback[0];
1798 $correct[] = $subresult->correct[0];
1799
1800 // Each response instance also contains the partial
1801 // fraction grade for the response:
1802 $response[] = $subresult->grade/$subquestion->grade
1803 . '-' . $subquestion->answer[0];
1804 }
1805 // Normalize grade:
1806 $grade *= $question->grade/($question->defaultgrade);
1807 break;
1808
44fc346f 1809 case DESCRIPTION: // Descriptions are not graded.
1810 break;
1811
1812 case RANDOM: // Returns a recursive call with the real question
1813 $realquestion = get_record
1814 ('quiz_questions', 'id', $question->random);
1815 $realquestion->answer = $question->answer;
1816 $realquestion->grade = $question->grade;
1817 return quiz_grade_attempt_question_result($realquestion, $answers);
1818 }
1819
7c9c2a8d 1820 $result->grade =
1821 $gradecanbenegative ? $grade // Grade can be negative
1822 : max(0.0, $grade); // Grade must not be negative
44fc346f 1823 $result->correct = $correct;
1824 $result->feedback = $feedback;
1825 $result->response = $response;
1826 return $result;
1827}
a5e1f35c 1828
1829function quiz_grade_attempt_results($quiz, $questions) {
1830/// Given a list of questions (including answers for each one)
1831/// this function does all the hard work of calculating the
1832/// grades for each question, as well as a total grade for
1833/// for the whole quiz. It returns everything in a structure
1834/// that looks like:
1835/// $result->sumgrades (sum of all grades for all questions)
1836/// $result->percentage (Percentage of grades that were correct)
1837/// $result->grade (final grade result for the whole quiz)
1838/// $result->grades[] (array of grades, indexed by question id)
a8a372cc 1839/// $result->response[] (array of response arrays, indexed by question id)
a5e1f35c 1840/// $result->feedback[] (array of feedback arrays, indexed by question id)
8db3eadd 1841/// $result->correct[] (array of feedback arrays, indexed by question id)
a5e1f35c 1842
1843 if (!$questions) {
1844 error("No questions!");
1845 }
34d52ad7 1846
1847 if (!$grades = get_records_menu("quiz_question_grades", "quiz", $quiz->id, "", "question,grade")) {
1848 error("No grades defined for these quiz questions!");
1849 }
1850
a5e1f35c 1851 $result->sumgrades = 0;
1852
1853 foreach ($questions as $question) {
34d52ad7 1854
44fc346f 1855 $question->grade = $grades[$question->id];
34d52ad7 1856
44fc346f 1857 if (!$answers = quiz_get_answers($question)) {
1858 error("No answers defined for question id $question->id!");
a5e1f35c 1859 }
34d52ad7 1860
44fc346f 1861 $questionresult = quiz_grade_attempt_question_result($question,
1862 $answers);
10b9291c 1863
44fc346f 1864 $result->grades[$question->id] = round($questionresult->grade, 2);
1865 $result->sumgrades += $questionresult->grade;
1866 $result->feedback[$question->id] = $questionresult->feedback;
1867 $result->response[$question->id] = $questionresult->response;
1868 $result->correct[$question->id] = $questionresult->correct;
a5e1f35c 1869 }
1870
8d94f5a0 1871 $fraction = (float)($result->sumgrades / $quiz->sumgrades);
1872 $result->percentage = format_float($fraction * 100.0);
1873 $result->grade = format_float($fraction * $quiz->grade);
3a50203f 1874 $result->sumgrades = round($result->sumgrades, 2);
a5e1f35c 1875
1876 return $result;
1877}
6d86b5dc 1878
1879
49220fa7 1880function quiz_save_question_options($question) {
1881/// Given some question info and some data about the the answers
1882/// this function parses, organises and saves the question
1883/// It is used by question.php when saving new data from a
1884/// form, and also by import.php when importing questions
77cff589 1885///
1886/// If this is an update, and old answers already exist, then
1887/// these are overwritten using an update(). To do this, it
1888/// it is assumed that the IDs in quiz_answers are in the same
1889/// sort order as the new answers being saved. This should always
1890/// be true, but it's something to keep in mind if fiddling with
1891/// question.php
49220fa7 1892///
1893/// Returns $result->error or $result->notice
a5e1f35c 1894
49220fa7 1895 switch ($question->qtype) {
1896 case SHORTANSWER:
77cff589 1897
e1122620 1898 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
1899 $oldanswers = array();
1900 }
49220fa7 1901
1902 $answers = array();
1903 $maxfraction = -1;
1904
1905 // Insert all the new answers
1906 foreach ($question->answer as $key => $dataanswer) {
1907 if ($dataanswer != "") {
77cff589 1908 if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
1909 $answer = $oldanswer;
d7512435 1910 $answer->answer = trim($dataanswer);
77cff589 1911 $answer->fraction = $question->fraction[$key];
1912 $answer->feedback = $question->feedback[$key];
1913 if (!update_record("quiz_answers", $answer)) {
1914 $result->error = "Could not update quiz answer! (id=$answer->id)";
1915 return $result;
1916 }
1917 } else { // This is a completely new answer
1918 unset($answer);
d7512435 1919 $answer->answer = trim($dataanswer);
77cff589 1920 $answer->question = $question->id;
1921 $answer->fraction = $question->fraction[$key];
1922 $answer->feedback = $question->feedback[$key];
1923 if (!$answer->id = insert_record("quiz_answers", $answer)) {
1924 $result->error = "Could not insert quiz answer!";
1925 return $result;
1926 }
49220fa7 1927 }
1928 $answers[] = $answer->id;
1929 if ($question->fraction[$key] > $maxfraction) {
1930 $maxfraction = $question->fraction[$key];
1931 }
1932 }
1933 }
1934
77cff589 1935 if ($options = get_record("quiz_shortanswer", "question", $question->id)) {
1936 $options->answers = implode(",",$answers);
1937 $options->usecase = $question->usecase;
1938 if (!update_record("quiz_shortanswer", $options)) {
1939 $result->error = "Could not update quiz shortanswer options! (id=$options->id)";
1940 return $result;
1941 }
1942 } else {
1943 unset($options);
1944 $options->question = $question->id;
1945 $options->answers = implode(",",$answers);
1946 $options->usecase = $question->usecase;
1947 if (!insert_record("quiz_shortanswer", $options)) {
1948 $result->error = "Could not insert quiz shortanswer options!";
1949 return $result;
1950 }
49220fa7 1951 }
1952
1953 /// Perform sanity checks on fractional grades
1954 if ($maxfraction != 1) {
1955 $maxfraction = $maxfraction * 100;
1956 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
1957 return $result;
1958 }
1959 break;
77cff589 1960
361f649d 1961 case NUMERICAL: // Note similarities to SHORTANSWER
1962
1963 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
1964 $oldanswers = array();
1965 }
1966
1967 $answers = array();
1968 $maxfraction = -1;
1969
1970 // Insert all the new answers
1971 foreach ($question->answer as $key => $dataanswer) {
1972 if ($dataanswer != "") {
1973 if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
1974 $answer = $oldanswer;
1975 $answer->answer = $dataanswer;
1976 $answer->fraction = $question->fraction[$key];
1977 $answer->feedback = $question->feedback[$key];
1978 if (!update_record("quiz_answers", $answer)) {
1979 $result->error = "Could not update quiz answer! (id=$answer->id)";
1980 return $result;
1981 }
1982 } else { // This is a completely new answer
1983 unset($answer);
1984 $answer->answer = $dataanswer;
1985 $answer->question = $question->id;
1986 $answer->fraction = $question->fraction[$key];
1987 $answer->feedback = $question->feedback[$key];
1988 if (!$answer->id = insert_record("quiz_answers", $answer)) {
1989 $result->error = "Could not insert quiz answer!";
1990 return $result;
1991 }
1992 }
1993 $answers[] = $answer->id;
1994 if ($question->fraction[$key] > $maxfraction) {
1995 $maxfraction = $question->fraction[$key];
1996 }
1997
1998 if ($options = get_record("quiz_numerical", "answer", $answer->id)) {
1999 $options->min= $question->min[$key];
2000 $options->max= $question->max[$key];
2001 if (!update_record("quiz_numerical", $options)) {
2002 $result->error = "Could not update quiz numerical options! (id=$options->id)";
2003 return $result;
2004 }
2005 } else { // completely new answer
2006 unset($options);
9c026610 2007 $options->question = $question->id;
2008 $options->answer = $answer->id;
2009 $options->min = $question->min[$key];
2010 $options->max = $question->max[$key];
361f649d 2011 if (!insert_record("quiz_numerical", $options)) {
2012 $result->error = "Could not insert quiz numerical options!";
2013 return $result;
2014 }
2015 }
2016 }
2017 }
2018
2019 /// Perform sanity checks on fractional grades
2020 if ($maxfraction != 1) {
2021 $maxfraction = $maxfraction * 100;
2022 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
2023 return $result;
2024 }
2025 break;
77cff589 2026
2027
49220fa7 2028 case TRUEFALSE:
77cff589 2029
e1122620 2030 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
2031 $oldanswers = array();
2032 }
77cff589 2033
2034 if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it
4d01ada3 2035 $true->answer = get_string("true", "quiz");
77cff589 2036 $true->fraction = $question->answer;
2037 $true->feedback = $question->feedbacktrue;
2038 if (!update_record("quiz_answers", $true)) {
2039 $result->error = "Could not update quiz answer \"true\")!";
2040 return $result;
2041 }
2042 } else {
2043 unset($true);
4d01ada3 2044 $true->answer = get_string("true", "quiz");
77cff589 2045 $true->question = $question->id;
2046 $true->fraction = $question->answer;
2047 $true->feedback = $question->feedbacktrue;
2048 if (!$true->id = insert_record("quiz_answers", $true)) {
2049 $result->error = "Could not insert quiz answer \"true\")!";
2050 return $result;
2051 }
49220fa7 2052 }
2053
77cff589 2054 if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it
4d01ada3 2055 $false->answer = get_string("false", "quiz");
77cff589 2056 $false->fraction = 1 - (int)$question->answer;
2057 $false->feedback = $question->feedbackfalse;
2058 if (!update_record("quiz_answers", $false)) {
2059 $result->error = "Could not insert quiz answer \"false\")!";
2060 return $result;
2061 }
2062 } else {
2063 unset($false);
4d01ada3 2064 $false->answer = get_string("false", "quiz");
77cff589 2065 $false->question = $question->id;
2066 $false->fraction = 1 - (int)$question->answer;
2067 $false->feedback = $question->feedbackfalse;
2068 if (!$false->id = insert_record("quiz_answers", $false)) {
2069 $result->error = "Could not insert quiz answer \"false\")!";
2070 return $result;
2071 }
49220fa7 2072 }
2073
77cff589 2074 if ($options = get_record("quiz_truefalse", "question", $question->id)) {
2075 // No need to do anything, since the answer IDs won't have changed
2076 // But we'll do it anyway, just for robustness
2077 $options->trueanswer = $true->id;
2078 $options->falseanswer = $false->id;
2079 if (!update_record("quiz_truefalse", $options)) {
2080 $result->error = "Could not update quiz truefalse options! (id=$options->id)";
2081 return $result;
2082 }
2083 } else {
2084 unset($options);
2085 $options->question = $question->id;
2086 $options->trueanswer = $true->id;
2087 $options->falseanswer = $false->id;
2088 if (!insert_record("quiz_truefalse", $options)) {
2089 $result->error = "Could not insert quiz truefalse options!";
2090 return $result;
2091 }
49220fa7 2092 }
2093 break;
77cff589 2094
2095
49220fa7 2096 case MULTICHOICE:
77cff589 2097
e1122620 2098 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
2099 $oldanswers = array();
2100 }
49220fa7 2101
2102 $totalfraction = 0;
2103 $maxfraction = -1;
2104
2105 $answers = array();
2106
2107 // Insert all the new answers
2108 foreach ($question->answer as $key => $dataanswer) {
2109 if ($dataanswer != "") {
77cff589 2110 if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it
2111 $answer->answer = $dataanswer;
2112 $answer->fraction = $question->fraction[$key];
2113 $answer->feedback = $question->feedback[$key];
2114 if (!update_record("quiz_answers", $answer)) {
2115 $result->error = "Could not update quiz answer! (id=$answer->id)";
2116 return $result;
2117 }
2118 } else {
2119 unset($answer);
2120 $answer->answer = $dataanswer;
2121 $answer->question = $question->id;
2122 $answer->fraction = $question->fraction[$key];
2123 $answer->feedback = $question->feedback[$key];
2124 if (!$answer->id = insert_record("quiz_answers", $answer)) {
2125 $result->error = "Could not insert quiz answer! ";
2126 return $result;
2127 }
49220fa7 2128 }
2129 $answers[] = $answer->id;
2130
2131 if ($question->fraction[$key] > 0) { // Sanity checks
2132 $totalfraction += $question->fraction[$key];
2133 }
2134 if ($question->fraction[$key] > $maxfraction) {
2135 $maxfraction = $question->fraction[$key];
2136 }
2137 }
2138 }
2139
77cff589 2140 if ($options = get_record("quiz_multichoice", "question", $question->id)) {
2141 $options->answers = implode(",",$answers);
2142 $options->single = $question->single;
2143 if (!update_record("quiz_multichoice", $options)) {
2144 $result->error = "Could not update quiz multichoice options! (id=$options->id)";
2145 return $result;
2146 }
2147 } else {
2148 unset($options);
2149 $options->question = $question->id;
2150 $options->answers = implode(",",$answers);
2151 $options->single = $question->single;
2152 if (!insert_record("quiz_multichoice", $options)) {
2153 $result->error = "Could not insert quiz multichoice options!";
2154 return $result;
2155 }
49220fa7 2156 }
2157
2158 /// Perform sanity checks on fractional grades
2159 if ($options->single) {
2160 if ($maxfraction != 1) {
2161 $maxfraction = $maxfraction * 100;
2162 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
2163 return $result;
2164 }
2165 } else {
2166 $totalfraction = round($totalfraction,2);
2167 if ($totalfraction != 1) {
2168 $totalfraction = $totalfraction * 100;
2169 $result->notice = get_string("fractionsaddwrong", "quiz", $totalfraction);
2170 return $result;
2171 }
2172 }
2173 break;
95dbc030 2174
54a67a59 2175 case MATCH:
77cff589 2176
e1122620 2177 if (!$oldsubquestions = get_records("quiz_match_sub", "question", $question->id, "id ASC")) {
2178 $oldsubquestions = array();
2179 }
54a67a59 2180
2181 $subquestions = array();
2182
2183 // Insert all the new question+answer pairs
2184 foreach ($question->subquestions as $key => $questiontext) {
2185 $answertext = $question->subanswers[$key];
2186 if (!empty($questiontext) and !empty($answertext)) {
77cff589 2187 if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it
2188 $subquestion->questiontext = $questiontext;
2189 $subquestion->answertext = $answertext;
2190 if (!update_record("quiz_match_sub", $subquestion)) {
2191 $result->error = "Could not insert quiz match subquestion! (id=$subquestion->id)";
2192 return $result;
2193 }
2194 } else {
2195 unset($subquestion);
2196 $subquestion->question = $question->id;
2197 $subquestion->questiontext = $questiontext;
2198 $subquestion->answertext = $answertext;
2199 if (!$subquestion->id = insert_record("quiz_match_sub", $subquestion)) {
2200 $result->error = "Could not insert quiz match subquestion!";
2201 return $result;
2202 }
54a67a59 2203 }
2204 $subquestions[] = $subquestion->id;
2205 }
2206 }
2207
2208 if (count($subquestions) < 3) {
2209 $result->notice = get_string("notenoughsubquestions", "quiz");
2210 return $result;
2211 }
2212
77cff589 2213 if ($options = get_record("quiz_match", "question", $question->id)) {
2214 $options->subquestions = implode(",",$subquestions);
2215 if (!update_record("quiz_match", $options)) {
2216 $result->error = "Could not update quiz match options! (id=$options->id)";
2217 return $result;
2218 }
2219 } else {
2220 unset($options);
2221 $options->question = $question->id;
2222 $options->subquestions = implode(",",$subquestions);
2223 if (!insert_record("quiz_match", $options)) {
2224 $result->error = "Could not insert quiz match options!";
2225 return $result;
2226 }
54a67a59 2227 }
2228
2229 break;
2230
77cff589 2231
54a67a59 2232 case RANDOMSAMATCH:
95dbc030 2233 $options->question = $question->id;
2234 $options->choose = $question->choose;
54a67a59 2235 if ($existing = get_record("quiz_randomsamatch", "question", $options->question)) {
95dbc030 2236 $options->id = $existing->id;
54a67a59 2237 if (!update_record("quiz_randomsamatch", $options)) {
2238 $result->error = "Could not update quiz randomsamatch options!";
95dbc030 2239 return $result;
2240 }
2241 } else {
54a67a59 2242 if (!insert_record("quiz_randomsamatch", $options)) {
2243 $result->error = "Could not insert quiz randomsamatch options!";
95dbc030 2244 return $result;
2245 }
2246 }
2247 break;
2248
8b439f8c 2249 case MULTIANSWER:
2250 if (!$oldmultianswers = get_records("quiz_multianswers", "question", $question->id, "id ASC")) {
2251 $oldmultianswers = array();
2252 }
2253
2254 // Insert all the new multi answers
2255 foreach ($question->answers as $dataanswer) {
2256 if ($oldmultianswer = array_shift($oldmultianswers)) { // Existing answer, so reuse it
2257 $multianswer = $oldmultianswer;
2258 $multianswer->positionkey = $dataanswer->positionkey;
2259 $multianswer->norm = $dataanswer->norm;
2260 $multianswer->answertype = $dataanswer->answertype;
2261
2262 if (! $multianswer->answers = quiz_save_multianswer_alternatives
2263 ($question->id, $dataanswer->answertype,
2264 $dataanswer->alternatives, $oldmultianswer->answers))
2265 {
2266 $result->error = "Could not update multianswer alternatives! (id=$multianswer->id)";
2267 return $result;
2268 }
2269 if (!update_record("quiz_multianswers", $multianswer)) {
2270 $result->error = "Could not update quiz multianswer! (id=$multianswer->id)";
2271 return $result;
2272 }
2273 } else { // This is a completely new answer
2274 unset($multianswer);
2275 $multianswer->question = $question->id;
2276 $multianswer->positionkey = $dataanswer->positionkey;
2277 $multianswer->norm = $dataanswer->norm;
2278 $multianswer->answertype = $dataanswer->answertype;
2279
2280 if (! $multianswer->answers = quiz_save_multianswer_alternatives
2281 ($question->id, $dataanswer->answertype,
2282 $dataanswer->alternatives))
2283 {
2284 $result->error = "Could not insert multianswer alternatives! (questionid=$question->id)";
2285 return $result;
2286 }
2287 if (!insert_record("quiz_multianswers", $multianswer)) {
2288 $result->error = "Could not insert quiz multianswer!";
2289 return $result;
2290 }
2291 }
2292 }
2293 break;
2294
34d52ad7 2295 case RANDOM:
2296 break;
2297
401c8de6 2298 case DESCRIPTION:
2299 break;
2300
49220fa7 2301 default:
2302 $result->error = "Unsupported question type ($question->qtype)!";
2303 return $result;
2304 break;
2305 }
2306 return true;
2307}
2308
2309
29d5d0b4 2310function quiz_remove_unwanted_questions(&$questions, $quiz) {
40b1a221 2311/// Given an array of questions, and a list of question IDs,
2312/// this function removes unwanted questions from the array
586b2c82 2313/// Used by review.php and attempt.php to counter changing quizzes
29d5d0b4 2314
2315 $quizquestions = array();
2316 $quizids = explode(",", $quiz->questions);
2317 foreach ($quizids as $quizid) {
2318 $quizquestions[$quizid] = true;
2319 }
2320 foreach ($questions as $key => $question) {
2321 if (!isset($quizquestions[$question->id])) {
2322 unset($questions[$key]);
2323 }
2324 }
2325}
2326
8b439f8c 2327function quiz_save_multianswer_alternatives
2328 ($questionid, $answertype, $alternatives, $oldalternativeids= NULL)
2329{
2330// Returns false if something goes wrong,
2331// otherwise the ids of the answers.
95dbc030 2332
8b439f8c 2333 if (empty($oldalternativeids)
17546249 2334 or !($oldalternatives =
2335 get_records_list('quiz_answers', 'id', $oldalternativeids)))
8b439f8c 2336 {
2337 $oldalternatives = array();
2338 }
2339
2340 $alternativeids = array();
2341
2342 foreach ($alternatives as $altdata) {
2343
2344 if ($altold = array_shift($oldalternatives)) { // Use existing one...
2345 $alt = $altold;
2346 $alt->answer = $altdata->answer;
2347 $alt->fraction = $altdata->fraction;
2348 $alt->feedback = $altdata->feedback;
2349 if (!update_record("quiz_answers", $alt)) {
2350 return false;
2351 }
2352
2353 } else { // Completely new one
2354 unset($alt);
2355 $alt->question= $questionid;
2356 $alt->answer = $altdata->answer;
2357 $alt->fraction = $altdata->fraction;
2358 $alt->feedback = $altdata->feedback;
17546249 2359 if (!($alt->id = insert_record("quiz_answers", $alt))) {
8b439f8c 2360 return false;
2361 }
2362 }
2363
2364 // For the answer type numerical, each alternative has individual options:
2365 if ($answertype == NUMERICAL) {
2366 if ($numericaloptions =
2367 get_record('quiz_numerical', 'answer', $alt->id))
2368 {
2369 // Reuse existing numerical options
2370 $numericaloptions->min = $altdata->min;
2371 $numericaloptions->max = $altdata->max;
2372 if (!update_record('quiz_numerical', $numericaloptions)) {
2373 return false;
2374 }
2375 } else {
2376 // New numerical options
2377 $numericaloptions->answer = $alt->id;
2378 $numericaloptions->question = $questionid;
2379 $numericaloptions->min = $altdata->min;
2380 $numericaloptions->max = $altdata->max;
2381 if (!insert_record("quiz_numerical", $numericaloptions)) {
2382 return false;
2383 }
2384 }
2385 } else { // Delete obsolete numerical options
2386 delete_records('quiz_numerical', 'answer', $alt->id);
2387 } // end if NUMERICAL
2388
2389 $alternativeids[] = $alt->id;
2390 } // end foreach $alternatives
2391 $answers = implode(',', $alternativeids);
2392
2393 // Removal of obsolete alternatives from answers and quiz_numerical:
2394 while ($altobsolete = array_shift($oldalternatives)) {
2395 delete_records("quiz_answers", "id", $altobsolete->id);
2396
2397 // Possibly obsolute numerical options are also to be deleted:
17546249 2398 delete_records("quiz_numerical", 'answer', $altobsolete->id);
8b439f8c 2399 }
2400
2401 // Common alternative options and removal of obsolete options
2402 switch ($answertype) {
2403 case NUMERICAL:
2404 if (!empty($oldalternativeids)) {
2405 delete_records('quiz_shortanswer', 'answers',
2406$oldalternativeids);
2407 delete_records('quiz_multichoice', 'answers',
2408$oldalternativeids);
2409 }
2410 break;
2411 case SHORTANSWER:
2412 if (!empty($oldalternativeids)) {
2413 delete_records('quiz_multichoice', 'answers',
2414$oldalternativeids);
2415 $options = get_record('quiz_shortanswer',
2416 'answers', $oldalternativeids);
2417 } else {
2418 unset($options);
2419 }
2420 if (empty($options)) {
2421 // Create new shortanswer options
2422 $options->question = $questionid;
2423 $options->usecase = 0;
2424 $options->answers = $answers;
2425 if (!insert_record('quiz_shortanswer', $options)) {
2426 return false;
2427 }
2428 } else if ($answers != $oldalternativeids) {
2429 // Shortanswer options needs update:
2430 $options->answers = $answers;
2431 if (!update_record('quiz_shortanswer', $options)) {
2432 return false;
2433 }
2434 }
2435 break;
2436 case MULTICHOICE:
2437 if (!empty($oldalternativeids)) {
2438 delete_records('quiz_shortanswer', 'answers',
2439$oldalternativeids);
2440 $options = get_record('quiz_multichoice',
2441 'answers', $oldalternativeids);
2442 } else {
2443 unset($options);
2444 }
2445 if (empty($options)) {
2446 // Create new multichoice options
2447 $options->question = $questionid;
2448 $options->layout = 0;
2449 $options->single = 1;
2450 $options->answers = $answers;
2451 if (!insert_record('quiz_multichoice', $options)) {
2452 return false;
2453 }
2454 } else if ($answers != $oldalternativeids) {
2455 // Multichoice options needs update:
2456 $options->answers = $answers;
2457 if (!update_record('quiz_multichoice', $options)) {
2458 return false;
2459 }
2460 }
2461 break;
2462 default:
2463 return false;
2464 }
2465 return $answers;
2466}
95dbc030 2467
eb452548 2468
730fd187 2469?>