blocks/quiz_results: MDL-21284 - dont strip leading zeros from idnumbers
[moodle.git] / blocks / quiz_results / block_quiz_results.php
1 <?php
3 define('B_QUIZRESULTS_NAME_FORMAT_FULL', 1);
4 define('B_QUIZRESULTS_NAME_FORMAT_ID',   2);
5 define('B_QUIZRESULTS_NAME_FORMAT_ANON', 3);
6 define('B_QUIZRESULTS_GRADE_FORMAT_PCT', 1);
7 define('B_QUIZRESULTS_GRADE_FORMAT_FRA', 2);
8 define('B_QUIZRESULTS_GRADE_FORMAT_ABS', 3);
10 class block_quiz_results extends block_base {
11     function init() {
12         $this->title = get_string('formaltitle', 'block_quiz_results');
13         $this->version = 2007101509;
14     }
16     function applicable_formats() {
17         return array('course' => true, 'mod-quiz' => true);
18     }
20     /**
21      * If this block belongs to a quiz context, then return that quiz's id.
22      * Otherwise, return 0.
23      * @return integer the quiz id.
24      */
25     public function get_owning_quiz() {
26         if (empty($this->instance->parentcontextid)) {
27             return 0;
28         }
29         $parentcontext = get_context_instance_by_id($this->instance->parentcontextid);
30         if ($parentcontext->contextlevel != CONTEXT_MODULE) {
31             return 0;
32         }
33         $cm = get_coursemodule_from_id('quiz', $parentcontext->instanceid);
34         if (!$cm) {
35             return 0;
36         }
37         return $cm->instance;
38     }
40     function instance_config_save($data) {
41         if (empty($data->quizid)) {
42             $data->quizid = $this->get_owning_quiz();
43         }
44         parent::instance_config_save($data);
45     }
47     function get_content() {
48         global $USER, $CFG, $DB;
50         if ($this->content !== NULL) {
51             return $this->content;
52         }
54         $this->content = new stdClass;
55         $this->content->text = '';
56         $this->content->footer = '';
58         if (empty($this->instance)) {
59             return $this->content;
60         }
62         if ($this->page->activityname == 'quiz' && $this->page->context->id == $this->instance->parentcontextid) {
63             $quiz = $this->page->activityrecord;
64             $quizid = $quiz->id;
65             $courseid = $this->page->course->id;
66             $inquiz = true;
67         } else if (!empty($this->config->quizid)) {
68             $quizid = $this->config->quizid;
69             $quiz = $DB->get_record('quiz', array('id' => $quizid));
70             if (empty($quiz)) {
71                 $this->content->text = get_string('error_emptyquizrecord', 'block_quiz_results');
72                 return $this->content;
73             }
74             $courseid = $quiz->course;
75             $inquiz = false;
76         } else {
77             $quizid = 0;
78         }
80         if (empty($quizid)) {
81             $this->content->text = get_string('error_emptyquizid', 'block_quiz_results');
82             return $this->content;
83         }
85         if (empty($this->config->showbest) && empty($this->config->showworst)) {
86             $this->content->text = get_string('configuredtoshownothing', 'block_quiz_results');
87             return $this->content;
88         }
90         // Get the grades for this quiz
91         $grades = $DB->get_records('quiz_grades', array('quiz' => $quizid), 'grade, timemodified DESC');
93         if (empty($grades)) {
94             // No grades, sorry
95             // The block will hide itself in this case
96             return $this->content;
97         }
99         $groupmode = NOGROUPS;
100         $best      = array();
101         $worst     = array();
103         if (!empty($this->config->nameformat)) {
104             $nameformat = $this->config->nameformat;
105         } else {
106             $nameformat = B_QUIZRESULTS_NAME_FORMAT_FULL;
107         }
109         if (!empty($this->config->usegroups)) {
110             if ($inquiz) {
111                 $cm = $this->page->cm;
112                 $context = $this->page->context;
113             } else {
114                 $cm = get_coursemodule_from_instance('quiz', $quizid, $courseid);
115                 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
116             }
117             $groupmode = groups_get_activity_groupmode($cm);
119             if ($groupmode == SEPARATEGROUPS && has_capability('moodle/site:accessallgroups', $context)) {
120                 // We 'll make an exception in this case
121                 $groupmode = VISIBLEGROUPS;
122             }
123         }
125         switch ($groupmode) {
126             case VISIBLEGROUPS:
127             // Display group-mode results
128             $groups = groups_get_all_groups($courseid);
130             if(empty($groups)) {
131                 // No groups exist, sorry
132                 $this->content->text = get_string('error_nogroupsexist', 'block_quiz_results');
133                 return $this->content;
134             }
136             // Find out all the userids which have a submitted grade
137             $userids = array();
138             $gradeforuser = array();
139             foreach ($grades as $grade) {
140                 $userids[] = $grade->userid;
141                 $gradeforuser[$grade->userid] = (float)$grade->grade;
142             }
144             // Now find which groups these users belong in
145             list($usertest, $params) = $DB->get_in_or_equal($userids);
146             $params[] = $courseid;
147             $usergroups = $DB->get_records_sql('
148                     SELECT gm.id, gm.userid, gm.groupid, g.name
149                     FROM {groups} g
150                     LEFT JOIN {groups_members} gm ON g.id = gm.groupid
151                     WHERE gm.userid ' . $usertest . ' AND g.courseid = ?', $params);
153             // Now, iterate the grades again and sum them up for each group
154             $groupgrades = array();
155             foreach ($usergroups as $usergroup) {
156                 if (!isset($groupgrades[$usergroup->groupid])) {
157                     $groupgrades[$usergroup->groupid] = array(
158                             'sum' => (float)$gradeforuser[$usergroup->userid],
159                             'number' => 1,
160                             'group' => $usergroup->name);
161                 } else {
162                     $groupgrades[$usergroup->groupid]['sum'] += $gradeforuser[$usergroup->userid];
163                     $groupgrades[$usergroup->groupid]['number'] += 1;
164                 }
165             }
167             foreach($groupgrades as $groupid => $groupgrade) {
168                 $groupgrades[$groupid]['average'] = $groupgrades[$groupid]['sum'] / $groupgrades[$groupid]['number'];
169             }
171             // Sort groupgrades according to average grade, ascending
172             uasort($groupgrades, create_function('$a, $b', 'if($a["average"] == $b["average"]) return 0; return ($a["average"] > $b["average"] ? 1 : -1);'));
174             // How many groups do we have with graded member submissions to show?
175             $numbest  = empty($this->config->showbest) ? 0 : min($this->config->showbest, count($groupgrades));
176             $numworst = empty($this->config->showworst) ? 0 : min($this->config->showworst, count($groupgrades) - $numbest);
178             // Collect all the group results we are going to use in $best and $worst
179             $remaining = $numbest;
180             $groupgrade = end($groupgrades);
181             while ($remaining--) {
182                 $best[key($groupgrades)] = $groupgrade['average'];
183                 $groupgrade = prev($groupgrades);
184             }
186             $remaining = $numworst;
187             $groupgrade = reset($groupgrades);
188             while ($remaining--) {
189                 $worst[key($groupgrades)] = $groupgrade['average'];
190                 $groupgrade = next($groupgrades);
191             }
193             // Ready for output!
194             $gradeformat = intval(empty($this->config->gradeformat) ? B_QUIZRESULTS_GRADE_FORMAT_PCT : $this->config->gradeformat);
196             if (!$inquiz) {
197                 // Don't show header and link to the quiz if we ARE at the quiz...
198                 $this->content->text .= '<h1><a href="'.$CFG->wwwroot.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name.'</a></h1>';
199             }
201             if ($nameformat = B_QUIZRESULTS_NAME_FORMAT_FULL) {
202                 if (has_capability('moodle/course:managegroups', $context)) {
203                     $grouplink = $CFG->wwwroot.'/group/overview.php?id='.$courseid.'&amp;group=';
204                 } else if (has_capability('moodle/course:viewparticipants', $context)) {
205                     $grouplink = $CFG->wwwroot.'/user/index.php?id='.$courseid.'&amp;group=';
206                 } else {
207                     $grouplink = '';
208                 }
209             }
211             $rank = 0;
212             if(!empty($best)) {
213                 $this->content->text .= '<table class="grades"><caption>';
214                 $this->content->text .= ($numbest == 1?get_string('bestgroupgrade', 'block_quiz_results'):get_string('bestgroupgrades', 'block_quiz_results', $numbest));
215                 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
216                 foreach($best as $groupid => $averagegrade) {
217                     switch($nameformat) {
218                         case B_QUIZRESULTS_NAME_FORMAT_ANON:
219                         case B_QUIZRESULTS_NAME_FORMAT_ID:
220                             $thisname = get_string('group');
221                         break;
222                         default:
223                         case B_QUIZRESULTS_NAME_FORMAT_FULL:
224                             if ($grouplink) {
225                                 $thisname = '<a href="'.$grouplink.$groupid.'">'.$groupgrades[$groupid]['group'].'</a>';
226                             } else {
227                                 $thisname = $groupgrades[$groupid]['group'];
228                             }
229                         break;
230                     }
231                     $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
232                     switch($gradeformat) {
233                         case B_QUIZRESULTS_GRADE_FORMAT_FRA:
234                             $this->content->text .= quiz_format_grade($quiz, $averagegrade).'/'.$quiz->grade;
235                         break;
236                         case B_QUIZRESULTS_GRADE_FORMAT_ABS:
237                             $this->content->text .= quiz_format_grade($quiz, $averagegrade);
238                         break;
239                         default:
240                         case B_QUIZRESULTS_GRADE_FORMAT_PCT:
241                             $this->content->text .= round((float)$averagegrade / (float)$quiz->grade * 100).'%';
242                         break;
243                     }
244                     $this->content->text .= '</td></tr>';
245                 }
246                 $this->content->text .= '</tbody></table>';
247             }
249             $rank = 0;
250             if(!empty($worst)) {
251                 $worst = array_reverse($worst, true);
252                 $this->content->text .= '<table class="grades"><caption>';
253                 $this->content->text .= ($numworst == 1?get_string('worstgroupgrade', 'block_quiz_results'):get_string('worstgroupgrades', 'block_quiz_results', $numworst));
254                 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
255                 foreach($worst as $groupid => $averagegrade) {
256                     switch($nameformat) {
257                         case B_QUIZRESULTS_NAME_FORMAT_ANON:
258                         case B_QUIZRESULTS_NAME_FORMAT_ID:
259                             $thisname = get_string('group');
260                         break;
261                         default:
262                         case B_QUIZRESULTS_NAME_FORMAT_FULL:
263                             $thisname = '<a href="'.$CFG->wwwroot.'/course/group.php?group='.$groupid.'&amp;id='.$courseid.'">'.$groupgrades[$groupid]['group'].'</a>';
264                         break;
265                     }
266                     $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
267                     switch($gradeformat) {
268                         case B_QUIZRESULTS_GRADE_FORMAT_FRA:
269                             $this->content->text .= quiz_format_grade($quiz, $averagegrade).'/'.$quiz->grade;
270                         break;
271                         case B_QUIZRESULTS_GRADE_FORMAT_ABS:
272                             $this->content->text .= quiz_format_grade($quiz, $averagegrade);
273                         break;
274                         default:
275                         case B_QUIZRESULTS_GRADE_FORMAT_PCT:
276                             $this->content->text .= round((float)$averagegrade / (float)$quiz->grade * 100).'%';
277                         break;
278                     }
279                     $this->content->text .= '</td></tr>';
280                 }
281                 $this->content->text .= '</tbody></table>';
282             }
283             break;
286             case SEPARATEGROUPS:
287             // This is going to be just like no-groups mode, only we 'll filter
288             // out the grades from people not in our group.
289             if(empty($USER) || empty($USER->id)) {
290                 // Not logged in, so show nothing
291                 return $this->content;
292             }
294             $mygroups = groups_get_all_groups($courseid, $USER->id);
295             if(empty($mygroups)) {
296                 // Not member of a group, show nothing
297                 return $this->content;
298             }
300             // Get users from the same groups as me.
301             list($grouptest, $params) = $DB->get_in_or_equal(array_keys($mygroups));
302             $mygroupsusers = $DB->get_records_sql_menu(
303                     'SELECT DISTINCT userid, 1 FROM {groups_members} WHERE groupid ' . $grouptest,
304                     $params);
306             // Filter out the grades belonging to other users, and proceed as if there were no groups
307             foreach ($grades as $key => $grade) {
308                 if (!isset($mygroupsusers[$grade->userid])) {
309                     unset($grades[$key]);
310                 }
311             }
313             // No break, fall through to the default case now we have filtered the $grades array.
314             default:
315             case NOGROUPS:
316             // Single user mode
317             $numbest  = empty($this->config->showbest) ? 0 : min($this->config->showbest, count($grades));
318             $numworst = empty($this->config->showworst) ? 0 : min($this->config->showworst, count($grades) - $numbest);
320             // Collect all the usernames we are going to need
321             $remaining = $numbest;
322             $grade = end($grades);
323             while($remaining--) {
324                 $best[$grade->userid] = $grade->id;
325                 $grade = prev($grades);
326             }
328             $remaining = $numworst;
329             $grade = reset($grades);
330             while($remaining--) {
331                 $worst[$grade->userid] = $grade->id;
332                 $grade = next($grades);
333             }
335             if(empty($best) && empty($worst)) {
336                 // Nothing to show, for some reason...
337                 return $this->content;
338             }
340             // Now grab all the users from the database
341             $userids = array_merge(array_keys($best), array_keys($worst));
342             $users = $DB->get_records_list('user', 'id', $userids, '', 'id, firstname, lastname, idnumber');
344             // Ready for output!
346             $gradeformat = intval(empty($this->config->gradeformat) ? B_QUIZRESULTS_GRADE_FORMAT_PCT : $this->config->gradeformat);
348             if(!$inquiz) {
349                 // Don't show header and link to the quiz if we ARE at the quiz...
350                 $this->content->text .= '<h1><a href="'.$CFG->wwwroot.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name.'</a></h1>';
351             }
353             $rank = 0;
354             if(!empty($best)) {
355                 $this->content->text .= '<table class="grades"><caption>';
356                 $this->content->text .= ($numbest == 1?get_string('bestgrade', 'block_quiz_results'):get_string('bestgrades', 'block_quiz_results', $numbest));
357                 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
358                 foreach($best as $userid => $gradeid) {
359                     switch($nameformat) {
360                         case B_QUIZRESULTS_NAME_FORMAT_ID:
361                             $thisname = get_string('user').' '.$users[$userid]->idnumber;
362                         break;
363                         case B_QUIZRESULTS_NAME_FORMAT_ANON:
364                             $thisname = get_string('user');
365                         break;
366                         default:
367                         case B_QUIZRESULTS_NAME_FORMAT_FULL:
368                             $thisname = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userid.'&amp;course='.$courseid.'">'.fullname($users[$userid]).'</a>';
369                         break;
370                     }
371                     $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
372                     switch($gradeformat) {
373                         case B_QUIZRESULTS_GRADE_FORMAT_FRA:
374                             $this->content->text .=  quiz_format_grade($quiz, $grades[$gradeid]->grade).'/'.$quiz->grade;
375                         break;
376                         case B_QUIZRESULTS_GRADE_FORMAT_ABS:
377                             $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade);
378                         break;
379                         default:
380                         case B_QUIZRESULTS_GRADE_FORMAT_PCT:
381                             if ($quiz->grade) {
382                                 $this->content->text .= round((float)$grades[$gradeid]->grade / (float)$quiz->grade * 100).'%';
383                             } else {
384                                 $this->content->text .= '--%';
385                             }
386                         break;
387                     }
388                     $this->content->text .= '</td></tr>';
389                 }
390                 $this->content->text .= '</tbody></table>';
391             }
393             $rank = 0;
394             if(!empty($worst)) {
395                 $worst = array_reverse($worst, true);
396                 $this->content->text .= '<table class="grades"><caption>';
397                 $this->content->text .= ($numworst == 1?get_string('worstgrade', 'block_quiz_results'):get_string('worstgrades', 'block_quiz_results', $numworst));
398                 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
399                 foreach($worst as $userid => $gradeid) {
400                     switch($nameformat) {
401                         case B_QUIZRESULTS_NAME_FORMAT_ID:
402                             $thisname = get_string('user').' '.$users[$userid]->idnumber;
403                         break;
404                         case B_QUIZRESULTS_NAME_FORMAT_ANON:
405                             $thisname = get_string('user');
406                         break;
407                         default:
408                         case B_QUIZRESULTS_NAME_FORMAT_FULL:
409                             $thisname = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userid.'&amp;course='.$courseid.'">'.fullname($users[$userid]).'</a>';
410                         break;
411                     }
412                     $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
413                     switch($gradeformat) {
414                         case B_QUIZRESULTS_GRADE_FORMAT_FRA:
415                             $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade).'/'.$quiz->grade;
416                         break;
417                         case B_QUIZRESULTS_GRADE_FORMAT_ABS:
418                             $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade);
419                         break;
420                         default:
421                         case B_QUIZRESULTS_GRADE_FORMAT_PCT:
422                             $this->content->text .= round((float)$grades[$gradeid]->grade / (float)$quiz->grade * 100).'%';
423                         break;
424                     }
425                     $this->content->text .= '</td></tr>';
426                 }
427                 $this->content->text .= '</tbody></table>';
428             }
429             break;
430         }
432         return $this->content;
433     }
435     function instance_allow_multiple() {
436         return true;
437     }