Commit | Line | Data |
---|---|---|
aa6c1ced | 1 | <?php |
e20de262 PS |
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 | * Participation report | |
19 | * | |
20 | * @package report | |
21 | * @subpackage participation | |
22 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
24 | */ | |
25 | ||
26 | require('../../config.php'); | |
27 | require_once($CFG->dirroot.'/lib/tablelib.php'); | |
58cc51cd | 28 | require_once($CFG->dirroot.'/report/participation/locallib.php'); |
e20de262 PS |
29 | |
30 | define('DEFAULT_PAGE_SIZE', 20); | |
31 | define('SHOW_ALL_PAGE_SIZE', 5000); | |
32 | ||
33 | $id = required_param('id', PARAM_INT); // course id. | |
34 | $roleid = optional_param('roleid', 0, PARAM_INT); // which role to show | |
35 | $instanceid = optional_param('instanceid', 0, PARAM_INT); // instance we're looking at. | |
36 | $timefrom = optional_param('timefrom', 0, PARAM_INT); // how far back to look... | |
37 | $action = optional_param('action', '', PARAM_ALPHA); | |
38 | $page = optional_param('page', 0, PARAM_INT); // which page to show | |
39 | $perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); // how many per page | |
d5ae34a5 | 40 | $currentgroup = optional_param('group', null, PARAM_INT); // Get the active group. |
e20de262 PS |
41 | |
42 | $url = new moodle_url('/report/participation/index.php', array('id'=>$id)); | |
43 | if ($roleid !== 0) $url->param('roleid'); | |
44 | if ($instanceid !== 0) $url->param('instanceid'); | |
45 | if ($timefrom !== 0) $url->param('timefrom'); | |
46 | if ($action !== '') $url->param('action'); | |
47 | if ($page !== 0) $url->param('page'); | |
48 | if ($perpage !== DEFAULT_PAGE_SIZE) $url->param('perpage'); | |
49 | $PAGE->set_url($url); | |
50 | $PAGE->set_pagelayout('admin'); | |
51 | ||
52 | if ($action != 'view' and $action != 'post') { | |
53 | $action = ''; // default to all (don't restrict) | |
54 | } | |
55 | ||
56 | if (!$course = $DB->get_record('course', array('id'=>$id))) { | |
57 | print_error('invalidcourse'); | |
58 | } | |
59 | ||
60 | if ($roleid != 0 and !$role = $DB->get_record('role', array('id'=>$roleid))) { | |
61 | print_error('invalidrole'); | |
62 | } | |
63 | ||
64 | require_login($course); | |
21c08c63 | 65 | $context = context_course::instance($course->id); |
e20de262 PS |
66 | require_capability('report/participation:view', $context); |
67 | ||
e20de262 PS |
68 | $strparticipation = get_string('participationreport'); |
69 | $strviews = get_string('views'); | |
70 | $strposts = get_string('posts'); | |
e20de262 PS |
71 | $strreports = get_string('reports'); |
72 | ||
58cc51cd | 73 | $actionoptions = report_participation_get_action_options(); |
e20de262 PS |
74 | if (!array_key_exists($action, $actionoptions)) { |
75 | $action = ''; | |
76 | } | |
77 | ||
78 | $PAGE->set_title($course->shortname .': '. $strparticipation); | |
79 | $PAGE->set_heading($course->fullname); | |
80 | echo $OUTPUT->header(); | |
81 | ||
ff6a853a RT |
82 | $uselegacyreader = false; // Use legacy reader with sql_internal_reader to aggregate records. |
83 | $onlyuselegacyreader = false; // Use only legacy log table to aggregate records. | |
84 | ||
58cc51cd | 85 | $logtable = report_participation_get_log_table_name(); // Log table to use for fetaching records. |
ff6a853a | 86 | |
58cc51cd RT |
87 | // If no log table, then use legacy records. |
88 | if (empty($logtable)) { | |
89 | $onlyuselegacyreader = true; | |
ff6a853a RT |
90 | } |
91 | ||
92 | // If no legacy and no logtable then don't proceed. | |
93 | if (!$onlyuselegacyreader && empty($logtable)) { | |
94 | echo $OUTPUT->box_start('generalbox', 'notice'); | |
95 | echo get_string('nologreaderenabled', 'report_participation'); | |
96 | echo $OUTPUT->box_end(); | |
97 | echo $OUTPUT->footer(); | |
98 | die(); | |
99 | } | |
100 | ||
e20de262 PS |
101 | $modinfo = get_fast_modinfo($course); |
102 | ||
ff6a853a RT |
103 | $minloginternalreader = 0; // Time of first record in sql_internal_reader. |
104 | ||
105 | if ($onlyuselegacyreader) { | |
106 | // If no sql_inrenal_reader enabled then get min. time from log table. | |
107 | $minlog = $DB->get_field_sql('SELECT min(time) FROM {log} WHERE course = ?', array($course->id)); | |
108 | } else { | |
109 | $uselegacyreader = true; | |
110 | $minlog = $DB->get_field_sql('SELECT min(time) FROM {log} WHERE course = ?', array($course->id)); | |
111 | ||
112 | // If legacy reader is not logging then get data from new log table. | |
113 | // Get minimum log time for this course from preferred log reader. | |
114 | $minloginternalreader = $DB->get_field_sql('SELECT min(timecreated) FROM {' . $logtable . '} | |
115 | WHERE courseid = ?', array($course->id)); | |
116 | // If new log store has oldest data then don't use old log table. | |
117 | if (empty($minlog) || ($minloginternalreader <= $minlog)) { | |
118 | $uselegacyreader = false; | |
119 | $minlog = $minloginternalreader; | |
9e4e87b4 RT |
120 | } |
121 | ||
122 | // If timefrom is greater then first record in sql_internal_reader then get record from sql_internal_reader only. | |
123 | if (!empty($timefrom) && ($minloginternalreader < $timefrom)) { | |
124 | $uselegacyreader = false; | |
ff6a853a RT |
125 | } |
126 | } | |
bcadf372 | 127 | |
58cc51cd RT |
128 | // Print first controls. |
129 | report_participation_print_filter_form($course, $timefrom, $minlog, $action, $roleid, $instanceid); | |
e20de262 | 130 | |
d5ae34a5 SH |
131 | $baseurl = new moodle_url('/report/participation/index.php', array( |
132 | 'id' => $course->id, | |
133 | 'roleid' => $roleid, | |
134 | 'instanceid' => $instanceid, | |
135 | 'timefrom' => $timefrom, | |
136 | 'action' => $action, | |
137 | 'perpage' => $perpage, | |
138 | 'group' => $currentgroup | |
139 | )); | |
28c4399b AA |
140 | $select = groups_allgroups_course_menu($course, $baseurl, true, $currentgroup); |
141 | ||
142 | // User cannot see any group. | |
143 | if (empty($select)) { | |
144 | echo $OUTPUT->heading(get_string("notingroup")); | |
145 | echo $OUTPUT->footer(); | |
146 | exit; | |
147 | } else { | |
148 | echo $select; | |
149 | } | |
e20de262 | 150 | |
ef9ca106 AA |
151 | // Fetch current active group. |
152 | $groupmode = groups_get_course_groupmode($course); | |
153 | $currentgroup = $SESSION->activegroup[$course->id][$groupmode][$course->defaultgroupingid]; | |
154 | ||
e20de262 | 155 | if (!empty($instanceid) && !empty($roleid)) { |
e1b16f97 RT |
156 | |
157 | // Trigger a report viewed event. | |
158 | $event = \report_participation\event\report_viewed::create(array('context' => $context, | |
159 | 'other' => array('instanceid' => $instanceid, 'groupid' => $currentgroup, 'roleid' => $roleid, | |
160 | 'timefrom' => $timefrom, 'action' => $action))); | |
161 | $event->trigger(); | |
162 | ||
e20de262 PS |
163 | // from here assume we have at least the module we're using. |
164 | $cm = $modinfo->cms[$instanceid]; | |
28c4399b AA |
165 | |
166 | // Group security checks. | |
167 | if (!groups_group_visible($currentgroup, $course, $cm)) { | |
168 | echo $OUTPUT->heading(get_string("notingroup")); | |
169 | echo $OUTPUT->footer(); | |
170 | exit; | |
171 | } | |
172 | ||
e20de262 PS |
173 | $table = new flexible_table('course-participation-'.$course->id.'-'.$cm->id.'-'.$roleid); |
174 | $table->course = $course; | |
175 | ||
176 | $table->define_columns(array('fullname','count','select')); | |
177 | $table->define_headers(array(get_string('user'),((!empty($action)) ? get_string($action) : get_string('allactions')),get_string('select'))); | |
178 | $table->define_baseurl($baseurl); | |
179 | ||
180 | $table->set_attribute('cellpadding','5'); | |
181 | $table->set_attribute('class', 'generaltable generalbox reporttable'); | |
182 | ||
183 | $table->sortable(true,'lastname','ASC'); | |
184 | $table->no_sorting('select'); | |
185 | ||
186 | $table->set_control_variables(array( | |
187 | TABLE_VAR_SORT => 'ssort', | |
188 | TABLE_VAR_HIDE => 'shide', | |
189 | TABLE_VAR_SHOW => 'sshow', | |
190 | TABLE_VAR_IFIRST => 'sifirst', | |
191 | TABLE_VAR_ILAST => 'silast', | |
192 | TABLE_VAR_PAGE => 'spage' | |
193 | )); | |
194 | $table->setup(); | |
195 | ||
4e829d48 | 196 | // We want to query both the current context and parent contexts. |
ff6a853a RT |
197 | list($relatedctxsql, $params) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); |
198 | $params['roleid'] = $roleid; | |
199 | $params['instanceid'] = $instanceid; | |
200 | $params['timefrom'] = $timefrom; | |
e20de262 | 201 | |
28c4399b AA |
202 | $groupsql = ""; |
203 | if (!empty($currentgroup)) { | |
204 | $groupsql = "JOIN {groups_members} gm ON (gm.userid = u.id AND gm.groupid = :groupid)"; | |
205 | $params['groupid'] = $currentgroup; | |
206 | } | |
0be6f678 | 207 | |
e20de262 PS |
208 | $countsql = "SELECT COUNT(DISTINCT(ra.userid)) |
209 | FROM {role_assignments} ra | |
e3dfd1a1 | 210 | JOIN {user} u ON u.id = ra.userid |
28c4399b | 211 | $groupsql |
4e829d48 | 212 | WHERE ra.contextid $relatedctxsql AND ra.roleid = :roleid"; |
bcadf372 | 213 | |
e20de262 | 214 | $totalcount = $DB->count_records_sql($countsql, $params); |
0be6f678 | 215 | |
ff6a853a | 216 | list($twhere, $tparams) = $table->get_sql_where(); |
e20de262 | 217 | if ($twhere) { |
66aee2c2 | 218 | $params = array_merge($params, $tparams); |
e20de262 PS |
219 | $matchcount = $DB->count_records_sql($countsql.' AND '.$twhere, $params); |
220 | } else { | |
221 | $matchcount = $totalcount; | |
222 | } | |
0be6f678 | 223 | |
ff6a853a | 224 | $modulename = get_string('modulename', $cm->modname); |
e20de262 | 225 | echo '<div id="participationreport">' . "\n"; |
ff6a853a RT |
226 | echo '<p class="modulename">' . $modulename . ' ' . $strviews . '<br />'."\n" |
227 | . $modulename . ' ' . $strposts . '</p>'."\n"; | |
bcadf372 | 228 | |
e20de262 PS |
229 | $table->initialbars($totalcount > $perpage); |
230 | $table->pagesize($perpage, $matchcount); | |
bcadf372 | 231 | |
ff6a853a | 232 | if ($uselegacyreader || $onlyuselegacyreader) { |
58cc51cd | 233 | list($actionsql, $actionparams) = report_participation_get_action_sql($action, $cm->modname); |
ff6a853a RT |
234 | $params = array_merge($params, $actionparams); |
235 | } | |
236 | ||
58cc51cd RT |
237 | if (!$onlyuselegacyreader) { |
238 | list($crudsql, $crudparams) = report_participation_get_crud_sql($action); | |
ff6a853a RT |
239 | $params = array_merge($params, $crudparams); |
240 | } | |
241 | ||
242 | $usernamefields = get_all_user_name_fields(true, 'u'); | |
243 | $users = array(); | |
244 | // If using legacy log then get users from old table. | |
245 | if ($uselegacyreader || $onlyuselegacyreader) { | |
246 | $limittime = ''; | |
247 | if ($uselegacyreader && !empty($minloginternalreader)) { | |
248 | $limittime = ' AND time < :tilltime '; | |
249 | $params['tilltime'] = $minloginternalreader; | |
250 | } | |
251 | $sql = "SELECT ra.userid, $usernamefields, u.idnumber, l.actioncount AS count | |
e4401d04 | 252 | FROM (SELECT DISTINCT userid FROM {role_assignments} WHERE contextid $relatedctxsql AND roleid = :roleid ) ra |
ff6a853a RT |
253 | JOIN {user} u ON u.id = ra.userid |
254 | $groupsql | |
255 | LEFT JOIN ( | |
256 | SELECT userid, COUNT(action) AS actioncount | |
257 | FROM {log} | |
258 | WHERE cmid = :instanceid | |
259 | AND time > :timefrom " . $limittime . $actionsql . | |
260 | " GROUP BY userid) l ON (l.userid = ra.userid)"; | |
261 | if ($twhere) { | |
262 | $sql .= ' WHERE '.$twhere; // Initial bar. | |
ff6a853a RT |
263 | } |
264 | ||
265 | if ($table->get_sql_sort()) { | |
266 | $sql .= ' ORDER BY '.$table->get_sql_sort(); | |
267 | } | |
268 | if (!$users = $DB->get_records_sql($sql, $params, $table->get_page_start(), $table->get_page_size())) { | |
269 | $users = array(); // Tablelib will handle saying 'Nothing to display' for us. | |
270 | } | |
271 | } | |
272 | ||
273 | // Get record from sql_internal_reader and merge with records got from legacy log (if needed). | |
274 | if (!$onlyuselegacyreader) { | |
3bb8c9e3 | 275 | $sql = "SELECT ra.userid, $usernamefields, u.idnumber, COUNT(l.actioncount) AS count |
e4401d04 | 276 | FROM (SELECT DISTINCT userid FROM {role_assignments} WHERE contextid $relatedctxsql AND roleid = :roleid ) ra |
ff6a853a RT |
277 | JOIN {user} u ON u.id = ra.userid |
278 | $groupsql | |
279 | LEFT JOIN ( | |
280 | SELECT userid, COUNT(crud) AS actioncount | |
281 | FROM {" . $logtable . "} | |
282 | WHERE contextinstanceid = :instanceid | |
283 | AND timecreated > :timefrom" . $crudsql ." | |
284 | AND edulevel = :edulevel | |
285 | AND anonymous = 0 | |
286 | AND contextlevel = :contextlevel | |
66d8b25b | 287 | AND (origin = 'web' OR origin = 'ws') |
0e6d8ed2 SH |
288 | GROUP BY userid,timecreated) l ON (l.userid = ra.userid)"; |
289 | // We add this after the WHERE statement that may come below. | |
290 | $groupbysql = " GROUP BY ra.userid, $usernamefields, u.idnumber"; | |
ff6a853a RT |
291 | |
292 | $params['edulevel'] = core\event\base::LEVEL_PARTICIPATING; | |
293 | $params['contextlevel'] = CONTEXT_MODULE; | |
294 | ||
295 | if ($twhere) { | |
296 | $sql .= ' WHERE '.$twhere; // Initial bar. | |
ff6a853a | 297 | } |
0e6d8ed2 | 298 | $sql .= $groupbysql; |
ff6a853a RT |
299 | if ($table->get_sql_sort()) { |
300 | $sql .= ' ORDER BY '.$table->get_sql_sort(); | |
301 | } | |
302 | if ($u = $DB->get_records_sql($sql, $params, $table->get_page_start(), $table->get_page_size())) { | |
303 | if (empty($users)) { | |
304 | $users = $u; | |
305 | } else { | |
306 | // Merge two users array. | |
307 | foreach ($u as $key => $value) { | |
308 | if (isset($users[$key]) && !empty($users[$key]->count)) { | |
309 | if ($value->count) { | |
310 | $users[$key]->count += $value->count; | |
311 | } | |
312 | } else { | |
313 | $users[$key] = $value; | |
314 | } | |
315 | } | |
316 | } | |
317 | unset($u); | |
318 | $u = null; | |
319 | } | |
e20de262 | 320 | } |
0be6f678 | 321 | |
e20de262 | 322 | $data = array(); |
0be6f678 | 323 | |
b85b25eb | 324 | $a = new stdClass(); |
e20de262 PS |
325 | $a->count = $totalcount; |
326 | $a->items = $role->name; | |
0be6f678 | 327 | |
e20de262 PS |
328 | if ($matchcount != $totalcount) { |
329 | $a->count = $matchcount.'/'.$a->count; | |
330 | } | |
bcadf372 | 331 | |
e20de262 | 332 | echo '<h2>'.get_string('counteditems', '', $a).'</h2>'."\n"; |
bcadf372 | 333 | |
e20de262 PS |
334 | echo '<form action="'.$CFG->wwwroot.'/user/action_redir.php" method="post" id="studentsform">'."\n"; |
335 | echo '<div>'."\n"; | |
336 | echo '<input type="hidden" name="id" value="'.$id.'" />'."\n"; | |
f0202ae9 | 337 | echo '<input type="hidden" name="returnto" value="'. s($PAGE->url) .'" />'."\n"; |
e20de262 | 338 | echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />'."\n"; |
bcadf372 | 339 | |
e20de262 PS |
340 | foreach ($users as $u) { |
341 | $data = array('<a href="'.$CFG->wwwroot.'/user/view.php?id='.$u->userid.'&course='.$course->id.'">'.fullname($u,true).'</a>'."\n", | |
342 | ((!empty($u->count)) ? get_string('yes').' ('.$u->count.') ' : get_string('no')), | |
343 | '<input type="checkbox" class="usercheckbox" name="user'.$u->userid.'" value="'.$u->count.'" />'."\n", | |
344 | ); | |
345 | $table->add_data($data); | |
346 | } | |
bcadf372 | 347 | |
e20de262 | 348 | $table->print_html(); |
0be6f678 | 349 | |
e20de262 | 350 | if ($perpage == SHOW_ALL_PAGE_SIZE) { |
d5ae34a5 SH |
351 | $perpageurl = new moodle_url($baseurl, array('perpage' => DEFAULT_PAGE_SIZE)); |
352 | echo html_writer::start_div('', array('id' => 'showall')); | |
353 | echo html_writer::link($perpageurl, get_string('showperpage', '', DEFAULT_PAGE_SIZE)); | |
354 | echo html_writer::end_div(); | |
355 | } else if ($matchcount > 0 && $perpage < $matchcount) { | |
356 | $perpageurl = new moodle_url($baseurl, array('perpage' => SHOW_ALL_PAGE_SIZE)); | |
357 | echo html_writer::start_div('', array('id' => 'showall')); | |
358 | echo html_writer::link($perpageurl, get_string('showall', '', $matchcount)); | |
359 | echo html_writer::end_div(); | |
bcadf372 | 360 | } |
0be6f678 | 361 | |
e20de262 PS |
362 | echo '<div class="selectbuttons">'; |
363 | echo '<input type="button" id="checkall" value="'.get_string('selectall').'" /> '."\n"; | |
364 | echo '<input type="button" id="checknone" value="'.get_string('deselectall').'" /> '."\n"; | |
365 | if ($perpage >= $matchcount) { | |
366 | echo '<input type="button" id="checknos" value="'.get_string('selectnos').'" />'."\n"; | |
367 | } | |
368 | echo '</div>'; | |
369 | echo '<div>'; | |
d26cfd60 | 370 | echo html_writer::label(get_string('withselectedusers'), 'formactionselect'); |
e20de262 PS |
371 | $displaylist['messageselect.php'] = get_string('messageselectadd'); |
372 | echo html_writer::select($displaylist, 'formaction', '', array(''=>'choosedots'), array('id'=>'formactionselect')); | |
373 | echo $OUTPUT->help_icon('withselectedusers'); | |
374 | echo '<input type="submit" value="' . get_string('ok') . '" />'."\n"; | |
375 | echo '</div>'; | |
376 | echo '</div>'."\n"; | |
377 | echo '</form>'."\n"; | |
378 | echo '</div>'."\n"; | |
379 | ||
380 | $PAGE->requires->js_init_call('M.report_participation.init'); | |
381 | } | |
382 | ||
383 | echo $OUTPUT->footer(); |