So $CFG->filtermatchfirstonly isn't forgotten
[moodle.git] / mod / quiz / lib.php
CommitLineData
d1290cec 1<?php // $Id$
730fd187 2
a5e1f35c 3/// Library of function for module quiz
730fd187 4
c4d588cc 5require_once($CFG->libdir.'/pagelib.php');
6
a5e1f35c 7/// CONSTANTS ///////////////////////////////////////////////////////////////////
730fd187 8
b2a3cd2d 9define("QUIZ_MAX_EVENT_LENGTH", "432000"); // 5 days maximum
8966a111 10
a5e1f35c 11/// FUNCTIONS ///////////////////////////////////////////////////////////////////
730fd187 12
13function quiz_add_instance($quiz) {
f41e824f 14/// Given an object containing all the necessary data,
15/// (defined by the form in mod.html) this function
16/// will create a new instance and return the id number
a5e1f35c 17/// of the new instance.
730fd187 18
49dcdd18 19 $quiz->created = time();
730fd187 20 $quiz->timemodified = time();
f41e824f 21 $quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday,
c04c41c7 22 $quiz->openhour, $quiz->openminute, 0);
f41e824f 23 $quiz->timeclose = make_timestamp($quiz->closeyear, $quiz->closemonth, $quiz->closeday,
c04c41c7 24 $quiz->closehour, $quiz->closeminute, 0);
6f797013 25
56b2152f 26 if (empty($quiz->name)) {
27 if (empty($quiz->intro)) {
28 $quiz->name = get_string('modulename', 'quiz');
29 } else {
30 $quiz->name = strip_tags($quiz->intro);
31 }
32 }
33 $quiz->name = trim($quiz->name);
730fd187 34
7bd1aa1d 35 if (!$quiz->id = insert_record("quiz", $quiz)) {
36 return false; // some error occurred
37 }
6f797013 38
34283aa8 39 if (isset($quiz->optionsettingspref)) {
40 set_user_preference('quiz_optionsettingspref', $quiz->optionsettingspref);
41 }
7bd1aa1d 42
565340c6 43 delete_records('event', 'modulename', 'quiz', 'instance', $quiz->id); // Just in case
44
d2f308c0 45 $event = NULL;
46 $event->name = $quiz->name;
47 $event->description = $quiz->intro;
48 $event->courseid = $quiz->course;
49 $event->groupid = 0;
50 $event->userid = 0;
51 $event->modulename = 'quiz';
52 $event->instance = $quiz->id;
b2a3cd2d 53 $event->eventtype = 'open';
d2f308c0 54 $event->timestart = $quiz->timeopen;
b71213e3 55 $event->visible = instance_is_visible('quiz', $quiz);
d2f308c0 56 $event->timeduration = ($quiz->timeclose - $quiz->timeopen);
b2a3cd2d 57
58 if ($event->timeduration > QUIZ_MAX_EVENT_LENGTH) { /// Long durations create two events
59 $event2 = $event;
60
61 $event->name .= ' ('.get_string('quizopens', 'quiz').')';
62 $event->timeduration = 0;
63
64 $event2->timestart = $quiz->timeclose;
65 $event2->eventtype = 'close';
66 $event2->timeduration = 0;
67 $event2->name .= ' ('.get_string('quizcloses', 'quiz').')';
68
69 add_event($event2);
d2f308c0 70 }
b2a3cd2d 71
d2f308c0 72 add_event($event);
73
7bd1aa1d 74 return $quiz->id;
730fd187 75}
76
77
78function quiz_update_instance($quiz) {
f41e824f 79/// Given an object containing all the necessary data,
34283aa8 80/// (defined by the form in mod.html or edit.php) this function
a5e1f35c 81/// will update an existing instance with new data.
730fd187 82
83 $quiz->timemodified = time();
34283aa8 84 if (isset($quiz->openyear)) { // this would not be set if we come from edit.php
85 $quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday,
86 $quiz->openhour, $quiz->openminute, 0);
87 $quiz->timeclose = make_timestamp($quiz->closeyear, $quiz->closemonth, $quiz->closeday,
88 $quiz->closehour, $quiz->closeminute, 0);
89 }
730fd187 90 $quiz->id = $quiz->instance;
91
7bd1aa1d 92 if (!update_record("quiz", $quiz)) {
93 return false; // some error occurred
94 }
730fd187 95
34283aa8 96 if (isset($quiz->optionsettingspref)) {
97 set_user_preference('quiz_optionsettingspref', $quiz->optionsettingspref);
98 }
7bd1aa1d 99
78036f78 100 // currently this code deletes all existing events and adds new ones
101 // this should be improved to update existing events only
102 if ($events = get_records_select('event', "modulename = 'quiz' and instance = '$quiz->id'")) {
103 foreach($events as $event) {
104 delete_event($event->id);
105 }
106 }
6f797013 107
78036f78 108 unset($event);
565340c6 109 $event->description = $quiz->intro;
110 $event->courseid = $quiz->course;
111 $event->groupid = 0;
112 $event->userid = 0;
113 $event->modulename = 'quiz';
114 $event->instance = $quiz->id;
115 $event->eventtype = 'open';
116 $event->timestart = $quiz->timeopen;
ba288539 117 $event->visible = instance_is_visible('quiz', $quiz);
565340c6 118 $event->timeduration = ($quiz->timeclose - $quiz->timeopen);
d2f308c0 119
565340c6 120 if ($event->timeduration > QUIZ_MAX_EVENT_LENGTH) { /// Long durations create two events
d2f308c0 121
78036f78 122 $event->name = $quiz->name.' ('.get_string('quizopens', 'quiz').')';
565340c6 123 $event->timeduration = 0;
78036f78 124 add_event($event);
565340c6 125
78036f78 126 $event->timestart = $quiz->timeclose;
127 $event->eventtype = 'close';
128 $event->name = $quiz->name.' ('.get_string('quizcloses', 'quiz').')';
129 unset($event->id);
130 add_event($event);
131 } else { // single event with duration
132 $event->name = $quiz->name;
133 add_event($event);
d2f308c0 134 }
135
7bd1aa1d 136 return true;
730fd187 137}
138
139
140function quiz_delete_instance($id) {
f41e824f 141/// Given an ID of an instance of this module,
142/// this function will permanently delete the instance
143/// and any data that depends on it.
730fd187 144
145 if (! $quiz = get_record("quiz", "id", "$id")) {
146 return false;
147 }
148
149 $result = true;
150
464edd6d 151 if ($attempts = get_records("quiz_attempts", "quiz", "$quiz->id")) {
10b9291c 152 foreach ($attempts as $attempt) {
153 if (! delete_records("quiz_responses", "attempt", "$attempt->id")) {
154 $result = false;
155 }
156 }
157 }
158
159 if (! delete_records("quiz_attempts", "quiz", "$quiz->id")) {
160 $result = false;
161 }
162
163 if (! delete_records("quiz_grades", "quiz", "$quiz->id")) {
164 $result = false;
165 }
166
167 if (! delete_records("quiz_question_grades", "quiz", "$quiz->id")) {
168 $result = false;
169 }
730fd187 170
171 if (! delete_records("quiz", "id", "$quiz->id")) {
172 $result = false;
173 }
174
880d8675 175 $pagetypes = page_import_types('mod/quiz/');
176 foreach($pagetypes as $pagetype) {
177 if(!delete_records('block_instance', 'pageid', $quiz->id, 'pagetype', $pagetype)) {
178 $result = false;
179 }
180 }
181
78036f78 182 if ($events = get_records_select('event', "modulename = 'quiz' and instance = '$quiz->id'")) {
183 foreach($events as $event) {
184 delete_event($event->id);
185 }
b2a3cd2d 186 }
187
730fd187 188 return $result;
189}
190
b2d594c8 191function quiz_delete_course($course) {
192/// Given a course object, this function will clean up anything that
193/// would be leftover after all the instances were deleted
464edd6d 194/// In this case, all non-publish quiz categories and questions
b2d594c8 195
464edd6d 196 if ($categories = get_records_select("quiz_categories", "course = '$course->id' AND publish = '0'")) {
b2d594c8 197 foreach ($categories as $category) {
198 if ($questions = get_records("quiz_questions", "category", $category->id)) {
199 foreach ($questions as $question) {
200 delete_records("quiz_answers", "question", $question->id);
201 delete_records("quiz_match", "question", $question->id);
202 delete_records("quiz_match_sub", "question", $question->id);
203 delete_records("quiz_multianswers", "question", $question->id);
204 delete_records("quiz_multichoice", "question", $question->id);
205 delete_records("quiz_numerical", "question", $question->id);
206 delete_records("quiz_randommatch", "question", $question->id);
207 delete_records("quiz_responses", "question", $question->id);
208 delete_records("quiz_shortanswer", "question", $question->id);
209 delete_records("quiz_truefalse", "question", $question->id);
210 }
211 delete_records("quiz_questions", "category", $category->id);
212 }
213 }
214 return delete_records("quiz_categories", "course", $course->id);
215 }
216 return true;
217}
218
219
730fd187 220function quiz_user_outline($course, $user, $mod, $quiz) {
f41e824f 221/// Return a small object with summary information about what a
a5e1f35c 222/// user has done with a given particular instance of this module
223/// Used for user activity reports.
224/// $return->time = the time they did it
225/// $return->info = a short text description
9d677360 226 if ($grade = get_record('quiz_grades', 'userid', $user->id, 'quiz', $quiz->id)) {
f41e824f 227
090cf95a 228 if ((float)$grade->grade) {
c6e88f10 229 $result->info = get_string('grade').':&nbsp;'.format_float($grade->grade, $quiz->decimalpoints);
98092498 230 }
231 $result->time = $grade->timemodified;
232 return $result;
233 }
234 return NULL;
730fd187 235
730fd187 236}
237
238function quiz_user_complete($course, $user, $mod, $quiz) {
f41e824f 239/// Print a detailed representation of what a user has done with
a5e1f35c 240/// a given particular instance of this module, for user activity reports.
730fd187 241
242 return true;
243}
244
730fd187 245function quiz_cron () {
a5e1f35c 246/// Function to be run periodically according to the moodle cron
f41e824f 247/// This function searches for things that need to be done, such
248/// as sending out mail, toggling flags etc ...
730fd187 249
250 global $CFG;
251
252 return true;
253}
254
d0ac6bc2 255function quiz_grades($quizid) {
858deff0 256/// Must return an array of grades, indexed by user, and a max grade.
257
d5838a4b 258 $quiz = get_record('quiz', 'id', intval($quizid));
259 if (empty($quiz) || empty($quiz->grade)) {
ed1daaa9 260 return NULL;
261 }
262
d5838a4b 263 $return->grades = get_records_menu('quiz_grades', 'quiz', $quiz->id, '', 'userid, grade');
264 $return->maxgrade = get_field('quiz', 'grade', 'id', $quiz->id);
858deff0 265 return $return;
d0ac6bc2 266}
267
d061d883 268function quiz_get_participants($quizid) {
269/// Returns an array of users who have data in a given quiz
270/// (users with records in quiz_attempts, students)
271
272 global $CFG;
273
95e72c12 274 return get_records_sql("SELECT DISTINCT u.id, u.id
d061d883 275 FROM {$CFG->prefix}user u,
276 {$CFG->prefix}quiz_attempts a
f41e824f 277 WHERE a.quiz = '$quizid' and
d061d883 278 u.id = a.userid");
279}
730fd187 280
d2f308c0 281function quiz_refresh_events($courseid = 0) {
282// This standard function will check all instances of this module
283// and make sure there are up-to-date events created for each of them.
b2a3cd2d 284// If courseid = 0, then every quiz event in the site is checked, else
285// only quiz events belonging to the course specified are checked.
d2f308c0 286// This function is used, in its new format, by restore_refresh_events()
287
288 if ($courseid == 0) {
289 if (! $quizzes = get_records("quiz")) {
290 return true;
291 }
292 } else {
293 if (! $quizzes = get_records("quiz", "course", $courseid)) {
294 return true;
295 }
296 }
dcd338ff 297 $moduleid = get_field('modules', 'id', 'name', 'quiz');
f41e824f 298
d2f308c0 299 foreach ($quizzes as $quiz) {
300 $event = NULL;
b2a3cd2d 301 $event2 = NULL;
302 $event2old = NULL;
303
304 if ($events = get_records_select('event', "modulename = 'quiz' AND instance = '$quiz->id' ORDER BY timestart")) {
305 $event = array_shift($events);
306 if (!empty($events)) {
307 $event2old = array_shift($events);
308 if (!empty($events)) {
309 foreach ($events as $badevent) {
310 delete_records('event', 'id', $badevent->id);
311 }
312 }
313 }
314 }
315
d2f308c0 316 $event->name = addslashes($quiz->name);
317 $event->description = addslashes($quiz->intro);
b2a3cd2d 318 $event->courseid = $quiz->course;
319 $event->groupid = 0;
320 $event->userid = 0;
321 $event->modulename = 'quiz';
322 $event->instance = $quiz->id;
ba288539 323 $event->visible = instance_is_visible('quiz', $quiz);
d2f308c0 324 $event->timestart = $quiz->timeopen;
b2a3cd2d 325 $event->eventtype = 'open';
d2f308c0 326 $event->timeduration = ($quiz->timeclose - $quiz->timeopen);
d2f308c0 327
b2a3cd2d 328 if ($event->timeduration > QUIZ_MAX_EVENT_LENGTH) { /// Set up two events
d2f308c0 329
b2a3cd2d 330 $event2 = $event;
d2f308c0 331
b2a3cd2d 332 $event->name = addslashes($quiz->name).' ('.get_string('quizopens', 'quiz').')';
333 $event->timeduration = 0;
334
335 $event2->name = addslashes($quiz->name).' ('.get_string('quizcloses', 'quiz').')';
336 $event2->timestart = $quiz->timeclose;
337 $event2->eventtype = 'close';
338 $event2->timeduration = 0;
339
340 if (empty($event2old->id)) {
341 unset($event2->id);
342 add_event($event2);
343 } else {
344 $event2->id = $event2old->id;
345 update_event($event2);
346 }
347 } else if (!empty($event2->id)) {
348 delete_event($event2->id);
349 }
350
351 if (empty($event->id)) {
d2f308c0 352 add_event($event);
b2a3cd2d 353 } else {
354 update_event($event);
d2f308c0 355 }
b2a3cd2d 356
d2f308c0 357 }
358 return true;
359}
360
6710ec87 361
362function quiz_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $quiz="0", $user="", $groupid="") {
363// Returns all quizzes since a given time. If quiz is specified then
364// this restricts the results
365
366 global $CFG;
367
368 if ($quiz) {
369 $quizselect = " AND cm.id = '$quiz'";
370 } else {
371 $quizselect = "";
372 }
373 if ($user) {
374 $userselect = " AND u.id = '$user'";
375 } else {
376 $userselect = "";
377 }
378
379 $quizzes = get_records_sql("SELECT qa.*, q.name, u.firstname, u.lastname, u.picture,
380 q.course, q.sumgrades as maxgrade, cm.instance, cm.section
381 FROM {$CFG->prefix}quiz_attempts qa,
382 {$CFG->prefix}quiz q,
383 {$CFG->prefix}user u,
384 {$CFG->prefix}course_modules cm
385 WHERE qa.timefinish > '$sincetime'
386 AND qa.userid = u.id $userselect
387 AND qa.quiz = q.id $quizselect
388 AND cm.instance = q.id
389 AND cm.course = '$courseid'
390 AND q.course = cm.course
391 ORDER BY qa.timefinish ASC");
392
393 if (empty($quizzes))
394 return;
395
396 foreach ($quizzes as $quiz) {
397 if (empty($groupid) || ismember($groupid, $quiz->userid)) {
398
399 $tmpactivity->type = "quiz";
400 $tmpactivity->defaultindex = $index;
401 $tmpactivity->instance = $quiz->quiz;
402
403 $tmpactivity->name = $quiz->name;
404 $tmpactivity->section = $quiz->section;
405
406 $tmpactivity->content->attemptid = $quiz->id;
407 $tmpactivity->content->sumgrades = $quiz->sumgrades;
408 $tmpactivity->content->maxgrade = $quiz->maxgrade;
409 $tmpactivity->content->attempt = $quiz->attempt;
410
411 $tmpactivity->user->userid = $quiz->userid;
412 $tmpactivity->user->fullname = fullname($quiz);
413 $tmpactivity->user->picture = $quiz->picture;
414
415 $tmpactivity->timestamp = $quiz->timefinish;
416
417 $activities[] = $tmpactivity;
418
419 $index++;
420 }
421 }
422
423 return;
424}
425
426
427function quiz_print_recent_mod_activity($activity, $course, $detail=false) {
6eaae5bd 428 global $CFG;
6710ec87 429
430 echo '<table border="0" cellpadding="3" cellspacing="0">';
431
6f797013 432 echo "<tr><td class=\"forumpostpicture\" width=\"35\" valign=\"top\">";
6710ec87 433 print_user_picture($activity->user->userid, $course, $activity->user->picture);
434 echo "</td><td width=\"100%\"><font size=\"2\">";
435
436 if ($detail) {
437 echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
438 "height=\"16\" width=\"16\" alt=\"$activity->type\" /> ";
439 echo "<a href=\"$CFG->wwwroot/mod/quiz/view.php?id=" . $activity->instance . "\">"
440 . $activity->name . "</a> - ";
441
442 }
443
444 if (isteacher($course)) {
445 $grades = "(" . $activity->content->sumgrades . " / " . $activity->content->maxgrade . ") ";
446 echo "<a href=\"$CFG->wwwroot/mod/quiz/review.php?q="
447 . $activity->instance . "&amp;attempt="
448 . $activity->content->attemptid . "\">" . $grades . "</a> ";
449
450 echo get_string("attempt", "quiz") . " - " . $activity->content->attempt . "<br />";
451 }
452 echo "<a href=\"$CFG->wwwroot/user/view.php?id="
453 . $activity->user->userid . "&amp;course=$course\">"
454 . $activity->user->fullname . "</a> ";
455
456 echo " - " . userdate($activity->timestamp);
457
458 echo "</font></td></tr>";
459 echo "</table>";
460
461 return;
462}
463
730fd187 464?>