Merge branch 'wip-MDL-28582-master' of git://github.com/samhemelryk/moodle
[moodle.git] / course / user.php
CommitLineData
d9cb06dc 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Display user activity reports for a course
20 *
21 * @copyright 1999 Martin Dougiamas http://dougiamas.com
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 * @package course
24 */
25
26require_once("../config.php");
27require_once("lib.php");
516c5eca 28require_once($CFG->libdir.'/completionlib.php');
d9cb06dc 29
30$id = required_param('id',PARAM_INT); // course id
31$user = required_param('user',PARAM_INT); // user id
32$mode = optional_param('mode', "todaylogs", PARAM_ALPHA);
33$page = optional_param('page', 0, PARAM_INT);
34$perpage = optional_param('perpage', 100, PARAM_INT);
35
a6855934 36$url = new moodle_url('/course/user.php', array('id'=>$id,'user'=>$user, 'mode'=>$mode));
d9cb06dc 37if ($page !== 0) {
38 $url->param('page', $page);
39}
40if ($perpage !== 100) {
41 $url->param('perpage', $perpage);
42}
43$PAGE->set_url($url);
f9903ed0 44
d9cb06dc 45if (!$course = $DB->get_record('course', array('id'=>$id))) {
46 print_error('invalidcourseid', 'error');
47}
5bf04b36 48
d9cb06dc 49if (! $user = $DB->get_record("user", array("id"=>$user))) {
50 print_error('invaliduserid', 'error');
51}
5bf04b36 52
d9cb06dc 53require_login();
54$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
55$personalcontext = get_context_instance(CONTEXT_USER, $user->id);
358e2a0b 56
df997f84 57require_login();
87c215de 58$PAGE->set_pagelayout('admin');
df997f84 59if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and !is_enrolled($coursecontext)) {
d9cb06dc 60 // do not require parents to be enrolled in courses ;-)
61 $PAGE->set_course($course);
62} else {
63 require_login($course);
64}
6bb91986 65
d9cb06dc 66if ($user->deleted) {
67 echo $OUTPUT->header();
68 echo $OUTPUT->heading(get_string('userdeleted'));
69 echo $OUTPUT->footer();
70 die;
71}
f5fc83e8 72
d9cb06dc 73// prepare list of allowed modes
74$myreports = ($course->showreports and $USER->id == $user->id);
75$anyreport = has_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
358e2a0b 76
d9cb06dc 77$modes = array();
358e2a0b 78
d9cb06dc 79if ($myreports or $anyreport or has_capability('coursereport/outline:view', $coursecontext)) {
80 $modes[] = 'outline';
81}
358e2a0b 82
d9cb06dc 83if ($myreports or $anyreport or has_capability('coursereport/outline:view', $coursecontext)) {
84 $modes[] = 'complete';
85}
358e2a0b 86
d9cb06dc 87if ($myreports or $anyreport or has_capability('coursereport/log:viewtoday', $coursecontext)) {
88 $modes[] = 'todaylogs';
89}
3924b988 90
d9cb06dc 91if ($myreports or $anyreport or has_capability('coursereport/log:view', $coursecontext)) {
92 $modes[] = 'alllogs';
93}
358e2a0b 94
d9cb06dc 95if ($myreports or $anyreport or has_capability('coursereport/stats:view', $coursecontext)) {
96 $modes[] = 'stats';
97}
358e2a0b 98
d9cb06dc 99if (has_capability('moodle/grade:viewall', $coursecontext)) {
100 //ok - can view all course grades
101 $modes[] = 'grade';
358e2a0b 102
d9cb06dc 103} else if ($course->showgrades and $user->id == $USER->id and has_capability('moodle/grade:view', $coursecontext)) {
104 //ok - can view own grades
105 $modes[] = 'grade';
358e2a0b 106
d9cb06dc 107} else if ($course->showgrades and has_capability('moodle/grade:viewall', $personalcontext)) {
108 // ok - can view grades of this user - parent most probably
109 $modes[] = 'grade';
3c7da16d 110
d9cb06dc 111} else if ($course->showgrades and $anyreport) {
112 // ok - can view grades of this user - parent most probably
113 $modes[] = 'grade';
114}
358e2a0b 115
2be4d090
MD
116// Course completion tab
117if (!empty($CFG->enablecompletion) && ($course->id == SITEID || !empty($course->enablecompletion)) && // completion enabled
118 ($myreports || $anyreport || ($course->id == SITEID || has_capability('coursereport/completion:view', $coursecontext)))) { // permissions to view the report
119
120 // Decide if singular or plural
121 if ($course->id == SITEID) {
122 $modes[] = 'coursecompletions';
123 } else {
124 $modes[] = 'coursecompletion';
125 }
126}
127
128
d9cb06dc 129if (empty($modes)) {
130 require_capability('moodle/user:viewuseractivitiesreport', $personalcontext);
131}
358e2a0b 132
d9cb06dc 133if (!in_array($mode, $modes)) {
7e85563d 134 // forbidden or non-existent mode
d9cb06dc 135 $mode = reset($modes);
136}
85a6e76c 137
d9cb06dc 138add_to_log($course->id, "course", "user report", "user.php?id=$course->id&amp;user=$user->id&amp;mode=$mode", "$user->id");
f9903ed0 139
d9cb06dc 140$stractivityreport = get_string("activityreport");
141$strparticipants = get_string("participants");
142$stroutline = get_string("outline");
143$strcomplete = get_string("complete");
144$stralllogs = get_string("alllogs");
145$strtodaylogs = get_string("todaylogs");
146$strmode = get_string($mode);
147$fullname = fullname($user, true);
a2ab3b05 148
d9cb06dc 149$link = null;
150if ($course->id != SITEID && has_capability('moodle/course:viewparticipants', $coursecontext)) {
a6855934 151 $link = new moodle_url('/user/index.php', array('id'=>$course->id));
d9cb06dc 152}
7a7e209d
SH
153
154$PAGE->navigation->extend_for_user($user);
870815fa 155$PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed.
d9cb06dc 156$PAGE->set_title("$course->shortname: $stractivityreport ($mode)");
157$PAGE->set_heading($course->fullname);
158echo $OUTPUT->header();
f9a0ea69 159
d9cb06dc 160switch ($mode) {
161 case "grade":
162 if (empty($CFG->grade_profilereport) or !file_exists($CFG->dirroot.'/grade/report/'.$CFG->grade_profilereport.'/lib.php')) {
163 $CFG->grade_profilereport = 'user';
164 }
165 require_once $CFG->libdir.'/gradelib.php';
166 require_once $CFG->dirroot.'/grade/lib.php';
167 require_once $CFG->dirroot.'/grade/report/'.$CFG->grade_profilereport.'/lib.php';
168
169 $functionname = 'grade_report_'.$CFG->grade_profilereport.'_profilereport';
170 if (function_exists($functionname)) {
171 $functionname($course, $user);
172 }
173 break;
174
175 case "todaylogs" :
176 echo '<div class="graph">';
177 print_log_graph($course, $user->id, "userday.png");
178 echo '</div>';
179 print_log($course, $user->id, usergetmidnight(time()), "l.time DESC", $page, $perpage,
180 "user.php?id=$course->id&amp;user=$user->id&amp;mode=$mode");
181 break;
182
183 case "alllogs" :
184 echo '<div class="graph">';
185 print_log_graph($course, $user->id, "usercourse.png");
186 echo '</div>';
187 print_log($course, $user->id, 0, "l.time DESC", $page, $perpage,
188 "user.php?id=$course->id&amp;user=$user->id&amp;mode=$mode");
189 break;
190 case 'stats':
191
192 if (empty($CFG->enablestats)) {
193 print_error('statsdisable', 'error');
194 }
195
196 require_once($CFG->dirroot.'/lib/statslib.php');
197
198 $statsstatus = stats_check_uptodate($course->id);
199 if ($statsstatus !== NULL) {
200 echo $OUTPUT->notification($statsstatus);
201 }
202
203 $earliestday = $DB->get_field_sql('SELECT timeend FROM {stats_user_daily} ORDER BY timeend');
204 $earliestweek = $DB->get_field_sql('SELECT timeend FROM {stats_user_weekly} ORDER BY timeend');
205 $earliestmonth = $DB->get_field_sql('SELECT timeend FROM {stats_user_monthly} ORDER BY timeend');
206
207 if (empty($earliestday)) $earliestday = time();
208 if (empty($earliestweek)) $earliestweek = time();
209 if (empty($earliestmonth)) $earliestmonth = time();
210
211 $now = stats_get_base_daily();
212 $lastweekend = stats_get_base_weekly();
213 $lastmonthend = stats_get_base_monthly();
214
215 $timeoptions = stats_get_time_options($now,$lastweekend,$lastmonthend,$earliestday,$earliestweek,$earliestmonth);
216
217 if (empty($timeoptions)) {
218 print_error('nostatstodisplay', '', $CFG->wwwroot.'/course/user.php?id='.$course->id.'&user='.$user->id.'&mode=outline');
219 }
220
221 // use the earliest.
222 $time = array_pop(array_keys($timeoptions));
223
224 $param = stats_get_parameters($time,STATS_REPORT_USER_VIEW,$course->id,STATS_MODE_DETAILED);
225 $params = $param->params;
226
227 $param->table = 'user_'.$param->table;
228
229 $sql = 'SELECT timeend,'.$param->fields.' FROM {stats_'.$param->table.'} WHERE '
230 .(($course->id == SITEID) ? '' : ' courseid = '.$course->id.' AND ')
231 .' userid = '.$user->id.' AND timeend >= '.$param->timeafter .$param->extras
232 .' ORDER BY timeend DESC';
233 $stats = $DB->get_records_sql($sql, $params); //TODO: improve these params!!
234
235 if (empty($stats)) {
236 print_error('nostatstodisplay', '', $CFG->wwwroot.'/course/user.php?id='.$course->id.'&user='.$user->id.'&mode=outline');
237 }
238
239 // MDL-10818, do not display broken graph when user has no permission to view graph
240 if ($myreports or has_capability('coursereport/stats:view', $coursecontext)) {
241 echo '<center><img src="'.$CFG->wwwroot.'/course/report/stats/graph.php?mode='.STATS_MODE_DETAILED.'&course='.$course->id.'&time='.$time.'&report='.STATS_REPORT_USER_VIEW.'&userid='.$user->id.'" alt="'.get_string('statisticsgraph').'" /></center>';
242 }
243
244 // What the heck is this about? -- MD
245 $stats = stats_fix_zeros($stats,$param->timeafter,$param->table,(!empty($param->line2)),(!empty($param->line3)));
246
247 $table = new html_table();
248 $table->align = array('left','center','center','center');
249 $param->table = str_replace('user_','',$param->table);
250 switch ($param->table) {
251 case 'daily' : $period = get_string('day'); break;
252 case 'weekly' : $period = get_string('week'); break;
253 case 'monthly': $period = get_string('month', 'form'); break;
254 default : $period = '';
255 }
256 $table->head = array(get_string('periodending','moodle',$period),$param->line1,$param->line2,$param->line3);
257 foreach ($stats as $stat) {
258 if (!empty($stat->zerofixed)) { // Don't know why this is necessary, see stats_fix_zeros above - MD
259 continue;
f3221af9 260 }
d9cb06dc 261 $a = array(userdate($stat->timeend,get_string('strftimedate'),$CFG->timezone),$stat->line1);
262 $a[] = $stat->line2;
263 $a[] = $stat->line3;
264 $table->data[] = $a;
265 }
16be8974 266 echo html_writer::table($table);
d9cb06dc 267 break;
268
269 case "outline" :
270 case "complete" :
271 get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
272 $sections = get_all_sections($course->id);
5a1b6297 273 $itemsprinted = false;
d9cb06dc 274
275 for ($i=0; $i<=$course->numsections; $i++) {
276
277 if (isset($sections[$i])) { // should always be true
278
279 $section = $sections[$i];
280 $showsection = (has_capability('moodle/course:viewhiddensections', $coursecontext) or $section->visible or !$course->hiddensections);
281
282 if ($showsection) { // prevent hidden sections in user activity. Thanks to Geoff Wilbert!
5a1b6297
SH
283 // Check the section has a sequence. This is the sequence of modules/resources.
284 // If there is no sequence there is nothing to display.
d9cb06dc 285 if ($section->sequence) {
5a1b6297 286 $itemsprinted = true;
d9cb06dc 287 echo '<div class="section">';
288 echo '<h2>';
7487c856
SH
289 echo get_section_name($course, $section);
290 echo "</h2>";
f3221af9 291
d9cb06dc 292 echo '<div class="content">';
7468bf01 293
d9cb06dc 294 if ($mode == "outline") {
295 echo "<table cellpadding=\"4\" cellspacing=\"0\">";
296 }
7468bf01 297
d9cb06dc 298 $sectionmods = explode(",", $section->sequence);
299 foreach ($sectionmods as $sectionmod) {
300 if (empty($mods[$sectionmod])) {
301 continue;
7af6281f 302 }
d9cb06dc 303 $mod = $mods[$sectionmod];
4f91674c 304
d9cb06dc 305 if (empty($mod->visible)) {
306 continue;
4f91674c 307 }
308
d9cb06dc 309 $instance = $DB->get_record("$mod->modname", array("id"=>$mod->instance));
310 $libfile = "$CFG->dirroot/mod/$mod->modname/lib.php";
0be6f678 311
d9cb06dc 312 if (file_exists($libfile)) {
313 require_once($libfile);
359b138b 314
d9cb06dc 315 switch ($mode) {
316 case "outline":
317 $user_outline = $mod->modname."_user_outline";
318 if (function_exists($user_outline)) {
319 $output = $user_outline($course, $user, $mod, $instance);
320 print_outline_row($mod, $instance, $output);
321 }
322 break;
323 case "complete":
324 $user_complete = $mod->modname."_user_complete";
325 if (function_exists($user_complete)) {
b50d110e 326 $image = $OUTPUT->pix_icon('icon', $mod->modfullname, 'mod_'.$mod->modname, array('class'=>'icon'));
d9cb06dc 327 echo "<h4>$image $mod->modfullname: ".
328 "<a href=\"$CFG->wwwroot/mod/$mod->modname/view.php?id=$mod->id\">".
329 format_string($instance->name,true)."</a></h4>";
330
331 ob_start();
332
333 echo "<ul>";
334 $user_complete($course, $user, $mod, $instance);
335 echo "</ul>";
336
337 $output = ob_get_contents();
338 ob_end_clean();
339
340 if (str_replace(' ', '', $output) != '<ul></ul>') {
341 echo $output;
1757627a 342 }
359b138b 343 }
d9cb06dc 344 break;
1757627a 345 }
7468bf01 346 }
347 }
d9cb06dc 348
349 if ($mode == "outline") {
350 echo "</table>";
7468bf01 351 }
d9cb06dc 352 echo '</div>'; // content
353 echo '</div>'; // section
7468bf01 354 }
600149be 355 }
f9903ed0 356 }
d9cb06dc 357 }
5a1b6297
SH
358
359 if (!$itemsprinted) {
360 echo $OUTPUT->notification(get_string('nothingtodisplay'));
361 }
362
2be4d090
MD
363 break;
364 case "coursecompletion":
365 case "coursecompletions":
366
367 // Display course completion user report
2be4d090
MD
368
369 // Grab all courses the user is enrolled in and their completion status
370 $sql = "
371 SELECT DISTINCT
372 c.id AS id
373 FROM
ffe50258 374 {course} c
2be4d090 375 INNER JOIN
ffe50258 376 {context} con
2be4d090 377 ON con.instanceid = c.id
b64a827c 378 INNER JOIN
379 {role_assignments} ra
380 ON ra.contextid = con.id
1c6ed505 381 INNER JOIN
382 {enrol} e
383 ON c.id = e.courseid
384 INNER JOIN
385 {user_enrolments} ue
b64a827c 386 ON e.id = ue.enrolid AND ra.userid = ue.userid
2be4d090
MD
387 AND ra.userid = {$user->id}
388 ";
389
390 // Get roles that are tracked by course completion
1c6ed505 391 if ($roles = $CFG->gradebookroles) {
2be4d090
MD
392 $sql .= '
393 AND ra.roleid IN ('.$roles.')
394 ';
395 }
396
397 $sql .= '
398 WHERE
399 con.contextlevel = '.CONTEXT_COURSE.'
400 AND c.enablecompletion = 1
401 ';
402
403
404 // If we are looking at a specific course
405 if ($course->id != 1) {
406 $sql .= '
407 AND c.id = '.(int)$course->id.'
408 ';
409 }
410
411 // Check if result is empty
a0c3fc63
EL
412 $rs = $DB->get_recordset_sql($sql);
413 if (!$rs->valid()) {
2be4d090
MD
414
415 if ($course->id != 1) {
416 $error = get_string('nocompletions', 'coursereport_completion');
417 } else {
418 $error = get_string('nocompletioncoursesenroled', 'coursereport_completion');
419 }
420
421 echo $OUTPUT->notification($error);
a0c3fc63 422 $rs->close(); // not going to loop (but break), close rs
2be4d090
MD
423 break;
424 }
425
426 // Categorize courses by their status
427 $courses = array(
428 'inprogress' => array(),
429 'complete' => array(),
430 'unstarted' => array()
431 );
432
433 // Sort courses by the user's status in each
434 foreach ($rs as $course_completion) {
435 $c_info = new completion_info((object)$course_completion);
436
437 // Is course complete?
438 $coursecomplete = $c_info->is_course_complete($user->id);
439
440 // Has this user completed any criteria?
441 $criteriacomplete = $c_info->count_course_user_data($user->id);
442
443 if ($coursecomplete) {
444 $courses['complete'][] = $c_info;
445 } else if ($criteriacomplete) {
446 $courses['inprogress'][] = $c_info;
447 } else {
448 $courses['unstarted'][] = $c_info;
449 }
450 }
a0c3fc63 451 $rs->close(); // after loop, close rs
2be4d090
MD
452
453 // Loop through course status groups
454 foreach ($courses as $type => $infos) {
455
456 // If there are courses with this status
457 if (!empty($infos)) {
458
459 echo '<h1 align="center">'.get_string($type, 'coursereport_completion').'</h1>';
460 echo '<table class="generalbox boxaligncenter">';
461 echo '<tr class="ccheader">';
462 echo '<th class="c0 header" scope="col">'.get_string('course').'</th>';
463 echo '<th class="c1 header" scope="col">'.get_string('requiredcriteria', 'completion').'</th>';
464 echo '<th class="c2 header" scope="col">'.get_string('status').'</th>';
465 echo '<th class="c3 header" scope="col" width="15%">'.get_string('info').'</th>';
466
467 if ($type === 'complete') {
468 echo '<th class="c4 header" scope="col">'.get_string('completiondate', 'coursereport_completion').'</th>';
469 }
470
471 echo '</tr>';
472
473 // For each course
474 foreach ($infos as $c_info) {
475
476 // Get course info
ffe50258 477 $c_course = $DB->get_record('course', array('id' => $c_info->course_id));
91d284c1
SH
478 $course_context = get_context_instance(CONTEXT_COURSE, $c_course->id, MUST_EXIST);
479 $course_name = format_string($c_course->fullname, true, array('context' => $course_context));
2be4d090
MD
480
481 // Get completions
482 $completions = $c_info->get_completions($user->id);
483
484 // Save row data
485 $rows = array();
486
487 // For aggregating activity completion
488 $activities = array();
489 $activities_complete = 0;
7e85563d 490
2be4d090
MD
491 // For aggregating prerequisites
492 $prerequisites = array();
493 $prerequisites_complete = 0;
494
495 // Loop through course criteria
496 foreach ($completions as $completion) {
497 $criteria = $completion->get_criteria();
498 $complete = $completion->is_complete();
499
500 // Activities are a special case, so cache them and leave them till last
501 if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
502 $activities[$criteria->moduleinstance] = $complete;
503
504 if ($complete) {
505 $activities_complete++;
506 }
507
508 continue;
509 }
510
511 // Prerequisites are also a special case, so cache them and leave them till last
512 if ($criteria->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
513 $prerequisites[$criteria->courseinstance] = $complete;
514
515 if ($complete) {
516 $prerequisites_complete++;
517 }
518
519 continue;
520 }
521
522 $row = array();
523 $row['title'] = $criteria->get_title();
524 $row['status'] = $completion->get_status();
525 $rows[] = $row;
526 }
527
528 // Aggregate activities
529 if (!empty($activities)) {
530
531 $row = array();
532 $row['title'] = get_string('activitiescomplete', 'coursereport_completion');
533 $row['status'] = $activities_complete.' of '.count($activities);
534 $rows[] = $row;
535 }
536
537 // Aggregate prerequisites
538 if (!empty($prerequisites)) {
539
540 $row = array();
541 $row['title'] = get_string('prerequisitescompleted', 'completion');
542 $row['status'] = $prerequisites_complete.' of '.count($prerequisites);
543 array_splice($rows, 0, 0, array($row));
544 }
545
546 $first_row = true;
547
548 // Print table
549 foreach ($rows as $row) {
550
551 // Display course name on first row
552 if ($first_row) {
91d284c1 553 echo '<tr><td class="c0"><a href="'.$CFG->wwwroot.'/course/view.php?id='.$c_course->id.'">'.$course_name.'</a></td>';
2be4d090
MD
554 } else {
555 echo '<tr><td class="c0"></td>';
556 }
557
558 echo '<td class="c1">';
559 echo $row['title'];
560 echo '</td><td class="c2">';
561
562 switch ($row['status']) {
563 case 'Yes':
564 echo get_string('complete');
565 break;
566
567 case 'No':
568 echo get_string('incomplete', 'coursereport_completion');
569 break;
570
571 default:
572 echo $row['status'];
573 }
574
575 // Display link on first row
576 echo '</td><td class="c3">';
577 if ($first_row) {
578 echo '<a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$c_course->id.'&user='.$user->id.'">'.get_string('detailedview', 'coursereport_completion').'</a>';
579 }
580 echo '</td>';
581
582 // Display completion date for completed courses on first row
583 if ($type === 'complete' && $first_row) {
584 $params = array(
585 'userid' => $user->id,
586 'course' => $c_course->id
587 );
588
589 $ccompletion = new completion_completion($params);
590 echo '<td class="c4">'.userdate($ccompletion->timecompleted, '%e %B %G').'</td>';
591 }
592
593 $first_row = false;
594 echo '</tr>';
595 }
596 }
597
598 echo '</table>';
599 }
600
601 }
602
d9cb06dc 603 break;
604 default:
605 // can not be reached ;-)
606}
f9903ed0 607
7468bf01 608
d9cb06dc 609echo $OUTPUT->footer();
f9903ed0 610
7468bf01 611
359b138b 612function print_outline_row($mod, $instance, $result) {
e63f88c9 613 global $OUTPUT;
bad35380 614
b5d0cafc 615 $image = "<img src=\"" . $OUTPUT->pix_url('icon', $mod->modname) . "\" class=\"icon\" alt=\"$mod->modfullname\" />";
24f2b65a 616
617 echo "<tr>";
618 echo "<td valign=\"top\">$image</td>";
fbe31d22 619 echo "<td valign=\"top\" style=\"width:300\">";
24f2b65a 620 echo " <a title=\"$mod->modfullname\"";
46043aff 621 echo " href=\"../mod/$mod->modname/view.php?id=$mod->id\">".format_string($instance->name,true)."</a></td>";
24f2b65a 622 echo "<td>&nbsp;&nbsp;&nbsp;</td>";
1530c97c 623 echo "<td valign=\"top\">";
359b138b 624 if (isset($result->info)) {
625 echo "$result->info";
626 } else {
fbe31d22 627 echo "<p style=\"text-align:center\">-</p>";
359b138b 628 }
24f2b65a 629 echo "</td>";
630 echo "<td>&nbsp;&nbsp;&nbsp;</td>";
20affd15 631 if (!empty($result->time)) {
359b138b 632 $timeago = format_time(time() - $result->time);
fbe31d22 633 echo "<td valign=\"top\" style=\"white-space: nowrap\">".userdate($result->time)." ($timeago)</td>";
359b138b 634 }
24f2b65a 635 echo "</tr>";
7468bf01 636}
637