MDL-10078 unit tests for grade category failing on set_as_parent()
[moodle.git] / mod / quiz / lib.php
CommitLineData
d1290cec 1<?php // $Id$
ee1fb969 2/**
3* Library of functions for the quiz module.
4*
5* This contains functions that are called also from outside the quiz module
6* Functions that are only called by the quiz module itself are in {@link locallib.php}
7* @version $Id$
8* @author Martin Dougiamas and many others.
9* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
10* @package quiz
11*/
730fd187 12
c4d588cc 13require_once($CFG->libdir.'/pagelib.php');
f67172b6 14require_once($CFG->libdir.'/questionlib.php');
8966a111 15
75cd257b 16/// CONSTANTS ///////////////////////////////////////////////////////////////////
17
18/**#@+
19 * The different review options are stored in the bits of $quiz->review
20 * These constants help to extract the options
21 */
22/**
23 * The first 6 bits refer to the time immediately after the attempt
24 */
1b8a7434 25define('QUIZ_REVIEW_IMMEDIATELY', 0x3f);
75cd257b 26/**
27 * the next 6 bits refer to the time after the attempt but while the quiz is open
28 */
1b8a7434 29define('QUIZ_REVIEW_OPEN', 0xfc0);
75cd257b 30/**
31 * the final 6 bits refer to the time after the quiz closes
32 */
1b8a7434 33define('QUIZ_REVIEW_CLOSED', 0x3f000);
75cd257b 34
35// within each group of 6 bits we determine what should be shown
1b8a7434 36define('QUIZ_REVIEW_RESPONSES', 1*0x1041); // Show responses
37define('QUIZ_REVIEW_SCORES', 2*0x1041); // Show scores
38define('QUIZ_REVIEW_FEEDBACK', 4*0x1041); // Show feedback
39define('QUIZ_REVIEW_ANSWERS', 8*0x1041); // Show correct answers
75cd257b 40// Some handling of worked solutions is already in the code but not yet fully supported
41// and not switched on in the user interface.
1b8a7434 42define('QUIZ_REVIEW_SOLUTIONS', 16*0x1041); // Show solutions
a4514d91 43define('QUIZ_REVIEW_GENERALFEEDBACK', 32*0x1041); // Show general feedback
75cd257b 44/**#@-*/
45
46/**
47 * If start and end date for the quiz are more than this many seconds apart
48 * they will be represented by two separate events in the calendar
49 */
50define("QUIZ_MAX_EVENT_LENGTH", "432000"); // 5 days maximum
ee1fb969 51
a5e1f35c 52/// FUNCTIONS ///////////////////////////////////////////////////////////////////
730fd187 53
920b93d1 54/**
55 * Given an object containing all the necessary data,
56 * (defined by the form in mod.html) this function
57 * will create a new instance and return the id number
58 * of the new instance.
a23f0aaf 59 *
920b93d1 60 * @param object $quiz the data that came from the form.
212b7b8c 61 * @return mixed the id of the new instance on success,
62 * false or a string error message on failure.
920b93d1 63 */
730fd187 64function quiz_add_instance($quiz) {
730fd187 65
920b93d1 66 // Process the options from the form.
67 $quiz->created = time();
bc569413 68 $quiz->questions = '';
212b7b8c 69 $result = quiz_process_options($quiz);
70 if ($result && is_string($result)) {
71 return $result;
72 }
6f797013 73
920b93d1 74 // Try to store it in the database.
75 if (!$quiz->id = insert_record("quiz", $quiz)) {
76 return false;
34283aa8 77 }
7bd1aa1d 78
920b93d1 79 // Do the processing required after an add or an update.
80 quiz_after_add_or_update($quiz);
a23f0aaf 81
7bd1aa1d 82 return $quiz->id;
730fd187 83}
84
920b93d1 85/**
86 * Given an object containing all the necessary data,
87 * (defined by the form in mod.html) this function
88 * will update an existing instance with new data.
a23f0aaf 89 *
920b93d1 90 * @param object $quiz the data that came from the form.
212b7b8c 91 * @return mixed true on success, false or a string error message on failure.
920b93d1 92 */
730fd187 93function quiz_update_instance($quiz) {
730fd187 94
920b93d1 95 // Process the options from the form.
212b7b8c 96 $result = quiz_process_options($quiz);
97 if ($result && is_string($result)) {
98 return $result;
99 }
ee1fb969 100
920b93d1 101 // Update the database.
730fd187 102 $quiz->id = $quiz->instance;
7bd1aa1d 103 if (!update_record("quiz", $quiz)) {
104 return false; // some error occurred
105 }
730fd187 106
920b93d1 107 // Do the processing required after an add or an update.
108 quiz_after_add_or_update($quiz);
ee1fb969 109
920b93d1 110 // Delete any previous preview attempts
111 delete_records('quiz_attempts', 'preview', '1', 'quiz', $quiz->id);
d2f308c0 112
7bd1aa1d 113 return true;
730fd187 114}
115
116
117function quiz_delete_instance($id) {
f41e824f 118/// Given an ID of an instance of this module,
119/// this function will permanently delete the instance
120/// and any data that depends on it.
730fd187 121
122 if (! $quiz = get_record("quiz", "id", "$id")) {
123 return false;
124 }
125
126 $result = true;
127
464edd6d 128 if ($attempts = get_records("quiz_attempts", "quiz", "$quiz->id")) {
10b9291c 129 foreach ($attempts as $attempt) {
212b7b8c 130 // TODO: this should use the delete_attempt($attempt->uniqueid) function in questionlib.php
4f48fb42 131 if (! delete_records("question_states", "attempt", "$attempt->uniqueid")) {
d115d8c7 132 $result = false;
133 }
03d1753c 134 if (! delete_records("question_sessions", "attemptid", "$attempt->uniqueid")) {
10b9291c 135 $result = false;
708b521a 136 }
10b9291c 137 }
138 }
139
212b7b8c 140 $tables_to_purge = array(
141 'quiz_attempts' => 'quiz',
142 'quiz_grades' => 'quiz',
143 'quiz_question_instances' => 'quiz',
144 'quiz_grades' => 'quiz',
145 'quiz_feedback' => 'quizid',
146 'quiz' => 'id'
147 );
148 foreach ($tables_to_purge as $table => $keyfield) {
149 if (!delete_records($table, $keyfield, $quiz->id)) {
150 $result = false;
151 }
730fd187 152 }
153
880d8675 154 $pagetypes = page_import_types('mod/quiz/');
155 foreach($pagetypes as $pagetype) {
156 if(!delete_records('block_instance', 'pageid', $quiz->id, 'pagetype', $pagetype)) {
157 $result = false;
158 }
159 }
160
78036f78 161 if ($events = get_records_select('event', "modulename = 'quiz' and instance = '$quiz->id'")) {
162 foreach($events as $event) {
163 delete_event($event->id);
164 }
b2a3cd2d 165 }
166
730fd187 167 return $result;
168}
169
b2d594c8 170
730fd187 171function quiz_user_outline($course, $user, $mod, $quiz) {
f41e824f 172/// Return a small object with summary information about what a
a5e1f35c 173/// user has done with a given particular instance of this module
174/// Used for user activity reports.
175/// $return->time = the time they did it
176/// $return->info = a short text description
9d677360 177 if ($grade = get_record('quiz_grades', 'userid', $user->id, 'quiz', $quiz->id)) {
a23f0aaf 178
ce687025 179 $result = new stdClass;
090cf95a 180 if ((float)$grade->grade) {
1105b32d 181 $result->info = get_string('grade').':&nbsp;'.round($grade->grade, $quiz->decimalpoints);
98092498 182 }
183 $result->time = $grade->timemodified;
184 return $result;
185 }
186 return NULL;
730fd187 187
730fd187 188}
189
ee1fb969 190
730fd187 191function quiz_user_complete($course, $user, $mod, $quiz) {
f41e824f 192/// Print a detailed representation of what a user has done with
a5e1f35c 193/// a given particular instance of this module, for user activity reports.
730fd187 194
ee1fb969 195 if ($attempts = get_records_select('quiz_attempts', "userid='$user->id' AND quiz='$quiz->id'", 'attempt ASC')) {
8779eab5 196 if ($quiz->grade and $quiz->sumgrades && $grade = get_record('quiz_grades', 'userid', $user->id, 'quiz', $quiz->id)) {
1105b32d 197 echo get_string('grade').': '.round($grade->grade, $quiz->decimalpoints).'/'.$quiz->grade.'<br />';
ee1fb969 198 }
199 foreach ($attempts as $attempt) {
200 echo get_string('attempt', 'quiz').' '.$attempt->attempt.': ';
201 if ($attempt->timefinish == 0) {
202 print_string('unfinished');
203 } else {
1105b32d 204 echo round($attempt->sumgrades, $quiz->decimalpoints).'/'.$quiz->sumgrades;
ee1fb969 205 }
206 echo ' - '.userdate($attempt->timemodified).'<br />';
207 }
208 } else {
209 print_string('noattempts', 'quiz');
210 }
211
730fd187 212 return true;
213}
214
ee1fb969 215
730fd187 216function quiz_cron () {
a5e1f35c 217/// Function to be run periodically according to the moodle cron
f41e824f 218/// This function searches for things that need to be done, such
219/// as sending out mail, toggling flags etc ...
730fd187 220
221 global $CFG;
222
223 return true;
224}
225
d0ac6bc2 226function quiz_grades($quizid) {
858deff0 227/// Must return an array of grades, indexed by user, and a max grade.
228
d5838a4b 229 $quiz = get_record('quiz', 'id', intval($quizid));
230 if (empty($quiz) || empty($quiz->grade)) {
ed1daaa9 231 return NULL;
232 }
233
ce687025 234 $return = new stdClass;
d5838a4b 235 $return->grades = get_records_menu('quiz_grades', 'quiz', $quiz->id, '', 'userid, grade');
236 $return->maxgrade = get_field('quiz', 'grade', 'id', $quiz->id);
858deff0 237 return $return;
d0ac6bc2 238}
239
d061d883 240function quiz_get_participants($quizid) {
241/// Returns an array of users who have data in a given quiz
e4acc4ce 242/// (users with records in quiz_attempts and quiz_question_versions)
d061d883 243
244 global $CFG;
245
e4acc4ce 246 //Get users from attempts
247 $us_attempts = get_records_sql("SELECT DISTINCT u.id, u.id
248 FROM {$CFG->prefix}user u,
249 {$CFG->prefix}quiz_attempts a
250 WHERE a.quiz = '$quizid' and
251 u.id = a.userid");
252
253 //Get users from question_versions
254 $us_versions = get_records_sql("SELECT DISTINCT u.id, u.id
255 FROM {$CFG->prefix}user u,
19098759 256 {$CFG->prefix}quiz_question_versions v
e4acc4ce 257 WHERE v.quiz = '$quizid' and
258 u.id = v.userid");
259
260 //Add us_versions to us_attempts
261 if ($us_versions) {
262 foreach ($us_versions as $us_version) {
263 $us_attempts[$us_version->id] = $us_version;
264 }
265 }
266 //Return us_attempts array (it contains an array of unique users)
267 return ($us_attempts);
268
d061d883 269}
730fd187 270
d2f308c0 271function quiz_refresh_events($courseid = 0) {
920b93d1 272// This horrible function only seems to be called from mod/quiz/db/[dbtype].php.
273
d2f308c0 274// This standard function will check all instances of this module
275// and make sure there are up-to-date events created for each of them.
b2a3cd2d 276// If courseid = 0, then every quiz event in the site is checked, else
277// only quiz events belonging to the course specified are checked.
d2f308c0 278// This function is used, in its new format, by restore_refresh_events()
279
280 if ($courseid == 0) {
281 if (! $quizzes = get_records("quiz")) {
282 return true;
283 }
284 } else {
285 if (! $quizzes = get_records("quiz", "course", $courseid)) {
286 return true;
287 }
288 }
dcd338ff 289 $moduleid = get_field('modules', 'id', 'name', 'quiz');
f41e824f 290
d2f308c0 291 foreach ($quizzes as $quiz) {
292 $event = NULL;
b2a3cd2d 293 $event2 = NULL;
294 $event2old = NULL;
295
296 if ($events = get_records_select('event', "modulename = 'quiz' AND instance = '$quiz->id' ORDER BY timestart")) {
297 $event = array_shift($events);
298 if (!empty($events)) {
299 $event2old = array_shift($events);
300 if (!empty($events)) {
301 foreach ($events as $badevent) {
302 delete_records('event', 'id', $badevent->id);
303 }
304 }
305 }
306 }
307
d2f308c0 308 $event->name = addslashes($quiz->name);
309 $event->description = addslashes($quiz->intro);
b2a3cd2d 310 $event->courseid = $quiz->course;
311 $event->groupid = 0;
312 $event->userid = 0;
313 $event->modulename = 'quiz';
314 $event->instance = $quiz->id;
ba288539 315 $event->visible = instance_is_visible('quiz', $quiz);
d2f308c0 316 $event->timestart = $quiz->timeopen;
b2a3cd2d 317 $event->eventtype = 'open';
d2f308c0 318 $event->timeduration = ($quiz->timeclose - $quiz->timeopen);
d2f308c0 319
b2a3cd2d 320 if ($event->timeduration > QUIZ_MAX_EVENT_LENGTH) { /// Set up two events
d2f308c0 321
b2a3cd2d 322 $event2 = $event;
d2f308c0 323
b2a3cd2d 324 $event->name = addslashes($quiz->name).' ('.get_string('quizopens', 'quiz').')';
325 $event->timeduration = 0;
326
327 $event2->name = addslashes($quiz->name).' ('.get_string('quizcloses', 'quiz').')';
328 $event2->timestart = $quiz->timeclose;
329 $event2->eventtype = 'close';
330 $event2->timeduration = 0;
331
332 if (empty($event2old->id)) {
333 unset($event2->id);
334 add_event($event2);
335 } else {
336 $event2->id = $event2old->id;
337 update_event($event2);
338 }
339 } else if (!empty($event2->id)) {
340 delete_event($event2->id);
341 }
342
343 if (empty($event->id)) {
d2f308c0 344 add_event($event);
b2a3cd2d 345 } else {
346 update_event($event);
d2f308c0 347 }
b2a3cd2d 348
d2f308c0 349 }
350 return true;
351}
352
6710ec87 353
354function quiz_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $quiz="0", $user="", $groupid="") {
355// Returns all quizzes since a given time. If quiz is specified then
356// this restricts the results
357
358 global $CFG;
359
360 if ($quiz) {
361 $quizselect = " AND cm.id = '$quiz'";
362 } else {
363 $quizselect = "";
364 }
365 if ($user) {
366 $userselect = " AND u.id = '$user'";
367 } else {
368 $userselect = "";
369 }
370
371 $quizzes = get_records_sql("SELECT qa.*, q.name, u.firstname, u.lastname, u.picture,
372 q.course, q.sumgrades as maxgrade, cm.instance, cm.section
373 FROM {$CFG->prefix}quiz_attempts qa,
374 {$CFG->prefix}quiz q,
375 {$CFG->prefix}user u,
376 {$CFG->prefix}course_modules cm
377 WHERE qa.timefinish > '$sincetime'
378 AND qa.userid = u.id $userselect
379 AND qa.quiz = q.id $quizselect
380 AND cm.instance = q.id
381 AND cm.course = '$courseid'
382 AND q.course = cm.course
383 ORDER BY qa.timefinish ASC");
384
385 if (empty($quizzes))
386 return;
387
388 foreach ($quizzes as $quiz) {
f3f7610c 389 if (empty($groupid) || groups_is_member($groupid, $quiz->userid)) {
6710ec87 390
ac21ad39 391 $tmpactivity = new Object;
392
6710ec87 393 $tmpactivity->type = "quiz";
394 $tmpactivity->defaultindex = $index;
395 $tmpactivity->instance = $quiz->quiz;
396
397 $tmpactivity->name = $quiz->name;
398 $tmpactivity->section = $quiz->section;
399
400 $tmpactivity->content->attemptid = $quiz->id;
401 $tmpactivity->content->sumgrades = $quiz->sumgrades;
402 $tmpactivity->content->maxgrade = $quiz->maxgrade;
403 $tmpactivity->content->attempt = $quiz->attempt;
404
405 $tmpactivity->user->userid = $quiz->userid;
406 $tmpactivity->user->fullname = fullname($quiz);
407 $tmpactivity->user->picture = $quiz->picture;
408
409 $tmpactivity->timestamp = $quiz->timefinish;
410
411 $activities[] = $tmpactivity;
412
413 $index++;
414 }
415 }
416
417 return;
418}
419
420
421function quiz_print_recent_mod_activity($activity, $course, $detail=false) {
6eaae5bd 422 global $CFG;
6710ec87 423
424 echo '<table border="0" cellpadding="3" cellspacing="0">';
425
6f797013 426 echo "<tr><td class=\"forumpostpicture\" width=\"35\" valign=\"top\">";
6710ec87 427 print_user_picture($activity->user->userid, $course, $activity->user->picture);
09275894 428 echo "</td><td style=\"width:100%;\"><font size=\"2\">";
6710ec87 429
430 if ($detail) {
431 echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
0d905d9f 432 "class=\"icon\" alt=\"$activity->type\" /> ";
6710ec87 433 echo "<a href=\"$CFG->wwwroot/mod/quiz/view.php?id=" . $activity->instance . "\">"
95c67e2f 434 . format_string($activity->name,true) . "</a> - ";
6710ec87 435
436 }
437
2bb63d99 438 if (has_capability('mod/quiz:grade', get_context_instance(CONTEXT_MODULE, $course))) {
6710ec87 439 $grades = "(" . $activity->content->sumgrades . " / " . $activity->content->maxgrade . ") ";
440 echo "<a href=\"$CFG->wwwroot/mod/quiz/review.php?q="
441 . $activity->instance . "&amp;attempt="
442 . $activity->content->attemptid . "\">" . $grades . "</a> ";
443
444 echo get_string("attempt", "quiz") . " - " . $activity->content->attempt . "<br />";
445 }
446 echo "<a href=\"$CFG->wwwroot/user/view.php?id="
447 . $activity->user->userid . "&amp;course=$course\">"
448 . $activity->user->fullname . "</a> ";
449
450 echo " - " . userdate($activity->timestamp);
451
452 echo "</font></td></tr>";
453 echo "</table>";
454
455 return;
456}
457
ee1fb969 458/**
920b93d1 459 * Pre-process the quiz options form data, making any necessary adjustments.
a0807a00 460 * Called by add/update instance in this file, and the save code in admin/module.php.
461 *
920b93d1 462 * @param object $quiz The variables set on the form.
463 */
464function quiz_process_options(&$quiz) {
465 $quiz->timemodified = time();
ee1fb969 466
920b93d1 467 // Quiz open time.
a23f0aaf 468 if (empty($quiz->timeopen)) {
920b93d1 469 $quiz->preventlate = 0;
ee1fb969 470 }
471
920b93d1 472 // Quiz name. (Make up a default if one was not given.)
473 if (empty($quiz->name)) {
474 if (empty($quiz->intro)) {
475 $quiz->name = get_string('modulename', 'quiz');
476 } else {
477 $quiz->name = shorten_text(strip_tags($quiz->intro));
478 }
479 }
480 $quiz->name = trim($quiz->name);
a23f0aaf 481
920b93d1 482 // Time limit. (Get rid of it if the checkbox was not ticked.)
a23f0aaf 483 if (empty($quiz->timelimitenable)) {
920b93d1 484 $quiz->timelimit = 0;
485 }
486 $quiz->timelimit = round($quiz->timelimit);
a23f0aaf 487
ab0a8ff2 488 // Password field - different in form to stop browsers that remember passwords
489 // getting confused.
490 $quiz->password = $quiz->quizpassword;
491 unset($quiz->quizpassword);
492
212b7b8c 493 // Quiz feedback
a0807a00 494 if (isset($quiz->feedbacktext)) {
495 // Clean up the boundary text.
496 for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
497 if (empty($quiz->feedbacktext[$i])) {
498 $quiz->feedbacktext[$i] = '';
499 } else {
500 $quiz->feedbacktext[$i] = trim($quiz->feedbacktext[$i]);
501 }
212b7b8c 502 }
a0807a00 503
504 // Check the boundary value is a number or a percentage, and in range.
505 $i = 0;
506 while (!empty($quiz->feedbackboundaries[$i])) {
507 $boundary = trim($quiz->feedbackboundaries[$i]);
508 if (!is_numeric($boundary)) {
509 if (strlen($boundary) > 0 && $boundary[strlen($boundary) - 1] == '%') {
510 $boundary = trim(substr($boundary, 0, -1));
511 if (is_numeric($boundary)) {
512 $boundary = $boundary * $quiz->grade / 100.0;
513 } else {
514 return get_string('feedbackerrorboundaryformat', 'quiz', $i + 1);
515 }
212b7b8c 516 }
517 }
a0807a00 518 if ($boundary <= 0 || $boundary >= $quiz->grade) {
519 return get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1);
520 }
521 if ($i > 0 && $boundary >= $quiz->feedbackboundaries[$i - 1]) {
522 return get_string('feedbackerrororder', 'quiz', $i + 1);
523 }
524 $quiz->feedbackboundaries[$i] = $boundary;
525 $i += 1;
212b7b8c 526 }
a0807a00 527 $numboundaries = $i;
528
529 // Check there is nothing in the remaining unused fields.
530 for ($i = $numboundaries; $i < count($quiz->feedbackboundaries); $i += 1) {
531 if (!empty($quiz->feedbackboundaries[$i]) && trim($quiz->feedbackboundaries[$i]) != '') {
532 return get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1);
533 }
212b7b8c 534 }
a0807a00 535 for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
536 if (!empty($quiz->feedbacktext[$i]) && trim($quiz->feedbacktext[$i]) != '') {
537 return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
538 }
212b7b8c 539 }
a0807a00 540 $quiz->feedbackboundaries[-1] = $quiz->grade + 1; // Needs to be bigger than $quiz->grade because of '<' test in quiz_feedback_for_grade().
541 $quiz->feedbackboundaries[$numboundaries] = 0;
542 $quiz->feedbackboundarycount = $numboundaries;
212b7b8c 543 }
a23f0aaf 544
920b93d1 545 // Settings that get combined to go into the optionflags column.
546 $quiz->optionflags = 0;
547 if (!empty($quiz->adaptive)) {
548 $quiz->optionflags |= QUESTION_ADAPTIVE;
549 }
550
551 // Settings that get combined to go into the review column.
552 $review = 0;
553 if (isset($quiz->responsesimmediately)) {
ee1fb969 554 $review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_IMMEDIATELY);
920b93d1 555 unset($quiz->responsesimmediately);
ee1fb969 556 }
920b93d1 557 if (isset($quiz->responsesopen)) {
ee1fb969 558 $review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_OPEN);
920b93d1 559 unset($quiz->responsesopen);
ee1fb969 560 }
920b93d1 561 if (isset($quiz->responsesclosed)) {
ee1fb969 562 $review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_CLOSED);
920b93d1 563 unset($quiz->responsesclosed);
ee1fb969 564 }
565
920b93d1 566 if (isset($quiz->scoreimmediately)) {
ee1fb969 567 $review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_IMMEDIATELY);
920b93d1 568 unset($quiz->scoreimmediately);
ee1fb969 569 }
920b93d1 570 if (isset($quiz->scoreopen)) {
ee1fb969 571 $review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN);
920b93d1 572 unset($quiz->scoreopen);
ee1fb969 573 }
920b93d1 574 if (isset($quiz->scoreclosed)) {
ee1fb969 575 $review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED);
920b93d1 576 unset($quiz->scoreclosed);
ee1fb969 577 }
578
920b93d1 579 if (isset($quiz->feedbackimmediately)) {
ee1fb969 580 $review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY);
920b93d1 581 unset($quiz->feedbackimmediately);
ee1fb969 582 }
920b93d1 583 if (isset($quiz->feedbackopen)) {
ee1fb969 584 $review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN);
920b93d1 585 unset($quiz->feedbackopen);
ee1fb969 586 }
920b93d1 587 if (isset($quiz->feedbackclosed)) {
ee1fb969 588 $review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED);
920b93d1 589 unset($quiz->feedbackclosed);
ee1fb969 590 }
591
920b93d1 592 if (isset($quiz->answersimmediately)) {
ee1fb969 593 $review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY);
920b93d1 594 unset($quiz->answersimmediately);
ee1fb969 595 }
920b93d1 596 if (isset($quiz->answersopen)) {
ee1fb969 597 $review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_OPEN);
920b93d1 598 unset($quiz->answersopen);
ee1fb969 599 }
920b93d1 600 if (isset($quiz->answersclosed)) {
ee1fb969 601 $review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_CLOSED);
920b93d1 602 unset($quiz->answersclosed);
ee1fb969 603 }
604
920b93d1 605 if (isset($quiz->solutionsimmediately)) {
ee1fb969 606 $review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_IMMEDIATELY);
920b93d1 607 unset($quiz->solutionsimmediately);
ee1fb969 608 }
920b93d1 609 if (isset($quiz->solutionsopen)) {
ee1fb969 610 $review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_OPEN);
920b93d1 611 unset($quiz->solutionsopen);
ee1fb969 612 }
920b93d1 613 if (isset($quiz->solutionsclosed)) {
ee1fb969 614 $review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_CLOSED);
920b93d1 615 unset($quiz->solutionsclosed);
ee1fb969 616 }
617
a4514d91 618 if (isset($quiz->generalfeedbackimmediately)) {
619 $review += (QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_IMMEDIATELY);
920b93d1 620 unset($quiz->solutionsimmediately);
1b8a7434 621 }
a4514d91 622 if (isset($quiz->generalfeedbackopen)) {
623 $review += (QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_OPEN);
920b93d1 624 unset($quiz->solutionsopen);
1b8a7434 625 }
a4514d91 626 if (isset($quiz->generalfeedbackclosed)) {
627 $review += (QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_CLOSED);
920b93d1 628 unset($quiz->solutionsclosed);
1b8a7434 629 }
630
920b93d1 631 $quiz->review = $review;
632}
633
634/**
635 * This function is called at the end of quiz_add_instance
636 * and quiz_update_instance, to do the common processing.
a23f0aaf 637 *
920b93d1 638 * @param object $quiz the quiz object.
639 */
640function quiz_after_add_or_update($quiz) {
641
212b7b8c 642 // Save the feedback
643 delete_records('quiz_feedback', 'quizid', $quiz->id);
a23f0aaf 644
212b7b8c 645 for ($i = 0; $i <= $quiz->feedbackboundarycount; $i += 1) {
646 $feedback = new stdClass;
647 $feedback->quizid = $quiz->id;
648 $feedback->feedbacktext = $quiz->feedbacktext[$i];
649 $feedback->mingrade = $quiz->feedbackboundaries[$i];
650 $feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
651 if (!insert_record('quiz_feedback', $feedback, false)) {
652 return "Could not save quiz feedback.";
653 }
654 }
655
ee1fb969 656
920b93d1 657 // Update the events relating to this quiz.
658 // This is slightly inefficient, deleting the old events and creating new ones. However,
659 // there are at most two events, and this keeps the code simpler.
660 if ($events = get_records_select('event', "modulename = 'quiz' and instance = '$quiz->id'")) {
661 foreach($events as $event) {
662 delete_event($event->id);
663 }
664 }
665
666 $event = new stdClass;
667 $event->description = $quiz->intro;
668 $event->courseid = $quiz->course;
669 $event->groupid = 0;
670 $event->userid = 0;
671 $event->modulename = 'quiz';
672 $event->instance = $quiz->id;
673 $event->timestart = $quiz->timeopen;
674 $event->timeduration = $quiz->timeclose - $quiz->timeopen;
675 $event->visible = instance_is_visible('quiz', $quiz);
676 $event->eventtype = 'open';
677
678 if ($quiz->timeclose and $quiz->timeopen and $event->timeduration <= QUIZ_MAX_EVENT_LENGTH) {
679 // Single event for the whole quiz.
680 $event->name = $quiz->name;
681 add_event($event);
682 } else {
683 // Separate start and end events.
684 $event->timeduration = 0;
685 if ($quiz->timeopen) {
686 $event->name = $quiz->name.' ('.get_string('quizopens', 'quiz').')';
687 add_event($event);
688 unset($event->id); // So we can use the same object for the close event.
689 }
690 if ($quiz->timeclose) {
691 $event->name = $quiz->name.' ('.get_string('quizcloses', 'quiz').')';
692 $event->timestart = $quiz->timeclose;
693 $event->eventtype = 'close';
694 add_event($event);
695 }
696 }
ee1fb969 697}
698
f3221af9 699function quiz_get_view_actions() {
700 return array('view','view all','report');
701}
ee1fb969 702
f3221af9 703function quiz_get_post_actions() {
704 return array('attempt','editquestions','review','submit');
705}
ee1fb969 706
f67172b6 707/**
708 * Returns an array of names of quizzes that use this question
709 *
710 * TODO: write this
711 * @param object $questionid
712 * @return array of strings
713 */
714function quiz_question_list_instances($questionid) {
715 return array();
716}
717
f3f7610c 718?>