lib.php 148 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Library of useful functions
 *
 * @copyright 1999 Martin Dougiamas  http://dougiamas.com
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
24
 * @package core
 * @subpackage course
25
 */
martin's avatar
martin committed
26

27
28
defined('MOODLE_INTERNAL') || die;

sam_marshall's avatar
sam_marshall committed
29
require_once($CFG->libdir.'/completionlib.php');
30
require_once($CFG->libdir.'/filelib.php');
martin's avatar
martin committed
31

32
33
34
35
36
define('COURSE_MAX_LOG_DISPLAY', 150);          // days
define('COURSE_MAX_LOGS_PER_PAGE', 1000);       // records
define('COURSE_LIVELOG_REFRESH', 60);           // Seconds
define('COURSE_MAX_RECENT_PERIOD', 172800);     // Two days, in seconds
define('COURSE_MAX_SUMMARIES_PER_PAGE', 10);    // courses
37
define('COURSE_MAX_COURSES_PER_DROPDOWN',1000); //  max courses in log dropdown before switching to optional
38
define('COURSE_MAX_USERS_PER_DROPDOWN',1000);   //  max users in log dropdown before switching to optional
39
40
41
42
43
define('FRONTPAGENEWS',           '0');
define('FRONTPAGECOURSELIST',     '1');
define('FRONTPAGECATEGORYNAMES',  '2');
define('FRONTPAGETOPICONLY',      '3');
define('FRONTPAGECATEGORYCOMBO',  '4');
44
45
46
define('FRONTPAGECOURSELIMIT',    200);         // maximum number of courses displayed on the frontpage
define('EXCELROWS', 65535);
define('FIRSTUSEDEXCELROW', 3);
47

48
49
50
define('MOD_CLASS_ACTIVITY', 0);
define('MOD_CLASS_RESOURCE', 1);

51
if (!defined('MAX_MODINFO_CACHE_SIZE')) {
52
53
    define('MAX_MODINFO_CACHE_SIZE', 10);
}
martin's avatar
martin committed
54

martin's avatar
martin committed
55
56
function make_log_url($module, $url) {
    switch ($module) {
moodler's avatar
moodler committed
57
58
59
60
61
62
        case 'course':
        case 'file':
        case 'login':
        case 'lib':
        case 'admin':
        case 'calendar':
63
        case 'mnet course':
64
65
66
67
68
            if (strpos($url, '../') === 0) {
                $url = ltrim($url, '.');
            } else {
                $url = "/course/$url";
            }
69
            break;
70
        case 'user':
moodler's avatar
moodler committed
71
        case 'blog':
72
            $url = "/$module/$url";
martin's avatar
martin committed
73
            break;
moodler's avatar
moodler committed
74
        case 'upload':
75
            $url = $url;
76
            break;
77
        case 'coursetags':
78
            $url = '/'.$url;
79
            break;
moodler's avatar
moodler committed
80
81
        case 'library':
        case '':
82
            $url = '/';
moodler's avatar
moodler committed
83
            break;
84
        case 'message':
85
86
87
88
            $url = "/message/$url";
            break;
        case 'notes':
            $url = "/notes/$url";
89
            break;
90
91
92
        case 'tag':
            $url = "/tag/$url";
            break;
93
94
95
        case 'role':
            $url = '/'.$url;
            break;
martin's avatar
martin committed
96
        default:
97
            $url = "/mod/$module/$url";
martin's avatar
martin committed
98
99
            break;
    }
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

    //now let's sanitise urls - there might be some ugly nasties:-(
    $parts = explode('?', $url);
    $script = array_shift($parts);
    if (strpos($script, 'http') === 0) {
        $script = clean_param($script, PARAM_URL);
    } else {
        $script = clean_param($script, PARAM_PATH);
    }

    $query = '';
    if ($parts) {
        $query = implode('', $parts);
        $query = str_replace('&amp;', '&', $query); // both & and &amp; are stored in db :-|
        $parts = explode('&', $query);
        $eq = urlencode('=');
        foreach ($parts as $key=>$part) {
            $part = urlencode(urldecode($part));
            $part = str_replace($eq, '=', $part);
            $parts[$key] = $part;
        }
        $query = '?'.implode('&amp;', $parts);
    }

    return $script.$query;
martin's avatar
martin committed
125
126
}

127

128
129
function build_mnet_logs_array($hostid, $course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
                   $modname="", $modid=0, $modaction="", $groupid=0) {
130
    global $CFG, $DB;
131
132
133
134
135

    // It is assumed that $date is the GMT time of midnight for that day,
    // and so the next 86400 seconds worth of logs are printed.

    /// Setup for group handling.
136
137

    // TODO: I don't understand group/context/etc. enough to be able to do
138
139
    // something interesting with it here
    // What is the context of a remote course?
140

141
142
143
144
145
146
147
148
149
    /// If the group mode is separate, and this user does not have editing privileges,
    /// then only the user's group can be viewed.
    //if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
    //    $groupid = get_current_group($course->id);
    //}
    /// If this course doesn't have groups, no groupid can be specified.
    //else if (!$course->groupmode) {
    //    $groupid = 0;
    //}
150

151
152
153
154
    $groupid = 0;

    $joins = array();

155
156
157
158
159
160
161
162
    $qry = "SELECT l.*, u.firstname, u.lastname, u.picture
              FROM {mnet_log} l
               LEFT JOIN {user} u ON l.userid = u.id
              WHERE ";
    $params = array();

    $where .= "l.hostid = :hostid";
    $params['hostid'] = $hostid;
163
164

    // TODO: Is 1 really a magic number referring to the sitename?
165
166
167
    if ($course != SITEID || $modid != 0) {
        $where .= " AND l.course=:courseid";
        $params['courseid'] = $course;
168
169
170
    }

    if ($modname) {
171
172
        $where .= " AND l.module = :modname";
        $params['modname'] = $modname;
173
174
175
    }

    if ('site_errors' === $modid) {
176
        $where .= " AND ( l.action='error' OR l.action='infected' )";
177
    } else if ($modid) {
178
        //TODO: This assumes that modids are the same across sites... probably
179
        //not true
180
181
        $where .= " AND l.cmid = :modid";
        $params['modid'] = $modid;
182
183
184
185
    }

    if ($modaction) {
        $firstletter = substr($modaction, 0, 1);
186
        if ($firstletter == '-') {
187
            $where .= " AND ".$DB->sql_like('l.action', ':modaction', false, true, true);
188
189
190
191
            $params['modaction'] = '%'.substr($modaction, 1).'%';
        } else {
            $where .= " AND ".$DB->sql_like('l.action', ':modaction', false);
            $params['modaction'] = '%'.$modaction.'%';
192
193
194
195
        }
    }

    if ($user) {
196
197
        $where .= " AND l.userid = :user";
        $params['user'] = $user;
198
199
200
201
    }

    if ($date) {
        $enddate = $date + 86400;
202
203
204
        $where .= " AND l.time > :date AND l.time < :enddate";
        $params['date'] = $date;
        $params['enddate'] = $enddate;
205
206
207
    }

    $result = array();
208
    $result['totalcount'] = $DB->count_records_sql("SELECT COUNT('x') FROM {mnet_log} l WHERE $where", $params);
209
    if(!empty($result['totalcount'])) {
210
211
        $where .= " ORDER BY $order";
        $result['logs'] = $DB->get_records_sql("$qry $where", $params, $limitfrom, $limitnum);
212
213
214
215
216
217
    } else {
        $result['logs'] = array();
    }
    return $result;
}

218
219
function build_logs_array($course, $user=0, $date=0, $order="l.time ASC", $limitfrom='', $limitnum='',
                   $modname="", $modid=0, $modaction="", $groupid=0) {
Petr Skoda's avatar
Petr Skoda committed
220
    global $DB, $SESSION, $USER;
221
222
    // It is assumed that $date is the GMT time of midnight for that day,
    // and so the next 86400 seconds worth of logs are printed.
martin's avatar
martin committed
223

224
    /// Setup for group handling.
225

226
227
    /// If the group mode is separate, and this user does not have editing privileges,
    /// then only the user's group can be viewed.
toyomoyo's avatar
toyomoyo committed
228
    if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $course->id))) {
229
230
231
232
233
234
235
236
237
238
239
        if (isset($SESSION->currentgroup[$course->id])) {
            $groupid =  $SESSION->currentgroup[$course->id];
        } else {
            $groupid = groups_get_all_groups($course->id, $USER->id);
            if (is_array($groupid)) {
                $groupid = array_shift(array_keys($groupid));
                $SESSION->currentgroup[$course->id] = $groupid;
            } else {
                $groupid = 0;
            }
        }
240
241
242
243
244
245
    }
    /// If this course doesn't have groups, no groupid can be specified.
    else if (!$course->groupmode) {
        $groupid = 0;
    }

246
    $joins = array();
247
    $params = array();
martin's avatar
martin committed
248

249
    if ($course->id != SITEID || $modid != 0) {
250
251
        $joins[] = "l.course = :courseid";
        $params['courseid'] = $course->id;
252
    }
martin's avatar
martin committed
253

moodler's avatar
moodler committed
254
    if ($modname) {
255
        $joins[] = "l.module = :modname";
256
        $params['modname'] = $modname;
257
258
    }

moodler's avatar
moodler committed
259
    if ('site_errors' === $modid) {
260
        $joins[] = "( l.action='error' OR l.action='infected' )";
moodler's avatar
moodler committed
261
    } else if ($modid) {
262
263
        $joins[] = "l.cmid = :modid";
        $params['modid'] = $modid;
264
265
266
    }

    if ($modaction) {
267
        $firstletter = substr($modaction, 0, 1);
268
        if ($firstletter == '-') {
269
            $joins[] = $DB->sql_like('l.action', ':modaction', false, true, true);
270
            $params['modaction'] = '%'.substr($modaction, 1).'%';
271
272
273
        } else {
            $joins[] = $DB->sql_like('l.action', ':modaction', false);
            $params['modaction'] = '%'.$modaction.'%';
274
        }
275
276
    }

277

278
279
    /// Getting all members of a group.
    if ($groupid and !$user) {
280
281
        if ($gusers = groups_get_members($groupid)) {
            $gusers = array_keys($gusers);
282
283
            $joins[] = 'l.userid IN (' . implode(',', $gusers) . ')';
        } else {
284
            $joins[] = 'l.userid = 0'; // No users in groups, so we want something that will always be false.
285
286
287
        }
    }
    else if ($user) {
288
289
        $joins[] = "l.userid = :userid";
        $params['userid'] = $user;
martin's avatar
martin committed
290
291
292
293
    }

    if ($date) {
        $enddate = $date + 86400;
294
295
296
        $joins[] = "l.time > :date AND l.time < :enddate";
        $params['date'] = $date;
        $params['enddate'] = $enddate;
martin's avatar
martin committed
297
298
    }

299
    $selector = implode(' AND ', $joins);
300

301
    $totalcount = 0;  // Initialise
302
    $result = array();
303
    $result['logs'] = get_logs($selector, $params, $order, $limitfrom, $limitnum, $totalcount);
304
305
306
    $result['totalcount'] = $totalcount;
    return $result;
}
307
308


309
310
function print_log($course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
                   $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
311

312
    global $CFG, $DB, $OUTPUT;
313

314
315
    if (!$logs = build_logs_array($course, $user, $date, $order, $page*$perpage, $perpage,
                       $modname, $modid, $modaction, $groupid)) {
316
        echo $OUTPUT->notification("No logs found!");
317
        echo $OUTPUT->footer();
martin's avatar
martin committed
318
319
        exit;
    }
320

321
322
    $courses = array();

323
324
    if ($course->id == SITEID) {
        $courses[0] = '';
325
        if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
326
            foreach ($ccc as $cc) {
327
                $courses[$cc->id] = $cc->shortname;
328
329
            }
        }
330
    } else {
331
        $courses[$course->id] = $course->shortname;
332
    }
333

334
    $totalcount = $logs['totalcount'];
martin's avatar
martin committed
335
    $count=0;
336
    $ldcache = array();
martin's avatar
martin committed
337
338
    $tt = getdate(time());
    $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);
moodler's avatar
moodler committed
339

moodler's avatar
   
moodler committed
340
341
    $strftimedatetime = get_string("strftimedatetime");

342
    echo "<div class=\"info\">\n";
moodler's avatar
   
moodler committed
343
    print_string("displayingrecords", "", $totalcount);
344
    echo "</div>\n";
moodler's avatar
moodler committed
345

346
    echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
moodler's avatar
   
moodler committed
347

348
    $table = new html_table();
349
    $table->classes = array('logtable','generalbox');
350
351
352
353
354
355
356
357
358
    $table->align = array('right', 'left', 'left');
    $table->head = array(
        get_string('time'),
        get_string('ip_address'),
        get_string('fullnamecourse'),
        get_string('action'),
        get_string('info')
    );
    $table->data = array();
359

360
    if ($course->id == SITEID) {
361
362
        array_unshift($table->align, 'left');
        array_unshift($table->head, get_string('course'));
363
364
    }

365
    // Make sure that the logs array is an array, even it is empty, to avoid warnings from the foreach.
moodler's avatar
moodler committed
366
    if (empty($logs['logs'])) {
367
        $logs['logs'] = array();
moodler's avatar
moodler committed
368
    }
369

370
    foreach ($logs['logs'] as $log) {
martin's avatar
martin committed
371

372
373
374
        if (isset($ldcache[$log->module][$log->action])) {
            $ld = $ldcache[$log->module][$log->action];
        } else {
375
            $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
376
377
            $ldcache[$log->module][$log->action] = $ld;
        }
378
        if ($ld && is_numeric($log->info)) {
379
            // ugly hack to make sure fullname is shown correctly
380
            if ($ld->mtable == 'user' && $ld->field == $DB->sql_concat('firstname', "' '" , 'lastname')) {
381
                $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
382
            } else {
383
                $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
384
            }
martin's avatar
martin committed
385
386
        }

387
        //Filter log->info
stronk7's avatar
stronk7 committed
388
389
        $log->info = format_string($log->info);

390
391
392
393
394
        // If $log->url has been trimmed short by the db size restriction
        // code in add_to_log, keep a note so we don't add a link to a broken url
        $tl=textlib_get_instance();
        $brokenurl=($tl->strlen($log->url)==100 && $tl->substr($log->url,97)=='...');

395
        $row = array();
396
        if ($course->id == SITEID) {
397
            if (empty($log->course)) {
398
                $row[] = get_string('site');
399
            } else {
400
                $row[] = "<a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">". format_string($courses[$log->course])."</a>";
401
            }
402
        }
403

404
        $row[] = userdate($log->time, '%a').' '.userdate($log->time, $strftimedatetime);
405

406
407
        $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
        $row[] = $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 440, 'width' => 700)));
408

Petr Skoda's avatar
Petr Skoda committed
409
        $row[] = html_writer::link(new moodle_url("/user/view.php?id={$log->userid}&course={$log->course}"), fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id))));
410

411
        $displayaction="$log->module $log->action";
412
413
        if ($brokenurl) {
            $row[] = $displayaction;
414
        } else {
415
416
            $link = make_log_url($log->module,$log->url);
            $row[] = $OUTPUT->action_link($link, $displayaction, new popup_action('click', $link, 'fromloglive'), array('height' => 440, 'width' => 700));
417
        }
418
        $row[] = $log->info;
419
        $table->data[] = $row;
martin's avatar
martin committed
420
    }
moodler's avatar
   
moodler committed
421

422
    echo html_writer::table($table);
423
    echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
martin's avatar
martin committed
424
425
426
}


427
428
function print_mnet_log($hostid, $course, $user=0, $date=0, $order="l.time ASC", $page=0, $perpage=100,
                   $url="", $modname="", $modid=0, $modaction="", $groupid=0) {
429

430
    global $CFG, $DB, $OUTPUT;
431

432
433
    if (!$logs = build_mnet_logs_array($hostid, $course, $user, $date, $order, $page*$perpage, $perpage,
                       $modname, $modid, $modaction, $groupid)) {
434
        echo $OUTPUT->notification("No logs found!");
435
        echo $OUTPUT->footer();
436
437
        exit;
    }
438

439
440
441
442
443
444
445
446
    if ($course->id == SITEID) {
        $courses[0] = '';
        if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname,c.visible')) {
            foreach ($ccc as $cc) {
                $courses[$cc->id] = $cc->shortname;
            }
        }
    }
447

448
449
450
451
452
453
454
455
    $totalcount = $logs['totalcount'];
    $count=0;
    $ldcache = array();
    $tt = getdate(time());
    $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);

    $strftimedatetime = get_string("strftimedatetime");

456
    echo "<div class=\"info\">\n";
457
    print_string("displayingrecords", "", $totalcount);
458
    echo "</div>\n";
459

460
    echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
461

462
    echo "<table class=\"logtable\" cellpadding=\"3\" cellspacing=\"0\">\n";
463
464
465
466
467
468
    echo "<tr>";
    if ($course->id == SITEID) {
        echo "<th class=\"c0 header\">".get_string('course')."</th>\n";
    }
    echo "<th class=\"c1 header\">".get_string('time')."</th>\n";
    echo "<th class=\"c2 header\">".get_string('ip_address')."</th>\n";
469
    echo "<th class=\"c3 header\">".get_string('fullnamecourse')."</th>\n";
470
471
472
473
474
475
476
477
478
479
480
    echo "<th class=\"c4 header\">".get_string('action')."</th>\n";
    echo "<th class=\"c5 header\">".get_string('info')."</th>\n";
    echo "</tr>\n";

    if (empty($logs['logs'])) {
        echo "</table>\n";
        return;
    }

    $row = 1;
    foreach ($logs['logs'] as $log) {
481

482
483
484
485
486
487
        $log->info = $log->coursename;
        $row = ($row + 1) % 2;

        if (isset($ldcache[$log->module][$log->action])) {
            $ld = $ldcache[$log->module][$log->action];
        } else {
skodak's avatar
skodak committed
488
            $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
489
490
491
492
            $ldcache[$log->module][$log->action] = $ld;
        }
        if (0 && $ld && !empty($log->info)) {
            // ugly hack to make sure fullname is shown correctly
493
494
            if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
                $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
495
            } else {
496
                $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
497
498
499
            }
        }

500
        //Filter log->info
501
502
503
504
        $log->info = format_string($log->info);

        echo '<tr class="r'.$row.'">';
        if ($course->id == SITEID) {
505
            echo "<td class=\"r$row c0\" >\n";
506
507
508
            echo "    <a href=\"{$CFG->wwwroot}/course/view.php?id={$log->course}\">".$courses[$log->course]."</a>\n";
            echo "</td>\n";
        }
509
        echo "<td class=\"r$row c1\" align=\"right\">".userdate($log->time, '%a').
510
             ' '.userdate($log->time, $strftimedatetime)."</td>\n";
511
        echo "<td class=\"r$row c2\" >\n";
512
513
        $link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
        echo $OUTPUT->action_link($link, $log->ip, new popup_action('click', $link, 'iplookup', array('height' => 400, 'width' => 700)));
514
515
        echo "</td>\n";
        $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
516
        echo "<td class=\"r$row c3\" >\n";
517
518
        echo "    <a href=\"$CFG->wwwroot/user/view.php?id={$log->userid}\">$fullname</a>\n";
        echo "</td>\n";
519
        echo "<td class=\"r$row c4\">\n";
520
521
        echo $log->action .': '.$log->module;
        echo "</td>\n";;
522
        echo "<td class=\"r$row c5\">{$log->info}</td>\n";
523
524
525
526
        echo "</tr>\n";
    }
    echo "</table>\n";

527
    echo $OUTPUT->paging_bar($totalcount, $page, $perpage, "$url&perpage=$perpage");
528
529
530
}


531
532
function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
                        $modid, $modaction, $groupid) {
533
    global $DB;
534

moodler's avatar
moodler committed
535
    $text = get_string('course')."\t".get_string('time')."\t".get_string('ip_address')."\t".
536
            get_string('fullnamecourse')."\t".get_string('action')."\t".get_string('info');
537

moodler's avatar
moodler committed
538
    if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
539
540
541
                       $modname, $modid, $modaction, $groupid)) {
        return false;
    }
542

543
544
    $courses = array();

545
546
547
548
549
550
551
    if ($course->id == SITEID) {
        $courses[0] = '';
        if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
            foreach ($ccc as $cc) {
                $courses[$cc->id] = $cc->shortname;
            }
        }
552
553
    } else {
        $courses[$course->id] = $course->shortname;
554
    }
555

556
557
558
559
560
561
562
    $count=0;
    $ldcache = array();
    $tt = getdate(time());
    $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);

    $strftimedatetime = get_string("strftimedatetime");

563
    $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
moodler's avatar
moodler committed
564
    $filename .= '.txt';
565
    header("Content-Type: application/download\n");
moodler's avatar
moodler committed
566
567
568
569
570
571
572
573
    header("Content-Disposition: attachment; filename=$filename");
    header("Expires: 0");
    header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
    header("Pragma: public");

    echo get_string('savedat').userdate(time(), $strftimedatetime)."\n";
    echo $text;

moodler's avatar
moodler committed
574
575
576
577
    if (empty($logs['logs'])) {
        return true;
    }

moodler's avatar
moodler committed
578
579
580
581
    foreach ($logs['logs'] as $log) {
        if (isset($ldcache[$log->module][$log->action])) {
            $ld = $ldcache[$log->module][$log->action];
        } else {
skodak's avatar
skodak committed
582
            $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
moodler's avatar
moodler committed
583
584
585
586
            $ldcache[$log->module][$log->action] = $ld;
        }
        if ($ld && !empty($log->info)) {
            // ugly hack to make sure fullname is shown correctly
587
588
            if (($ld->mtable == 'user') and ($ld->field ==  $DB->sql_concat('firstname', "' '" , 'lastname'))) {
                $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
moodler's avatar
moodler committed
589
            } else {
590
                $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
moodler's avatar
moodler committed
591
592
593
            }
        }

594
        //Filter log->info
moodler's avatar
moodler committed
595
596
597
598
        $log->info = format_string($log->info);
        $log->info = strip_tags(urldecode($log->info));    // Some XSS protection

        $firstField = $courses[$log->course];
toyomoyo's avatar
toyomoyo committed
599
        $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
moodler's avatar
moodler committed
600
601
602
603
604
        $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action, $log->info);
        $text = implode("\t", $row);
        echo $text." \n";
    }
    return true;
605
606
607
608
609
}


function print_log_xls($course, $user, $date, $order='l.time DESC', $modname,
                        $modid, $modaction, $groupid) {
610

611
    global $CFG, $DB;
612

moodler's avatar
moodler committed
613
    require_once("$CFG->libdir/excellib.class.php");
614

moodler's avatar
moodler committed
615
    if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
616
617
618
                       $modname, $modid, $modaction, $groupid)) {
        return false;
    }
619

620
621
    $courses = array();

622
623
624
625
626
627
628
    if ($course->id == SITEID) {
        $courses[0] = '';
        if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
            foreach ($ccc as $cc) {
                $courses[$cc->id] = $cc->shortname;
            }
        }
629
630
    } else {
        $courses[$course->id] = $course->shortname;
631
    }
632

633
634
635
636
637
638
639
    $count=0;
    $ldcache = array();
    $tt = getdate(time());
    $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);

    $strftimedatetime = get_string("strftimedatetime");

moodler's avatar
moodler committed
640
    $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
641
    $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
moodler's avatar
moodler committed
642
    $filename .= '.xls';
643

644
645
    $workbook = new MoodleExcelWorkbook('-');
    $workbook->send($filename);
646

moodler's avatar
moodler committed
647
648
    $worksheet = array();
    $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
649
                        get_string('fullnamecourse'),    get_string('action'), get_string('info'));
650

moodler's avatar
moodler committed
651
652
    // Creating worksheets
    for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
653
        $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
moodler's avatar
moodler committed
654
655
656
657
658
659
660
661
662
663
664
        $worksheet[$wsnumber] =& $workbook->add_worksheet($sheettitle);
        $worksheet[$wsnumber]->set_column(1, 1, 30);
        $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
                                    userdate(time(), $strftimedatetime));
        $col = 0;
        foreach ($headers as $item) {
            $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
            $col++;
        }
    }

moodler's avatar
moodler committed
665
666
667
668
669
    if (empty($logs['logs'])) {
        $workbook->close();
        return true;
    }

moodler's avatar
moodler committed
670
671
672
673
674
675
676
677
678
679
    $formatDate =& $workbook->add_format();
    $formatDate->set_num_format(get_string('log_excel_date_format'));

    $row = FIRSTUSEDEXCELROW;
    $wsnumber = 1;
    $myxls =& $worksheet[$wsnumber];
    foreach ($logs['logs'] as $log) {
        if (isset($ldcache[$log->module][$log->action])) {
            $ld = $ldcache[$log->module][$log->action];
        } else {
680
            $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
moodler's avatar
moodler committed
681
682
683
684
            $ldcache[$log->module][$log->action] = $ld;
        }
        if ($ld && !empty($log->info)) {
            // ugly hack to make sure fullname is shown correctly
685
686
            if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
                $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
moodler's avatar
moodler committed
687
            } else {
688
                $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
moodler's avatar
moodler committed
689
690
691
692
693
694
695
696
697
698
699
700
701
702
            }
        }

        // Filter log->info
        $log->info = format_string($log->info);
        $log->info = strip_tags(urldecode($log->info));  // Some XSS protection

        if ($nroPages>1) {
            if ($row > EXCELROWS) {
                $wsnumber++;
                $myxls =& $worksheet[$wsnumber];
                $row = FIRSTUSEDEXCELROW;
            }
        }
703

moodler's avatar
moodler committed
704
        $myxls->write($row, 0, $courses[$log->course], '');
705
        $myxls->write_date($row, 1, $log->time, $formatDate); // write_date() does conversion/timezone support. MDL-14934
moodler's avatar
moodler committed
706
        $myxls->write($row, 2, $log->ip, '');
toyomoyo's avatar
toyomoyo committed
707
        $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
moodler's avatar
moodler committed
708
709
710
        $myxls->write($row, 3, $fullname, '');
        $myxls->write($row, 4, $log->module.' '.$log->action, '');
        $myxls->write($row, 5, $log->info, '');
711

moodler's avatar
moodler committed
712
713
714
715
        $row++;
    }

    $workbook->close();
716
717
718
719
720
721
    return true;
}

function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
                        $modid, $modaction, $groupid) {

722
    global $CFG, $DB;
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751

    require_once("$CFG->libdir/odslib.class.php");

    if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
                       $modname, $modid, $modaction, $groupid)) {
        return false;
    }

    $courses = array();

    if ($course->id == SITEID) {
        $courses[0] = '';
        if ($ccc = get_courses('all', 'c.id ASC', 'c.id,c.shortname')) {
            foreach ($ccc as $cc) {
                $courses[$cc->id] = $cc->shortname;
            }
        }
    } else {
        $courses[$course->id] = $course->shortname;
    }

    $count=0;
    $ldcache = array();
    $tt = getdate(time());
    $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]);

    $strftimedatetime = get_string("strftimedatetime");

    $nroPages = ceil(count($logs)/(EXCELROWS-FIRSTUSEDEXCELROW+1));
752
    $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
753
754
755
756
757
758
759
    $filename .= '.ods';

    $workbook = new MoodleODSWorkbook('-');
    $workbook->send($filename);

    $worksheet = array();
    $headers = array(get_string('course'), get_string('time'), get_string('ip_address'),
760
                        get_string('fullnamecourse'),    get_string('action'), get_string('info'));
761
762
763

    // Creating worksheets
    for ($wsnumber = 1; $wsnumber <= $nroPages; $wsnumber++) {
764
        $sheettitle = get_string('logs').' '.$wsnumber.'-'.$nroPages;
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
        $worksheet[$wsnumber] =& $workbook->add_worksheet($sheettitle);
        $worksheet[$wsnumber]->set_column(1, 1, 30);
        $worksheet[$wsnumber]->write_string(0, 0, get_string('savedat').
                                    userdate(time(), $strftimedatetime));
        $col = 0;
        foreach ($headers as $item) {
            $worksheet[$wsnumber]->write(FIRSTUSEDEXCELROW-1,$col,$item,'');
            $col++;
        }
    }

    if (empty($logs['logs'])) {
        $workbook->close();
        return true;
    }

    $formatDate =& $workbook->add_format();
    $formatDate->set_num_format(get_string('log_excel_date_format'));

    $row = FIRSTUSEDEXCELROW;
    $wsnumber = 1;
    $myxls =& $worksheet[$wsnumber];
    foreach ($logs['logs'] as $log) {
        if (isset($ldcache[$log->module][$log->action])) {
            $ld = $ldcache[$log->module][$log->action];
        } else {
791
            $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
792
793
794
795
            $ldcache[$log->module][$log->action] = $ld;
        }
        if ($ld && !empty($log->info)) {
            // ugly hack to make sure fullname is shown correctly
skodak's avatar
skodak committed
796
            if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
797
                $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
798
            } else {
799
                $log->info = $DB->get_field($ld->mtable, $ld->field, array('id'=>$log->info));
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
            }
        }

        // Filter log->info
        $log->info = format_string($log->info);
        $log->info = strip_tags(urldecode($log->info));  // Some XSS protection

        if ($nroPages>1) {
            if ($row > EXCELROWS) {
                $wsnumber++;
                $myxls =& $worksheet[$wsnumber];
                $row = FIRSTUSEDEXCELROW;
            }
        }

815
816
817
        $myxls->write_string($row, 0, $courses[$log->course]);
        $myxls->write_date($row, 1, $log->time);
        $myxls->write_string($row, 2, $log->ip);
818
        $fullname = fullname($log, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $course->id)));
819
820
821
        $myxls->write_string($row, 3, $fullname);
        $myxls->write_string($row, 4, $log->module.' '.$log->action);
        $myxls->write_string($row, 5, $log->info);
822
823
824
825
826

        $row++;
    }

    $workbook->close();
moodler's avatar
moodler committed
827
    return true;
828
829
830
}


moodler's avatar
moodler committed
831
function print_log_graph($course, $userid=0, $type="course.png", $date=0) {
832
    global $CFG, $USER;
moodler's avatar
moodler committed
833
834
    if (empty($CFG->gdversion)) {
        echo "(".get_string("gdneed").")";
835
    } else {
836
        // MDL-10818, do not display broken graph when user has no permission to view graph
837
        if (has_capability('coursereport/log:view', get_context_instance(CONTEXT_COURSE, $course->id)) ||
838
839
840
841
            ($course->showreports and $USER->id == $userid)) {
            echo '<img src="'.$CFG->wwwroot.'/course/report/log/graph.php?id='.$course->id.
                 '&amp;user='.$userid.'&amp;type='.$type.'&amp;date='.$date.'" alt="" />';
        }
842
843
844
845
    }
}


846
function print_overview($courses) {
847
    global $CFG, $USER, $DB, $OUTPUT;
848

849
    $htmlarray = array();
skodak's avatar
skodak committed
850
    if ($modules = $DB->get_records('modules')) {
851
852
        foreach ($modules as $mod) {
            if (file_exists(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php')) {
853
                include_once(dirname(dirname(__FILE__)).'/mod/'.$mod->name.'/lib.php');
854
                $fname = $mod->name.'_print_overview';
855
                if (function_exists($fname)) {
856
                    $fname($courses,$htmlarray);
857
858
859
860
                }
            }
        }
    }
861
    foreach ($courses as $course) {
862
        echo $OUTPUT->box_start("coursebox");
863
864
865
866
        $linkcss = '';
        if (empty($course->visible)) {
            $linkcss = 'class="dimmed"';
        }
867
        echo $OUTPUT->heading('<a title="'. format_string($course->fullname).'" '.$linkcss.' href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'. format_string($course->fullname).'</a>', 3);
868
869
870
871
872
        if (array_key_exists($course->id,$htmlarray)) {
            foreach ($htmlarray[$course->id] as $modname => $html) {
                echo $html;
            }
        }
873
        echo $OUTPUT->box_end();
874
    }
875
876
877
}


878
879
880
881
/**
 * This function trawls through the logs looking for
 * anything new since the user's last login
 */
martin's avatar
martin committed
882
883
function print_recent_activity($course) {
    // $course is an object
884
    global $CFG, $USER, $SESSION, $DB, $OUTPUT;
martin's avatar
martin committed
885

886
    $context = get_context_instance(CONTEXT_COURSE, $course->id);
887

888
889
890
    $viewfullnames = has_capability('moodle/site:viewfullnames', $context);

    $timestart = round(time() - COURSE_MAX_RECENT_PERIOD, -2); // better db caching for guests - 100 seconds
891

892
    if (!isguestuser()) {
893
894
895
896
        if (!empty($USER->lastcourseaccess[$course->id])) {
            if ($USER->lastcourseaccess[$course->id] > $timestart) {
                $timestart = $USER->lastcourseaccess[$course->id];
            }
897
        }
898
    }
899

moodler's avatar
moodler committed
900
    echo '<div class="activitydate">';
moodler's avatar
moodler committed
901
    echo get_string('activitysince', '', userdate($timestart));
moodler's avatar
moodler committed
902
903
    echo '</div>';
    echo '<div class="activityhead">';
904

moodler's avatar
moodler committed
905
    echo '<a href="'.$CFG->wwwroot.'/course/recent.php?id='.$course->id.'">'.get_string('recentactivityreport').'</a>';
906

nfreear's avatar
nfreear committed
907
    echo "</div>\n";
908

martin's avatar
martin committed
909
    $content = false;
910

911
912
/// Firstly, have there been any new enrolments?

913
    $users = get_recent_enrolments($course->id, $timestart);
914

nfreear's avatar
nfreear committed
915
    //Accessibility: new users now appear in an <OL> list.
916
    if ($users) {
moodler's avatar
moodler committed
917
        echo '<div class="newusers">';
918
        echo $OUTPUT->heading(get_string("newusers").':', 3);
919
        $content = true;
nfreear's avatar
nfreear committed
920
        echo "<ol class=\"list\">\n";
921
        foreach ($users as $user) {
922
923
            $fullname = fullname($user, $viewfullnames);
            echo '<li class="name"><a href="'."$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a></li>\n";
martin's avatar
martin committed
924
        }
nfreear's avatar
nfreear committed
925
        echo "</ol>\n</div>\n";
martin's avatar
martin committed
926
927
    }

928
929
930
931
932
/// Next, have there been any modifications to the course structure?

    $modinfo =& get_fast_modinfo($course);

    $changelist = array();
933

934