Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
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
 * Log report renderer.
19
 *
20
 * @package    report_log
21
 * @copyright  2014 Rajesh Taneja <rajesh.taneja@gmail.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die;
26
use core\log\manager;
27
 
28
/**
29
 * Report log renderable class.
30
 *
31
 * @package    report_log
32
 * @copyright  2014 Rajesh Taneja <rajesh.taneja@gmail.com>
33
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
35
class report_log_renderable implements renderable {
36
    /** @var manager log manager */
37
    protected $logmanager;
38
 
39
    /** @var string selected log reader pluginname */
40
    public $selectedlogreader = null;
41
 
42
    /** @var int page number */
43
    public $page;
44
 
45
    /** @var int perpage records to show */
46
    public $perpage;
47
 
48
    /** @var stdClass course record */
49
    public $course;
50
 
51
    /** @var moodle_url url of report page */
52
    public $url;
53
 
54
    /** @var int selected date from which records should be displayed */
55
    public $date;
56
 
57
    /** @var int selected user id for which logs are displayed */
58
    public $userid;
59
 
60
    /** @var int selected moduleid */
61
    public $modid;
62
 
63
    /** @var string selected action filter */
64
    public $action;
65
 
66
    /** @var int educational level */
67
    public $edulevel;
68
 
69
    /** @var bool show courses */
70
    public $showcourses;
71
 
72
    /** @var bool show users */
73
    public $showusers;
74
 
75
    /** @var bool show report */
76
    public $showreport;
77
 
78
    /** @var bool show selector form */
79
    public $showselectorform;
80
 
81
    /** @var string selected log format */
82
    public $logformat;
83
 
84
    /** @var string order to sort */
85
    public $order;
86
 
87
    /** @var string origin to filter event origin */
88
    public $origin;
89
 
90
    /** @var int group id */
91
    public $groupid;
92
 
1441 ariadna 93
    /** @var int forces the use of a course filter in site context */
94
    public $sitecoursefilter;
95
 
1 efrain 96
    /** @var table_log table log which will be used for rendering logs */
97
    public $tablelog;
98
 
1441 ariadna 99
    /** @var array Index of delegated sections (indexed by component and itemid) */
100
    protected $delegatedbycm;
1 efrain 101
 
1441 ariadna 102
    /** @var bool if the page is activity page */
103
    public $isactivitypage;
104
 
1 efrain 105
    /**
106
     * Constructor.
107
     *
108
     * @param string $logreader (optional)reader pluginname from which logs will be fetched.
109
     * @param stdClass|int $course (optional) course record or id
110
     * @param int $userid (optional) id of user to filter records for.
111
     * @param int|string $modid (optional) module id or site_errors for filtering errors.
112
     * @param string $action (optional) action name to filter.
113
     * @param int $groupid (optional) groupid of user.
114
     * @param int $edulevel (optional) educational level.
115
     * @param bool $showcourses (optional) show courses.
116
     * @param bool $showusers (optional) show users.
117
     * @param bool $showreport (optional) show report.
118
     * @param bool $showselectorform (optional) show selector form.
119
     * @param moodle_url|string $url (optional) page url.
120
     * @param int $date date (optional) timestamp of start of the day for which logs will be displayed.
121
     * @param string $logformat log format.
122
     * @param int $page (optional) page number.
123
     * @param int $perpage (optional) number of records to show per page.
124
     * @param string $order (optional) sortorder of fetched records
1441 ariadna 125
     * @param string $origin (optional) origin of the event.
126
     * @param bool $isactivitypage (optional) if the page is activity page.
127
     * @param int $sitecoursefilter (optional) use a course filter in site context.
1 efrain 128
     */
1441 ariadna 129
    public function __construct(
130
        $logreader = "",
131
        $course = 0,
132
        $userid = 0,
133
        $modid = 0,
134
        $action = "",
135
        $groupid = 0,
136
        $edulevel = -1,
137
        $showcourses = false,
138
        $showusers = false,
139
        $showreport = true,
140
        $showselectorform = true,
141
        $url = "",
142
        $date = 0,
143
        $logformat='showashtml',
144
        $page = 0,
145
        $perpage = 100,
146
        $order = "timecreated ASC",
147
        $origin ='',
148
        bool $isactivitypage = false,
149
        $sitecoursefilter = 0,
150
    ) {
1 efrain 151
 
1441 ariadna 152
        global $PAGE, $SITE;
1 efrain 153
 
154
        // Use first reader as selected reader, if not passed.
155
        if (empty($logreader)) {
156
            $readers = $this->get_readers();
157
            if (!empty($readers)) {
158
                reset($readers);
159
                $logreader = key($readers);
160
            } else {
161
                $logreader = null;
162
            }
163
        }
164
        // Use page url if empty.
165
        if (empty($url)) {
166
            $url = new moodle_url($PAGE->url);
167
        } else {
168
            $url = new moodle_url($url);
169
        }
170
        $this->selectedlogreader = $logreader;
171
        $url->param('logreader', $logreader);
172
 
173
        // Use site course id, if course is empty.
174
        if (!empty($course) && is_int($course)) {
1441 ariadna 175
            $courseid = $course;
176
            try {
177
                $course = get_course($courseid);
178
            } catch (dml_missing_record_exception) {
179
                // Missing courses may have be deleted, so display them in site context.
180
                $course = $SITE;
181
                $sitecoursefilter = $courseid;
182
            }
1 efrain 183
        }
184
        $this->course = $course;
185
 
186
        $this->userid = $userid;
187
        $this->date = $date;
188
        $this->page = $page;
189
        $this->perpage = $perpage;
190
        $this->url = $url;
191
        $this->order = $order;
192
        $this->modid = $modid;
193
        $this->action = $action;
194
        $this->groupid = $groupid;
195
        $this->edulevel = $edulevel;
196
        $this->showcourses = $showcourses;
197
        $this->showusers = $showusers;
198
        $this->showreport = $showreport;
199
        $this->showselectorform = $showselectorform;
200
        $this->logformat = $logformat;
201
        $this->origin = $origin;
1441 ariadna 202
        $this->isactivitypage = $isactivitypage;
203
        $this->sitecoursefilter = $sitecoursefilter;
1 efrain 204
    }
205
 
206
    /**
207
     * Get a list of enabled sql_reader objects/name
208
     *
209
     * @param bool $nameonly if true only reader names will be returned.
210
     * @return array core\log\sql_reader object or name.
211
     */
212
    public function get_readers($nameonly = false) {
213
        if (!isset($this->logmanager)) {
214
            $this->logmanager = get_log_manager();
215
        }
216
 
217
        $readers = $this->logmanager->get_readers('core\log\sql_reader');
218
        if ($nameonly) {
219
            foreach ($readers as $pluginname => $reader) {
220
                $readers[$pluginname] = $reader->get_name();
221
            }
222
        }
223
        return $readers;
224
    }
225
 
226
    /**
227
     * Helper function to return list of activities to show in selection filter.
228
     *
229
     * @return array list of activities.
230
     */
231
    public function get_activities_list() {
1441 ariadna 232
        $activities = [];
233
        $disabled = [];
1 efrain 234
 
235
        // For site just return site errors option.
236
        $sitecontext = context_system::instance();
237
        if ($this->course->id == SITEID && has_capability('report/log:view', $sitecontext)) {
238
            $activities["site_errors"] = get_string("siteerrors");
1441 ariadna 239
            return [$activities, $disabled];
1 efrain 240
        }
241
 
242
        $modinfo = get_fast_modinfo($this->course);
1441 ariadna 243
        if (!$this->delegatedbycm) {
244
            $this->delegatedbycm = $modinfo->get_sections_delegated_by_cm();
245
        }
246
 
1 efrain 247
        if (!empty($modinfo->cms)) {
248
            $section = 0;
249
            $thissection = array();
250
            foreach ($modinfo->cms as $cm) {
1441 ariadna 251
                if (!$modname = $this->get_activity_name($cm)) {
1 efrain 252
                    continue;
253
                }
1441 ariadna 254
 
1 efrain 255
                if ($cm->sectionnum > 0 and $section <> $cm->sectionnum) {
1441 ariadna 256
                    $sectioninfo = $modinfo->get_section_info($cm->sectionnum);
257
 
258
                    // Don't show subsections here. We are showing them in the corresponding module.
259
                    if ($sectioninfo->get_component_instance()) {
260
                        continue;
261
                    }
262
 
1 efrain 263
                    $activities[] = $thissection;
264
                    $thissection = array();
265
                }
266
                $section = $cm->sectionnum;
267
                $key = get_section_name($this->course, $cm->sectionnum);
268
                if (!isset($thissection[$key])) {
1441 ariadna 269
                    $thissection[$key] = [];
1 efrain 270
                }
271
                $thissection[$key][$cm->id] = $modname;
1441 ariadna 272
                // Check if the module is delegating a section.
273
                if (array_key_exists($cm->id, $this->delegatedbycm)) {
274
                    $delegated = $this->delegatedbycm[$cm->id];
275
                    $modules = (empty($delegated->sequence)) ? [] : explode(',', $delegated->sequence);
276
                    $thissection[$key] = $thissection[$key] + $this->get_delegated_section_activities($modinfo, $modules);
277
                    $disabled[] = $cm->id;
278
                }
1 efrain 279
            }
280
            if (!empty($thissection)) {
281
                $activities[] = $thissection;
282
            }
283
        }
1441 ariadna 284
        return [$activities, $disabled];
285
    }
286
 
287
    /**
288
     * Helper function to return list of activities in a delegated section.
289
     *
290
     * @param course_modinfo $modinfo
291
     * @param array $cms List of cm ids in the section.
292
     * @return array list of activities.
293
     */
294
    protected function get_delegated_section_activities(course_modinfo $modinfo, array $cmids): array {
295
        $activities = [];
296
        $indenter = '&nbsp;&nbsp;&nbsp;&nbsp;';
297
        foreach ($cmids as $cmid) {
298
            $cm = $modinfo->cms[$cmid];
299
            if ($modname = $this->get_activity_name($cm)) {
300
                $activities[$cmid] = $indenter.$modname;
301
            }
302
        }
1 efrain 303
        return $activities;
304
    }
305
 
306
    /**
1441 ariadna 307
     * Helper function to return the name to show in the dropdown.
308
     *
309
     * @param cm_info $cm
310
     * @return string The name.
311
     */
312
    private function get_activity_name(cm_info $cm): string {
313
        // Exclude activities that aren't visible or have no view link (e.g. label). Account for folders displayed inline.
314
        // Activities delegating sections might not have a URL, but should be return a name to be shown.
315
        $tobeshown = (strcmp($cm->modname, 'folder') == 0) || array_key_exists($cm->id, $this->delegatedbycm);
316
        if (!$cm->uservisible || (!$cm->has_view() && !$tobeshown)) {
317
            return '';
318
        }
319
        $modname = strip_tags($cm->get_formatted_name());
320
        if (core_text::strlen($modname) > 55) {
321
            $modname = core_text::substr($modname, 0, 50)."...";
322
        }
323
        if (!$cm->visible) {
324
            $modname = "(".$modname.")";
325
        }
326
 
327
        return $modname;
328
    }
329
 
330
    /**
1 efrain 331
     * Helper function to get selected group.
332
     *
333
     * @return int selected group.
334
     */
335
    public function get_selected_group() {
336
        global $SESSION, $USER;
337
 
338
        // No groups for system.
339
        if (empty($this->course)) {
340
            return 0;
341
        }
342
 
343
        $context = context_course::instance($this->course->id);
344
 
345
        $selectedgroup = 0;
346
        // Setup for group handling.
347
        $groupmode = groups_get_course_groupmode($this->course);
348
        if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
349
            if (isset($SESSION->currentgroup[$this->course->id])) {
350
                $selectedgroup = $SESSION->currentgroup[$this->course->id];
351
            } else if ($this->groupid > 0) {
352
                $SESSION->currentgroup[$this->course->id] = $this->groupid;
353
                $selectedgroup = $this->groupid;
354
            }
355
        } else if ($groupmode) {
356
            $selectedgroup = $this->groupid;
357
        }
358
        return $selectedgroup;
359
    }
360
 
361
    /**
362
     * Return list of actions for log reader.
363
     *
364
     * @todo MDL-44528 Get list from log_store.
365
     * @return array list of action options.
366
     */
367
    public function get_actions() {
368
        $actions = array(
369
                'c' => get_string('create'),
370
                'r' => get_string('view'),
371
                'u' => get_string('update'),
372
                'd' => get_string('delete'),
373
                'cud' => get_string('allchanges')
374
                );
375
        return $actions;
376
    }
377
 
378
    /**
379
     * Return selected user fullname.
380
     *
381
     * @return string user fullname.
382
     */
383
    public function get_selected_user_fullname() {
384
        $user = core_user::get_user($this->userid);
385
        if (empty($this->course)) {
386
            // We are in system context.
387
            $context = context_system::instance();
388
        } else {
389
            // We are in course context.
390
            $context = context_course::instance($this->course->id);
391
        }
392
        return fullname($user, has_capability('moodle/site:viewfullnames', $context));
393
    }
394
 
395
    /**
396
     * Return list of courses to show in selector.
397
     *
398
     * @return array list of courses.
399
     */
400
    public function get_course_list() {
401
        global $DB, $SITE;
402
 
403
        $courses = array();
404
 
405
        $sitecontext = context_system::instance();
406
        // First check to see if we can override showcourses and showusers.
407
        $numcourses = $DB->count_records("course");
408
        if ($numcourses < COURSE_MAX_COURSES_PER_DROPDOWN && !$this->showcourses) {
409
            $this->showcourses = 1;
410
        }
411
 
412
        // Check if course filter should be shown.
413
        if (has_capability('report/log:view', $sitecontext) && $this->showcourses) {
414
            if ($courserecords = $DB->get_records("course", null, "fullname", "id,shortname,fullname,category")) {
415
                foreach ($courserecords as $course) {
416
                    if ($course->id == SITEID) {
417
                        $courses[$course->id] = format_string($course->fullname) . ' (' . get_string('site') . ')';
418
                    } else {
419
                        $courses[$course->id] = format_string(get_course_display_name_for_list($course));
420
                    }
421
                }
422
            }
1441 ariadna 423
            // If filtering by a missing course, add a placeholder.
424
            if (!empty($this->sitecoursefilter)) {
425
                $courses[$this->sitecoursefilter] = get_string('missingcourse', 'report_log', [
426
                    'instanceid' => $this->sitecoursefilter,
427
                ]);
428
            }
1 efrain 429
            core_collator::asort($courses);
430
        }
431
        return $courses;
432
    }
433
 
434
    /**
435
     * Return list of groups that are used in this course. This is done when groups are used in the course
436
     * and the user is allowed to see all groups or groups are visible anyway. If groups are used but the
437
     * mode is separate groups and the user is not allowed to see all groups, the list contains the groups
438
     * only, where the user is member.
439
     * If the course uses no groups, the list is empty.
440
     *
441
     * @return array list of groups.
442
     */
443
    public function get_group_list() {
444
        global $USER;
445
 
446
        // No groups for system.
447
        if (empty($this->course)) {
448
            return [];
449
        }
450
 
451
        $context = context_course::instance($this->course->id);
452
        $groupmode = groups_get_course_groupmode($this->course);
453
        $grouplist = [];
454
        $userid = $groupmode == SEPARATEGROUPS ? $USER->id : 0;
455
        if (has_capability('moodle/site:accessallgroups', $context)) {
456
            $userid = 0;
457
        }
458
        $cgroups = groups_get_all_groups($this->course->id, $userid);
459
        if (!empty($cgroups)) {
460
            $grouplist = array_column($cgroups, 'name', 'id');
461
        }
462
        return $grouplist;
463
    }
464
 
465
    /**
466
     * Return list of users.
467
     *
468
     * @return array list of users.
469
     */
470
    public function get_user_list() {
471
        global $CFG, $SITE;
472
 
473
        $courseid = $SITE->id;
474
        if (!empty($this->course)) {
475
            $courseid = $this->course->id;
476
        }
477
        $context = context_course::instance($courseid);
478
        $limitfrom = empty($this->showusers) ? 0 : '';
479
        $limitnum = empty($this->showusers) ? COURSE_MAX_USERS_PER_DROPDOWN + 1 : '';
480
        $userfieldsapi = \core_user\fields::for_name();
481
 
482
        // Get the groups of that course that the user can see.
483
        $groups = $this->get_group_list();
484
        $groupids = array_keys($groups);
485
        // Now doublecheck the value of groupids and deal with special case like USERWITHOUTGROUP.
486
        $groupmode = groups_get_course_groupmode($this->course);
487
        if (
488
            has_capability('moodle/site:accessallgroups', $context)
489
            || $groupmode != SEPARATEGROUPS
490
            || empty($groupids)
491
        ) {
492
            $groupids[] = USERSWITHOUTGROUP;
493
        }
494
        // First case, the user has selected a group and user is in this group.
495
        if ($this->groupid > 0) {
496
            if (!isset($groups[$this->groupid])) {
497
                // The user is not in this group, so we will ignore the group selection.
498
                $groupids = 0;
499
            } else {
500
                $groupids = [$this->groupid];
501
            }
502
        }
503
        $courseusers = get_enrolled_users($context, '', $groupids, 'u.id, ' .
504
            $userfieldsapi->get_sql('u', false, '', '', false)->selects,
505
            null, $limitfrom, $limitnum);
506
 
507
        if (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && !$this->showusers) {
508
            $this->showusers = 1;
509
        }
510
 
511
        $users = array();
512
        if ($this->showusers) {
513
            if ($courseusers) {
514
                foreach ($courseusers as $courseuser) {
515
                     $users[$courseuser->id] = fullname($courseuser, has_capability('moodle/site:viewfullnames', $context));
516
                }
517
            }
518
            $users[$CFG->siteguest] = get_string('guestuser');
519
        }
520
        return $users;
521
    }
522
 
523
    /**
524
     * Return list of date options.
525
     *
526
     * @return array date options.
527
     */
528
    public function get_date_options() {
529
        global $SITE;
530
 
531
        $strftimedate = get_string("strftimedate");
532
        $strftimedaydate = get_string("strftimedaydate");
533
 
534
        // Get all the possible dates.
535
        // Note that we are keeping track of real (GMT) time and user time.
536
        // User time is only used in displays - all calcs and passing is GMT.
537
        $timenow = time(); // GMT.
538
 
539
        // What day is it now for the user, and when is midnight that day (in GMT).
540
        $timemidnight = usergetmidnight($timenow);
541
 
542
        // Put today up the top of the list.
543
        $dates = array("$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate) );
544
 
545
        // If course is empty, get it from frontpage.
546
        $course = $SITE;
547
        if (!empty($this->course)) {
548
            $course = $this->course;
549
        }
550
        if (!$course->startdate or ($course->startdate > $timenow)) {
551
            $course->startdate = $course->timecreated;
552
        }
553
 
554
        $numdates = 1;
555
        while ($timemidnight > $course->startdate and $numdates < 365) {
556
            $timemidnight = $timemidnight - 86400;
557
            $timenow = $timenow - 86400;
558
            $dates["$timemidnight"] = userdate($timenow, $strftimedaydate);
559
            $numdates++;
560
        }
561
        return $dates;
562
    }
563
 
564
    /**
565
     * Return list of components to show in selector.
566
     *
567
     * @return array list of origins.
568
     */
569
    public function get_origin_options() {
570
        $ret = array();
571
        $ret[''] = get_string('allsources', 'report_log');
572
        $ret['cli'] = get_string('cli', 'report_log');
573
        $ret['restore'] = get_string('restore', 'report_log');
574
        $ret['web'] = get_string('web', 'report_log');
575
        $ret['ws'] = get_string('ws', 'report_log');
576
        $ret['---'] = get_string('other', 'report_log');
577
        return $ret;
578
    }
579
 
580
    /**
581
     * Return list of edulevel.
582
     *
583
     * @todo MDL-44528 Get list from log_store.
584
     * @return array list of edulevels.
585
     */
586
    public function get_edulevel_options() {
587
        $edulevels = array(
588
                    -1 => get_string("edulevel"),
589
                    1 => get_string('edulevelteacher'),
590
                    2 => get_string('edulevelparticipating'),
591
 
592
                    );
593
        return $edulevels;
594
    }
595
 
596
    /**
597
     * Setup table log.
598
     */
599
    public function setup_table() {
600
        $readers = $this->get_readers();
601
 
602
        $filter = new \stdClass();
603
        if (!empty($this->course)) {
604
            $filter->courseid = $this->course->id;
605
        } else {
606
            $filter->courseid = 0;
607
        }
608
 
609
        $filter->userid = $this->userid;
610
        $filter->modid = $this->modid;
611
        $filter->groupid = $this->get_selected_group();
612
        $filter->logreader = $readers[$this->selectedlogreader];
613
        $filter->edulevel = $this->edulevel;
614
        $filter->action = $this->action;
615
        $filter->date = $this->date;
616
        $filter->orderby = $this->order;
617
        $filter->origin = $this->origin;
1441 ariadna 618
        $filter->sitecoursefilter = $this->sitecoursefilter;
1 efrain 619
        // If showing site_errors.
620
        if ('site_errors' === $this->modid) {
621
            $filter->siteerrors = true;
622
            $filter->modid = 0;
623
        }
624
 
625
        $this->tablelog = new report_log_table_log('report_log', $filter);
626
        $this->tablelog->define_baseurl($this->url);
627
        $this->tablelog->is_downloadable(true);
628
        $this->tablelog->show_download_buttons_at(array(TABLE_P_BOTTOM));
629
    }
630
 
631
    /**
632
     * Download logs in specified format.
633
     */
634
    public function download() {
635
        $filename = 'logs_' . userdate(time(), get_string('backupnameformat', 'langconfig'), 99, false);
636
        if ($this->course->id !== SITEID) {
637
            $courseshortname = format_string($this->course->shortname, true,
638
                    array('context' => context_course::instance($this->course->id)));
639
            $filename = clean_filename('logs_' . $courseshortname . '_' . userdate(time(),
640
                    get_string('backupnameformat', 'langconfig'), 99, false));
641
        }
642
        $this->tablelog->is_downloading($this->logformat, $filename);
643
        $this->tablelog->out($this->perpage, false);
644
    }
645
}