MDL-29030 move user stats reporting to report_stats plugin
[moodle.git] / course / user.php
CommitLineData
d9cb06dc 1<?php
d9cb06dc 2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Display user activity reports for a course
19 *
20 * @copyright 1999 Martin Dougiamas http://dougiamas.com
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 * @package course
23 */
24
25require_once("../config.php");
26require_once("lib.php");
516c5eca 27require_once($CFG->libdir.'/completionlib.php');
d9cb06dc 28
29$id = required_param('id',PARAM_INT); // course id
30$user = required_param('user',PARAM_INT); // user id
31$mode = optional_param('mode', "todaylogs", PARAM_ALPHA);
32$page = optional_param('page', 0, PARAM_INT);
33$perpage = optional_param('perpage', 100, PARAM_INT);
34
a6855934 35$url = new moodle_url('/course/user.php', array('id'=>$id,'user'=>$user, 'mode'=>$mode));
d9cb06dc 36if ($page !== 0) {
37 $url->param('page', $page);
38}
39if ($perpage !== 100) {
40 $url->param('perpage', $perpage);
41}
42$PAGE->set_url($url);
f9903ed0 43
d9cb06dc 44if (!$course = $DB->get_record('course', array('id'=>$id))) {
45 print_error('invalidcourseid', 'error');
46}
5bf04b36 47
d9cb06dc 48if (! $user = $DB->get_record("user", array("id"=>$user))) {
49 print_error('invaliduserid', 'error');
50}
5bf04b36 51
4d00fded
PS
52if ($mode === 'outline' or $mode === 'complete') {
53 $url = new moodle_url('/report/outline/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$mode));
54 redirect($url);
fad8e024
PS
55
56} else if ($mode === 'todaylogs' or $mode === 'alllogs') {
57 $logmode = ($mode === 'todaylogs') ? 'today' : 'all';
58 $url = new moodle_url('/report/log/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$logmode));
59 redirect($url);
beda8fa8
PS
60} else if ($mode === 'stats') {
61 $url = new moodle_url('/report/stats/user.php', array('id'=>$user->id, 'course'=>$course->id));
62 redirect($url);
4d00fded
PS
63}
64
beda8fa8 65
d9cb06dc 66require_login();
67$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
68$personalcontext = get_context_instance(CONTEXT_USER, $user->id);
358e2a0b 69
df997f84 70require_login();
87c215de 71$PAGE->set_pagelayout('admin');
df997f84 72if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and !is_enrolled($coursecontext)) {
d9cb06dc 73 // do not require parents to be enrolled in courses ;-)
74 $PAGE->set_course($course);
75} else {
76 require_login($course);
77}
6bb91986 78
d9cb06dc 79if ($user->deleted) {
80 echo $OUTPUT->header();
81 echo $OUTPUT->heading(get_string('userdeleted'));
82 echo $OUTPUT->footer();
83 die;
84}
f5fc83e8 85
ba50bd3d
PS
86//TODO: all this is a hack - we can not link to plugins like this - all this must be abstracted to plugin callbacks!
87
d9cb06dc 88// prepare list of allowed modes
89$myreports = ($course->showreports and $USER->id == $user->id);
90$anyreport = has_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
358e2a0b 91
d9cb06dc 92$modes = array();
358e2a0b 93
d9cb06dc 94if (has_capability('moodle/grade:viewall', $coursecontext)) {
95 //ok - can view all course grades
96 $modes[] = 'grade';
358e2a0b 97
d9cb06dc 98} else if ($course->showgrades and $user->id == $USER->id and has_capability('moodle/grade:view', $coursecontext)) {
99 //ok - can view own grades
100 $modes[] = 'grade';
358e2a0b 101
d9cb06dc 102} else if ($course->showgrades and has_capability('moodle/grade:viewall', $personalcontext)) {
103 // ok - can view grades of this user - parent most probably
104 $modes[] = 'grade';
3c7da16d 105
d9cb06dc 106} else if ($course->showgrades and $anyreport) {
107 // ok - can view grades of this user - parent most probably
108 $modes[] = 'grade';
109}
358e2a0b 110
2be4d090
MD
111// Course completion tab
112if (!empty($CFG->enablecompletion) && ($course->id == SITEID || !empty($course->enablecompletion)) && // completion enabled
ba50bd3d 113 ($myreports || $anyreport || ($course->id == SITEID || has_capability('report/completion:view', $coursecontext)))) { // permissions to view the report
2be4d090
MD
114
115 // Decide if singular or plural
116 if ($course->id == SITEID) {
117 $modes[] = 'coursecompletions';
118 } else {
119 $modes[] = 'coursecompletion';
120 }
121}
122
123
d9cb06dc 124if (empty($modes)) {
125 require_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
126}
358e2a0b 127
d9cb06dc 128if (!in_array($mode, $modes)) {
7e85563d 129 // forbidden or non-existent mode
d9cb06dc 130 $mode = reset($modes);
131}
85a6e76c 132
d9cb06dc 133add_to_log($course->id, "course", "user report", "user.php?id=$course->id&amp;user=$user->id&amp;mode=$mode", "$user->id");
f9903ed0 134
d9cb06dc 135$stractivityreport = get_string("activityreport");
136$strparticipants = get_string("participants");
d9cb06dc 137$strcomplete = get_string("complete");
d9cb06dc 138$strmode = get_string($mode);
139$fullname = fullname($user, true);
a2ab3b05 140
d9cb06dc 141$link = null;
142if ($course->id != SITEID && has_capability('moodle/course:viewparticipants', $coursecontext)) {
a6855934 143 $link = new moodle_url('/user/index.php', array('id'=>$course->id));
d9cb06dc 144}
7a7e209d
SH
145
146$PAGE->navigation->extend_for_user($user);
870815fa 147$PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed.
d9cb06dc 148$PAGE->set_title("$course->shortname: $stractivityreport ($mode)");
149$PAGE->set_heading($course->fullname);
150echo $OUTPUT->header();
f9a0ea69 151
d9cb06dc 152switch ($mode) {
153 case "grade":
154 if (empty($CFG->grade_profilereport) or !file_exists($CFG->dirroot.'/grade/report/'.$CFG->grade_profilereport.'/lib.php')) {
155 $CFG->grade_profilereport = 'user';
156 }
157 require_once $CFG->libdir.'/gradelib.php';
158 require_once $CFG->dirroot.'/grade/lib.php';
159 require_once $CFG->dirroot.'/grade/report/'.$CFG->grade_profilereport.'/lib.php';
160
161 $functionname = 'grade_report_'.$CFG->grade_profilereport.'_profilereport';
162 if (function_exists($functionname)) {
163 $functionname($course, $user);
164 }
165 break;
166
2be4d090
MD
167 case "coursecompletion":
168 case "coursecompletions":
169
170 // Display course completion user report
2be4d090
MD
171
172 // Grab all courses the user is enrolled in and their completion status
173 $sql = "
174 SELECT DISTINCT
175 c.id AS id
176 FROM
ffe50258 177 {course} c
2be4d090 178 INNER JOIN
ffe50258 179 {context} con
2be4d090 180 ON con.instanceid = c.id
b64a827c 181 INNER JOIN
182 {role_assignments} ra
183 ON ra.contextid = con.id
1c6ed505 184 INNER JOIN
185 {enrol} e
186 ON c.id = e.courseid
187 INNER JOIN
188 {user_enrolments} ue
b64a827c 189 ON e.id = ue.enrolid AND ra.userid = ue.userid
2be4d090
MD
190 AND ra.userid = {$user->id}
191 ";
192
193 // Get roles that are tracked by course completion
1c6ed505 194 if ($roles = $CFG->gradebookroles) {
2be4d090
MD
195 $sql .= '
196 AND ra.roleid IN ('.$roles.')
197 ';
198 }
199
200 $sql .= '
201 WHERE
202 con.contextlevel = '.CONTEXT_COURSE.'
203 AND c.enablecompletion = 1
204 ';
205
206
207 // If we are looking at a specific course
208 if ($course->id != 1) {
209 $sql .= '
210 AND c.id = '.(int)$course->id.'
211 ';
212 }
213
214 // Check if result is empty
a0c3fc63
EL
215 $rs = $DB->get_recordset_sql($sql);
216 if (!$rs->valid()) {
2be4d090
MD
217
218 if ($course->id != 1) {
ba50bd3d 219 $error = get_string('nocompletions', 'report_completion');
2be4d090 220 } else {
ba50bd3d 221 $error = get_string('nocompletioncoursesenroled', 'report_completion');
2be4d090
MD
222 }
223
224 echo $OUTPUT->notification($error);
a0c3fc63 225 $rs->close(); // not going to loop (but break), close rs
2be4d090
MD
226 break;
227 }
228
229 // Categorize courses by their status
230 $courses = array(
231 'inprogress' => array(),
232 'complete' => array(),
233 'unstarted' => array()
234 );
235
236 // Sort courses by the user's status in each
237 foreach ($rs as $course_completion) {
238 $c_info = new completion_info((object)$course_completion);
239
240 // Is course complete?
241 $coursecomplete = $c_info->is_course_complete($user->id);
242
243 // Has this user completed any criteria?
244 $criteriacomplete = $c_info->count_course_user_data($user->id);
245
246 if ($coursecomplete) {
247 $courses['complete'][] = $c_info;
248 } else if ($criteriacomplete) {
249 $courses['inprogress'][] = $c_info;
250 } else {
251 $courses['unstarted'][] = $c_info;
252 }
253 }
a0c3fc63 254 $rs->close(); // after loop, close rs
2be4d090
MD
255
256 // Loop through course status groups
257 foreach ($courses as $type => $infos) {
258
259 // If there are courses with this status
260 if (!empty($infos)) {
261
ba50bd3d 262 echo '<h1 align="center">'.get_string($type, 'report_completion').'</h1>';
2be4d090
MD
263 echo '<table class="generalbox boxaligncenter">';
264 echo '<tr class="ccheader">';
265 echo '<th class="c0 header" scope="col">'.get_string('course').'</th>';
266 echo '<th class="c1 header" scope="col">'.get_string('requiredcriteria', 'completion').'</th>';
267 echo '<th class="c2 header" scope="col">'.get_string('status').'</th>';
268 echo '<th class="c3 header" scope="col" width="15%">'.get_string('info').'</th>';
269
270 if ($type === 'complete') {
ba50bd3d 271 echo '<th class="c4 header" scope="col">'.get_string('completiondate', 'report_completion').'</th>';
2be4d090
MD
272 }
273
274 echo '</tr>';
275
276 // For each course
277 foreach ($infos as $c_info) {
278
279 // Get course info
ffe50258 280 $c_course = $DB->get_record('course', array('id' => $c_info->course_id));
91d284c1
SH
281 $course_context = get_context_instance(CONTEXT_COURSE, $c_course->id, MUST_EXIST);
282 $course_name = format_string($c_course->fullname, true, array('context' => $course_context));
2be4d090
MD
283
284 // Get completions
285 $completions = $c_info->get_completions($user->id);
286
287 // Save row data
288 $rows = array();
289
290 // For aggregating activity completion
291 $activities = array();
292 $activities_complete = 0;
7e85563d 293
2be4d090
MD
294 // For aggregating prerequisites
295 $prerequisites = array();
296 $prerequisites_complete = 0;
297
298 // Loop through course criteria
299 foreach ($completions as $completion) {
300 $criteria = $completion->get_criteria();
301 $complete = $completion->is_complete();
302
303 // Activities are a special case, so cache them and leave them till last
304 if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
305 $activities[$criteria->moduleinstance] = $complete;
306
307 if ($complete) {
308 $activities_complete++;
309 }
310
311 continue;
312 }
313
314 // Prerequisites are also a special case, so cache them and leave them till last
315 if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
316 $prerequisites[$criteria->courseinstance] = $complete;
317
318 if ($complete) {
319 $prerequisites_complete++;
320 }
321
322 continue;
323 }
324
325 $row = array();
326 $row['title'] = $criteria->get_title();
327 $row['status'] = $completion->get_status();
328 $rows[] = $row;
329 }
330
331 // Aggregate activities
332 if (!empty($activities)) {
333
334 $row = array();
ba50bd3d 335 $row['title'] = get_string('activitiescomplete', 'report_completion');
2be4d090
MD
336 $row['status'] = $activities_complete.' of '.count($activities);
337 $rows[] = $row;
338 }
339
340 // Aggregate prerequisites
341 if (!empty($prerequisites)) {
342
343 $row = array();
344 $row['title'] = get_string('prerequisitescompleted', 'completion');
345 $row['status'] = $prerequisites_complete.' of '.count($prerequisites);
346 array_splice($rows, 0, 0, array($row));
347 }
348
349 $first_row = true;
350
351 // Print table
352 foreach ($rows as $row) {
353
354 // Display course name on first row
355 if ($first_row) {
91d284c1 356 echo '<tr><td class="c0"><a href="'.$CFG->wwwroot.'/course/view.php?id='.$c_course->id.'">'.$course_name.'</a></td>';
2be4d090
MD
357 } else {
358 echo '<tr><td class="c0"></td>';
359 }
360
361 echo '<td class="c1">';
362 echo $row['title'];
363 echo '</td><td class="c2">';
364
365 switch ($row['status']) {
366 case 'Yes':
367 echo get_string('complete');
368 break;
369
370 case 'No':
ba50bd3d 371 echo get_string('incomplete', 'report_completion');
2be4d090
MD
372 break;
373
374 default:
375 echo $row['status'];
376 }
377
378 // Display link on first row
379 echo '</td><td class="c3">';
380 if ($first_row) {
ba50bd3d 381 echo '<a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$c_course->id.'&user='.$user->id.'">'.get_string('detailedview', 'report_completion').'</a>';
2be4d090
MD
382 }
383 echo '</td>';
384
385 // Display completion date for completed courses on first row
386 if ($type === 'complete' && $first_row) {
387 $params = array(
388 'userid' => $user->id,
389 'course' => $c_course->id
390 );
391
392 $ccompletion = new completion_completion($params);
393 echo '<td class="c4">'.userdate($ccompletion->timecompleted, '%e %B %G').'</td>';
394 }
395
396 $first_row = false;
397 echo '</tr>';
398 }
399 }
400
401 echo '</table>';
402 }
403
404 }
405
d9cb06dc 406 break;
407 default:
408 // can not be reached ;-)
409}
f9903ed0 410
7468bf01 411
d9cb06dc 412echo $OUTPUT->footer();