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 |
13 | require_once($CFG->libdir.'/pagelib.php'); |
f67172b6 |
14 | require_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 |
25 | define('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 |
29 | define('QUIZ_REVIEW_OPEN', 0xfc0); |
75cd257b |
30 | /** |
31 | * the final 6 bits refer to the time after the quiz closes |
32 | */ |
1b8a7434 |
33 | define('QUIZ_REVIEW_CLOSED', 0x3f000); |
75cd257b |
34 | |
35 | // within each group of 6 bits we determine what should be shown |
1b8a7434 |
36 | define('QUIZ_REVIEW_RESPONSES', 1*0x1041); // Show responses |
37 | define('QUIZ_REVIEW_SCORES', 2*0x1041); // Show scores |
38 | define('QUIZ_REVIEW_FEEDBACK', 4*0x1041); // Show feedback |
39 | define('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 |
42 | define('QUIZ_REVIEW_SOLUTIONS', 16*0x1041); // Show solutions |
a4514d91 |
43 | define('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 | */ |
50 | define("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 |
64 | function 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 |
93 | function 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 | |
117 | function 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 |
171 | function 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').': '.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 |
191 | function 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 |
216 | function 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 |
226 | function 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 |
240 | function 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 |
271 | function 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 | |
354 | function 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 | |
421 | function 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 . "&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 . "&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 | */ |
464 | function 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 | */ |
640 | function 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 |
699 | function quiz_get_view_actions() { |
700 | return array('view','view all','report'); |
701 | } |
ee1fb969 |
702 | |
f3221af9 |
703 | function 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 | */ |
714 | function quiz_question_list_instances($questionid) { |
715 | return array(); |
716 | } |
717 | |
f3f7610c |
718 | ?> |