From ac8976c8b5d6caa68a886fc32c3c4fdd8974bf95 Mon Sep 17 00:00:00 2001 From: Rajesh Taneja Date: Thu, 13 Feb 2014 14:28:40 +0800 Subject: [PATCH] MDL-43681 report_log: Modified log report to use log readers --- report/log/classes/renderable.php | 499 ++++++++++++++++++++++++++++++ report/log/classes/renderer.php | 177 +++++++++++ report/log/classes/table_log.php | 438 ++++++++++++++++++++++++++ report/log/graph.php | 243 ++++++++------- report/log/index.php | 230 ++++++-------- report/log/lang/en/report_log.php | 7 + report/log/locallib.php | 382 +++++++---------------- report/log/settings.php | 7 +- report/log/user.php | 26 +- report/log/version.php | 2 +- 10 files changed, 1483 insertions(+), 528 deletions(-) create mode 100644 report/log/classes/renderable.php create mode 100644 report/log/classes/renderer.php create mode 100644 report/log/classes/table_log.php diff --git a/report/log/classes/renderable.php b/report/log/classes/renderable.php new file mode 100644 index 00000000000..10e6b7e47c3 --- /dev/null +++ b/report/log/classes/renderable.php @@ -0,0 +1,499 @@ +. + +/** + * Log report renderer. + * + * @package report_log + * @copyright 2014 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; +use core\log\manager; + +/** + * Report log renderable class. + * + * @package report_log + * @copyright 2014 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class report_log_renderable implements renderable { + /** @var manager log manager */ + protected $logmanager; + + /** @var string selected log reader pluginname */ + public $selectedlogreader = null; + + /** @var int page number */ + public $page; + + /** @var int perpage records to show */ + public $perpage; + + /** @var stdClass course record */ + public $course; + + /** @var moodle_url url of report page */ + public $url; + + /** @var int selected date from which records should be displayed */ + public $date; + + /** @var int selected user id for which logs are displayed */ + public $userid; + + /** @var int selected moduleid */ + public $modid; + + /** @var string selected action filter */ + public $action; + + /** @var int educational level */ + public $edulevel; + + /** @var bool show courses */ + public $showcourses; + + /** @var bool show users */ + public $showusers; + + /** @var bool show report */ + public $showreport; + + /** @var bool show selector form */ + public $showselectorform; + + /** @var string selected log format */ + public $logformat; + + /** @var string order to sort */ + public $order; + + /** @var int group id */ + public $groupid; + + /** @var table_log table log which will be used for rendering logs */ + public $tablelog; + + /** + * Constructor. + * + * @param string $logreader (optional)reader pluginname from which logs will be fetched. + * @param stdClass|int $course (optional) course record or id + * @param int $userid (optional) id of user to filter records for. + * @param int|string $modid (optional) module id or site_errors for filtering errors. + * @param string $action (optional) action name to filter. + * @param int $groupid (optional) groupid of user. + * @param int $edulevel (optional) educational level. + * @param bool $showcourses (optional) show courses. + * @param bool $showusers (optional) show users. + * @param bool $showreport (optional) show report. + * @param bool $showselectorform (optional) show selector form. + * @param moodle_url|string $url (optional) page url. + * @param int $date date (optional) from which records will be fetched. + * @param string $logformat log format. + * @param int $page (optional) page number. + * @param int $perpage (optional) number of records to show per page. + * @param string $order (optional) sortorder of fetched records + */ + public function __construct($logreader = "", $course = 0, $userid = 0, $modid = 0, $action = "", $groupid = 0, $edulevel = -1, + $showcourses = false, $showusers = false, $showreport = true, $showselectorform = true, $url = "", $date = 0, + $logformat='showashtml', $page = 0, $perpage = 100, $order = "timecreated ASC") { + + global $PAGE; + + // Use first reader as selected reader, if not passed. + if (empty($logreader)) { + $readers = $this->get_readers(); + if (!empty($readers)) { + reset($readers); + $logreader = key($readers); + } else { + $logreader = null; + } + } + // Use page url if empty. + if (empty($url)) { + $url = new moodle_url($PAGE->url); + } else { + $url = new moodle_url($url); + } + $this->selectedlogreader = $logreader; + + // Use site course id, if course is empty. + if (!empty($course) && is_int($course)) { + $course = get_course($course); + } + $this->course = $course; + + $this->userid = $userid; + $this->date = $date; + $this->page = $page; + $this->perpage = $perpage; + $this->url = $url; + $this->order = $order; + $this->modid = $modid; + $this->action = $action; + $this->groupid = $groupid; + $this->edulevel = $edulevel; + $this->showcourses = $showcourses; + $this->showusers = $showusers; + $this->showreport = $showreport; + $this->showselectorform = $showselectorform; + $this->logformat = $logformat; + } + + /** + * Get a list of enabled sql_select_reader objects/name + * + * @param bool $nameonly if true only reader names will be returned. + * @return array core\log\sql_select_reader object or name. + */ + public function get_readers($nameonly = false) { + if (!isset($this->manager)) { + $this->logmanager = get_log_manager(); + } + + $readers = $this->logmanager->get_readers('core\log\sql_select_reader'); + if ($nameonly) { + foreach ($readers as $pluginname => $reader) { + $readers[$pluginname] = $reader->get_name(); + } + } + return $readers; + } + + /** + * Helper function to return list of activities to show in selection filter. + * + * @return array list of activities. + */ + public function get_activities_list() { + $activities = array(); + + // For site just return site errors option. + $sitecontext = context_system::instance(); + if (empty($this->course) && has_capability('report/log:view', $sitecontext)) { + $activities["site_errors"] = get_string("siteerrors"); + return $activities; + } + + $modinfo = get_fast_modinfo($this->course); + if (!empty($modinfo->cms)) { + $section = 0; + $thissection = array(); + foreach ($modinfo->cms as $cm) { + if (!$cm->uservisible || !$cm->has_view()) { + continue; + } + if ($cm->sectionnum > 0 and $section <> $cm->sectionnum) { + $activities[] = $thissection; + $thissection = array(); + } + $section = $cm->sectionnum; + $modname = strip_tags($cm->get_formatted_name()); + if (core_text::strlen($modname) > 55) { + $modname = core_text::substr($modname, 0, 50)."..."; + } + if (!$cm->visible) { + $modname = "(".$modname.")"; + } + $key = get_section_name($this->course, $cm->sectionnum); + if (!isset($thissection[$key])) { + $thissection[$key] = array(); + } + $thissection[$key][$cm->id] = $modname; + } + if (!empty($thissection)) { + $activities[] = $thissection; + } + } + return $activities; + } + + /** + * Helper function to get selected group. + * + * @return int selected group. + */ + public function get_selected_group() { + global $SESSION, $USER; + + // No groups for system. + if (empty($this->course)) { + return 0; + } + + $context = context_course::instance($this->course->id); + + $selectedgroup = 0; + // Setup for group handling. + $groupmode = groups_get_course_groupmode($this->course); + if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { + $selectedgroup = -1; + } else if ($groupmode) { + $selectedgroup = $this->groupid; + } else { + $selectedgroup = 0; + } + + if ($selectedgroup === -1) { + if (isset($SESSION->currentgroup[$this->course->id])) { + $selectedgroup = $SESSION->currentgroup[$this->course->id]; + } else { + $selectedgroup = groups_get_all_groups($this->course->id, $USER->id); + if (is_array($selectedgroup)) { + $groupids = array_keys($selectedgroup); + $selectedgroup = array_shift($groupids); + $SESSION->currentgroup[$this->course->id] = $selectedgroup; + } else { + $selectedgroup = 0; + } + } + } + return $selectedgroup; + } + + /** + * Return list of actions for log reader. + * + * @todo MDL-44528 Get list from log_store. + * @return array list of action options. + */ + public function get_actions() { + $actions = array( + 'c' => get_string('create'), + 'r' => get_string('view'), + 'u' => get_string('update'), + 'd' => get_string('delete'), + '' => get_string('allchanges') + ); + return $actions; + } + + /** + * Return selected user fullname. + * + * @return string user fullname. + */ + public function get_selected_user_fullname() { + $user = core_user::get_user($this->userid); + return fullname($user); + } + + /** + * Return list of courses to show in selector. + * + * @return array list of courses. + */ + public function get_course_list() { + global $DB, $SITE; + + $courses = array(); + + $sitecontext = context_system::instance(); + // First check to see if we can override showcourses and showusers. + $numcourses = $DB->count_records("course"); + // Check if course filter should be shown. + if ((has_capability('report/log:view', $sitecontext)) && ($this->showcourses || + (empty($this->showcourses) && ($numcourses < COURSE_MAX_COURSES_PER_DROPDOWN)))) { + $courses[0] = get_string('sitelogs'); + $this->showcourses = true; + if ($courserecords = $DB->get_records("course", null, "fullname", "id,shortname,fullname,category")) { + foreach ($courserecords as $course) { + if ($course->category) { + $courses[$course->id] = format_string(get_course_display_name_for_list($course)); + } else { + $courses[$course->id] = $SITE->shortname; + } + } + } + core_collator::asort($courses); + } else { + if (!empty($this->course->id)) { + $coursecontext = context_course::instance($this->course->id); + if (has_capability('report/log:view', $coursecontext)) { + $courses[$this->course->id] = format_string(get_course_display_name_for_list($this->course)); + } else { + $this->showcourses = false; + } + } else { + $this->showcourses = false; + } + } + return $courses; + } + + /** + * Return list of groups. + * + * @return array list of groups. + */ + public function get_group_list() { + + // No groups for system. + if (empty($this->course)) { + return array(); + } + + $context = context_course::instance($this->course->id); + $groups = array(); + $groupmode = groups_get_course_groupmode($this->course); + if (($groupmode == VISIBLEGROUPS) || + ($groupmode == SEPARATEGROUPS and has_capability('moodle/site:accessallgroups', $context))) { + // Get all groups. + if ($cgroups = groups_get_all_groups($this->course->id)) { + foreach ($cgroups as $cgroup) { + $groups[$cgroup->id] = $cgroup->name; + } + } + } + return $groups; + } + + /** + * Return list of users. + * + * @return array list of users. + */ + public function get_user_list() { + global $CFG, $SITE; + + $courseid = $SITE->id; + if (!empty($this->course)) { + $courseid = $this->course->id; + } + $context = context_course::instance($courseid); + $limitfrom = empty($this->showusers) ? 0 : ''; + $limitnum = empty($this->showusers) ? COURSE_MAX_USERS_PER_DROPDOWN + 1 : ''; + $courseusers = get_enrolled_users($context, '', $this->groupid, 'u.id, ' . get_all_user_name_fields(true, 'u'), + null, $limitfrom, $limitnum); + + $users = array(); + if (($this->showusers) || (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && empty($this->showusers))) { + $this->showusers = true; + if ($courseusers) { + foreach ($courseusers as $courseuser) { + $users[$courseuser->id] = fullname($courseuser, has_capability('moodle/site:viewfullnames', $context)); + } + } + $users[$CFG->siteguest] = get_string('guestuser'); + } + return $users; + } + + /** + * Return list of date options. + * + * @return array date options. + */ + public function get_date_options() { + global $SITE; + + $strftimedate = get_string("strftimedate"); + $strftimedaydate = get_string("strftimedaydate"); + + // Get all the possible dates. + // Note that we are keeping track of real (GMT) time and user time. + // User time is only used in displays - all calcs and passing is GMT. + $timenow = time(); // GMT. + + // What day is it now for the user, and when is midnight that day (in GMT). + $timemidnight = usergetmidnight($timenow); + + // Put today up the top of the list. + $dates = array("$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate) ); + + // If course is empty, get it from frontpage. + $course = $SITE; + if (!empty($this->course)) { + $course = $this->course; + } + if (!$course->startdate or ($course->startdate > $timenow)) { + $course->startdate = $course->timecreated; + } + + $numdates = 1; + while ($timemidnight > $course->startdate and $numdates < 365) { + $timemidnight = $timemidnight - 86400; + $timenow = $timenow - 86400; + $dates["$timemidnight"] = userdate($timenow, $strftimedaydate); + $numdates++; + } + return $dates; + } + + /** + * Return list of edulevel. + * + * @todo MDL-44528 Get list from log_store. + * @return array list of edulevels. + */ + public function get_edulevel_options() { + $edulevels = array( + -1 => get_string("edulevel"), + 1 => get_string('edulevelteacher'), + 2 => get_string('edulevelparticipating'), + 0 => get_string('edulevelother') + ); + return $edulevels; + } + + /** + * Setup table log. + */ + public function setup_table() { + $readers = $this->get_readers(); + + $filter = new \stdClass(); + if (!empty($this->course)) { + $filter->courseid = $this->course->id; + } else { + $filter->courseid = 0; + } + + $filter->userid = $this->userid; + $filter->modid = $this->modid; + $filter->groupid = $this->get_selected_group(); + $filter->logreader = $readers[$this->selectedlogreader]; + $filter->edulevel = $this->edulevel; + $filter->action = $this->action; + $filter->date = $this->date; + $filter->orderby = $this->order; + + // If showing site_errors. + if ('site_errors' === $this->modid) { + $filter->siteerrors = true; + $filter->modid = 0; + } + + $this->tablelog = new report_log_table_log('report_log', $filter); + $this->tablelog->define_baseurl($this->url); + $this->tablelog->is_downloadable(true); + $this->tablelog->show_download_buttons_at(array(TABLE_P_BOTTOM)); + } + + /** + * Download logs in specified format. + */ + public function download() { + $filename = 'logs_' . userdate(time(), get_string('backupnameformat', 'langconfig'), 99, false); + $this->tablelog->is_downloading($this->logformat, $filename); + $this->tablelog->out($this->perpage, false); + } +} diff --git a/report/log/classes/renderer.php b/report/log/classes/renderer.php new file mode 100644 index 00000000000..dae19be8a33 --- /dev/null +++ b/report/log/classes/renderer.php @@ -0,0 +1,177 @@ +. + +/** + * Log report renderer. + * + * @package report_log + * @copyright 2014 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die; + +/** + * Report log renderer's for printing reports. + * + * @package report_log + * @copyright 2014 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class report_log_renderer extends plugin_renderer_base { + + /** + * Render log report page. + * + * @param report_log_renderable $reportlog object of report_log. + */ + public function render_report_log_renderable(report_log_renderable $reportlog) { + if (empty($reportlog->selectedlogreader)) { + echo $this->output->notification(get_string('nologreaderenabled', 'report_log'), 'notifyproblem'); + return; + } + if ($reportlog->showselectorform) { + $this->report_selector_form($reportlog); + } + + if ($reportlog->showreport) { + $reportlog->tablelog->out($reportlog->perpage, true); + } + } + + /** + * Prints/return reader selector + * + * @param report_log_renderable $reportlog log report. + */ + public function reader_selector(report_log_renderable $reportlog) { + $readers = $reportlog->get_readers(true); + if (empty($readers)) { + $readers = array(get_string('nologreaderenabled', 'report_log')); + } + $select = new single_select($reportlog->url, 'logreader', $readers, $reportlog->selectedlogreader, null); + $select->set_label(get_string('selectlogreader', 'report_log')); + echo $this->output->render($select); + } + + /** + * This function is used to generate and display selector form + * + * @param report_log_renderable $reportlog log report. + */ + public function report_selector_form(report_log_renderable $reportlog) { + echo html_writer::start_tag('form', array('class' => 'logselecform', 'action' => $reportlog->url, 'method' => 'get')); + echo html_writer::start_div(); + echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'chooselog', 'value' => '1')); + echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'showusers', 'value' => $reportlog->showusers)); + echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'showcourses', + 'value' => $reportlog->showcourses)); + + $selectedcourseid = empty($reportlog->course) ? 0 : $reportlog->course->id; + + // Add course selector. + $courses = $reportlog->get_course_list(); + if (!empty($courses)) { + if ($reportlog->showcourses) { + echo html_writer::label(get_string('selectacourse'), 'menuid', false, array('class' => 'accesshide')); + echo html_writer::select($courses, "id", $selectedcourseid, null); + } else { + $courseoption[$selectedcourseid] = $courses[$selectedcourseid]; + unset($courses); + echo html_writer::label(get_string('selectacourse'), 'menuid', false, array('class' => 'accesshide')); + echo html_writer::select($courseoption, "id", $selectedcourseid, null); + + // Check if user is admin and this came because of limitation on number of courses to show in dropdown. + $sitecontext = context_system::instance(); + if (has_capability('report/log:view', $sitecontext)) { + $a = new stdClass(); + $a->url = new moodle_url('/report/log/index.php', array('chooselog' => 0, + 'group' => $reportlog->get_selected_group(), 'user' => $reportlog->userid, + 'id' => $selectedcourseid, 'date' => $reportlog->date, 'modid' => $reportlog->modid, + 'showcourses' => 1, 'showusers' => $reportlog->showusers)); + print_string('logtoomanycourses', 'moodle', $a); + } + } + } + + // Add group selector. + $groups = $reportlog->get_group_list(); + if (!empty($groups)) { + echo html_writer::label(get_string('selectagroup'), 'menugroup', false, array('class' => 'accesshide')); + echo html_writer::select($groups, "group", $reportlog->groupid, get_string("allgroups")); + } + + // Add user selector. + $users = $reportlog->get_user_list(); + if (!empty($users)) { + if ($reportlog->showusers) { + echo html_writer::label(get_string('selctauser'), 'menuuser', false, array('class' => 'accesshide')); + echo html_writer::select($users, "user", $reportlog->userid, get_string("allparticipants")); + } else { + $users = array(); + if (!empty($reportlog->userid)) { + $users[$reportlog->userid] = $reportlog->get_selected_user_fullname(); + } else { + $users[0] = get_string('allparticipants'); + } + echo html_writer::label(get_string('selctauser'), 'menuuser', false, array('class' => 'accesshide')); + echo html_writer::select($users, "user", $reportlog->userid, false); + $a = new stdClass(); + $a->url = new moodle_url('/report/log/index.php', array('chooselog' => 0, + 'group' => $reportlog->get_selected_group(), 'user' => $reportlog->userid, + 'id' => $selectedcourseid, 'date' => $reportlog->date, 'modid' => $reportlog->modid, + 'showcourses' => 1, 'showusers' => $reportlog->showusers, 'showcourses' => $reportlog->showcourses)); + print_string('logtoomanyusers', 'moodle', $a); + } + } + + // Add date selector. + $dates = $reportlog->get_date_options(); + echo html_writer::label(get_string('date'), 'menudate', false, array('class' => 'accesshide')); + echo html_writer::select($dates, "date", $reportlog->date, get_string("alldays")); + + // Add activity selector. + $activities = $reportlog->get_activities_list(); + echo html_writer::label(get_string('activities'), 'menumodid', false, array('class' => 'accesshide')); + echo html_writer::select($activities, "modid", $reportlog->modid, get_string("allactivities")); + + // Add actions selector. + echo html_writer::label(get_string('actions'), 'menumodaction', false, array('class' => 'accesshide')); + echo html_writer::select($reportlog->get_actions(), 'modaction', $reportlog->action, get_string("allactions")); + + // Add edulevel. + $edulevel = $reportlog->get_edulevel_options(); + echo html_writer::label(get_string('edulevel'), 'menuedulevel', false, array('class' => 'accesshide')); + echo html_writer::select($edulevel, 'edulevel', $reportlog->edulevel, false); + + // Add reader option. + // If there is some reader available then only show submit button. + $readers = $reportlog->get_readers(true); + if (!empty($readers)) { + if (count($readers) == 1) { + $attributes = array('type' => 'hidden', 'name' => 'logreader', 'value' => key($readers)); + echo html_writer::empty_tag('input', $attributes); + } else { + echo html_writer::label(get_string('selectlogreader', 'report_log'), 'menureader', false, + array('class' => 'accesshide')); + echo html_writer::select($readers, 'logreader', $reportlog->selectedlogreader, false); + } + echo html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('gettheselogs'))); + } + echo html_writer::end_div(); + echo html_writer::end_tag('form'); + } +} + diff --git a/report/log/classes/table_log.php b/report/log/classes/table_log.php new file mode 100644 index 00000000000..c615b7806b5 --- /dev/null +++ b/report/log/classes/table_log.php @@ -0,0 +1,438 @@ +. + +/** + * Table log for displaying logs. + * + * @package report_log + * @copyright 2014 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +/** + * Table log class for displaying logs. + * + * @package report_log + * @copyright 2014 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class report_log_table_log extends table_sql { + + /** @var array list of user fullnames shown in report */ + private $userfullnames = array(); + + /** @var array list of course short names shown in report */ + private $courseshortnames = array(); + + /** @var array list of context name shown in report */ + private $contextname = array(); + + /** @var stdClass filters parameters */ + private $filterparams; + + /** + * Sets up the table_log parameters. + * + * @param string $uniqueid unique id of form. + * @param stdClass $filterparams (optional) filter params. + * - int courseid: id of course + * - int userid: user id + * - int|string modid: Module id or "site_errors" to view site errors + * - int groupid: Group id + * - \core\log\sql_select_reader logreader: reader from which data will be fetched. + * - int edulevel: educational level. + * - string action: view action + * - int date: Date from which logs to be viewed. + */ + public function __construct($uniqueid, $filterparams = null) { + parent::__construct($uniqueid); + + $this->set_attribute('class', 'reportlog generaltable generalbox'); + $this->filterparams = $filterparams; + // Add course column if logs are displayed for site. + $cols = array(); + $headers = array(); + if (empty($filterparams->courseid)) { + $cols = array('course'); + $headers = array(get_string('course')); + } + + $this->define_columns(array_merge($cols, array('time', 'fullnameuser', 'relatedfullnameuser', 'context', 'component', + 'eventname', 'description', 'origin', 'ip'))); + $this->define_headers(array_merge($headers, array( + get_string('time'), + get_string('fullnameuser'), + get_string('eventrelatedfullnameuser', 'report_log'), + get_string('eventcontext', 'report_log'), + get_string('eventcomponent', 'report_log'), + get_string('eventname'), + get_string('description'), + get_string('eventorigin', 'report_log'), + get_string('ip_address') + ) + )); + $this->collapsible(false); + $this->sortable(false); + $this->pageable(true); + } + + /** + * Generate the course column. + * + * @param stdClass $event event data. + * @return string HTML for the course column. + */ + public function col_course($event) { + if (empty($event->courseid) || empty($this->courseshortnames[$event->courseid])) { + return '-'; + } else { + return $this->courseshortnames[$event->courseid]; + } + } + + /** + * Generate the time column. + * + * @param stdClass $event event data. + * @return string HTML for the time column + */ + public function col_time($event) { + $recenttimestr = get_string('strftimerecent', 'core_langconfig'); + return userdate($event->timecreated, $recenttimestr); + } + + /** + * Generate the username column. + * + * @param stdClass $event event data. + * @return string HTML for the username column + */ + public function col_fullnameuser($event) { + // Get extra event data for origin and realuserid. + $logextra = $event->get_logextra(); + + // Add username who did the action. + if (!empty($logextra['realuserid'])) { + $a = new stdClass(); + $a->realusername = html_writer::link(new moodle_url("/user/view.php?id={$event->userid}&course={$event->courseid}"), + $this->userfullnames[$logextra['realuserid']]); + $a->asusername = html_writer::link(new moodle_url("/user/view.php?id={$event->userid}&course={$event->courseid}"), + $this->userfullnames[$event->userid]); + $username = get_string('eventloggedas', 'report_log', $a); + } else if (!empty($event->userid) && !empty($this->userfullnames[$event->userid])) { + $params = array('id' => $event->userid); + if ($event->courseid) { + $params['course'] = $event->courseid; + } + $username = html_writer::link(new moodle_url("/user/view.php", $params), $this->userfullnames[$event->userid]); + } else { + $username = '-'; + } + return $username; + } + + /** + * Generate the related username column. + * + * @param stdClass $event event data. + * @return string HTML for the related username column + */ + public function col_relatedfullnameuser($event) { + // Add affected user. + if (!empty($event->relateduserid)) { + return html_writer::link(new moodle_url("/user/view.php?id=" . $event->relateduserid . "&course=" . + $event->courseid), $this->userfullnames[$event->relateduserid]); + } else { + return '-'; + } + } + + /** + * Generate the context column. + * + * @param stdClass $event event data. + * @return string HTML for the context column + */ + public function col_context($event) { + // Add context name. + if ($event->contextid) { + // If context name was fetched before then return, else get one. + if (isset($this->contextname[$event->contextid])) { + return $this->contextname[$event->contextid]; + } else { + $context = context::instance_by_id($event->contextid, IGNORE_MISSING); + if ($context) { + $contextname = $context->get_context_name(true); + if ($url = $context->get_url()) { + $contextname = html_writer::link($url, $contextname); + } + } else { + $contextname = get_string('other'); + } + } + } else { + $contextname = get_string('other'); + } + + $this->contextname[$event->contextid] = $contextname; + return $contextname; + } + + /** + * Generate the component column. + * + * @param stdClass $event event data. + * @return string HTML for the component column + */ + public function col_component($event) { + // Component. + $componentname = $event->component; + if (($event->component === 'core') || ($event->component === 'legacy')) { + return get_string('coresystem'); + } else if (get_string_manager()->string_exists('pluginname', $event->component)) { + return get_string('pluginname', $event->component); + } else { + return $componentname; + } + } + + /** + * Generate the event name column. + * + * @param stdClass $event event data. + * @return string HTML for the event name column + */ + public function col_eventname($event) { + // Event name. + $eventname = $event->get_name(); + if ($url = $event->get_url()) { + $eventname = html_writer::link($url, $eventname); + } + return $eventname; + } + + /** + * Generate the description column. + * + * @param stdClass $event event data. + * @return string HTML for the description column + */ + public function col_description($event) { + // Description. + return $event->get_description(); + } + + /** + * Generate the origin column. + * + * @param stdClass $event event data. + * @return string HTML for the origin column + */ + public function col_origin($event) { + // Get extra event data for origin and realuserid. + $logextra = $event->get_logextra(); + + // Add event origin, normally IP/cron. + return $logextra['origin']; + } + + /** + * Generate the ip column. + * + * @param stdClass $event event data. + * @return string HTML for the ip column + */ + public function col_ip($event) { + // Get extra event data for origin and realuserid. + $logextra = $event->get_logextra(); + + $link = new moodle_url("/iplookup/index.php?ip={$logextra['ip']}&user=$event->userid"); + return html_writer::link($link, $logextra['ip']); + } + + /** + * Helper function to get legacy crud action. + * + * @param string $crud crud action + * @return string legacy action. + */ + public function get_legacy_crud_action($crud) { + $legacyactionmap = array('c' => 'add', 'r' => 'view', 'u' => 'update', 'd' => 'delete'); + if (array_key_exists($crud, $legacyactionmap)) { + return $legacyactionmap[$crud]; + } else { + // From old legacy log. + return '-view'; + } + } + + /** + * Helper function which is used by build logs to get action sql and param. + * + * @return array sql and param for action. + */ + public function get_action_sql() { + global $DB; + + // In new logs we have a field to pick, and in legacy try get this from action. + if ($this->filterparams->logreader instanceof logstore_legacy\log\store) { + $action = $this->get_legacy_crud_action($this->filterparams->action); + $firstletter = substr($action, 0, 1); + if ($firstletter == '-') { + $sql = $DB->sql_like('action', ':action', false, true, true); + $params['action'] = '%'.substr($action, 1).'%'; + } else { + $sql = $DB->sql_like('action', ':action', false); + $params['action'] = '%'.$action.'%'; + } + } else { + $sql = "crud = :crud"; + $params['crud'] = $this->filterparams->action; + } + return array($sql, $params); + } + + /** + * Query the reader. Store results in the object for use by build_table. + * + * @param int $pagesize size of page for paginated displayed table. + * @param bool $useinitialsbar do you want to use the initials bar. + */ + public function query_db($pagesize, $useinitialsbar = true) { + + $joins = array(); + $params = array(); + + $groupid = 0; + if (!empty($this->filterparams->courseid)) { + if (!empty($this->filterparams->groupid)) { + $groupid = $this->filterparams->groupid; + } + + $joins[] = "courseid = :courseid"; + $params['courseid'] = $this->filterparams->courseid; + } + + if (!empty($this->filterparams->siteerrors)) { + $joins[] = "( action='error' OR action='infected' OR action='failed' )"; + } + + if (!empty($this->filterparams->modid)) { + $joins[] = "contextinstanceid = :contextinstanceid"; + $params['contextinstanceid'] = $this->filterparams->modid; + } + + if (!empty($this->filterparams->action)) { + list($actionsql, $actionparams) = $this->get_action_sql(); + $joins[] = $actionsql; + $params = array_merge($params, $actionparams); + } + + // Getting all members of a group. + if ($groupid and empty($this->filterparams->userid)) { + if ($gusers = groups_get_members($groupid)) { + $gusers = array_keys($gusers); + $joins[] = 'userid IN (' . implode(',', $gusers) . ')'; + } else { + $joins[] = 'userid = 0'; // No users in groups, so we want something that will always be false. + } + } else if (!empty($this->filterparams->userid)) { + $joins[] = "userid = :userid"; + $params['userid'] = $this->filterparams->userid; + } + + if (!empty($this->filterparams->date)) { + $joins[] = "timecreated > :date"; + $params['date'] = $this->filterparams->date; + } + + if (isset($this->filterparams->edulevel) && ($this->filterparams->edulevel >= 0)) { + $joins[] = "edulevel = :edulevel"; + $params['edulevel'] = $this->filterparams->edulevel; + } + + $selector = implode(' AND ', $joins); + + if (!$this->is_downloading()) { + $total = $this->filterparams->logreader->get_events_select_count($selector, $params); + $this->pagesize($pagesize, $total); + } + $this->rawdata = $this->filterparams->logreader->get_events_select($selector, $params, $this->filterparams->orderby, + $this->get_page_start(), $this->get_page_size()); + + // Set initial bars. + if ($useinitialsbar && !$this->is_downloading()) { + $this->initialbars($total > $pagesize); + } + + // Update list of users and courses list which will be displayed on log page. + $this->update_users_and_courses_used(); + } + + /** + * Helper function to create list of course shortname and user fullname shown in log report. + * This will update $this->userfullnames and $this->courseshortnames array with userfullname and courseshortname (with link), + * which will be used to render logs in table. + */ + public function update_users_and_courses_used() { + global $SITE, $DB; + + $this->userfullnames = array(); + $this->courseshortnames = array($SITE->id => $SITE->shortname); + $userids = array(); + $courseids = array(); + // For each event cache full username and course. + // Get list of userids and courseids which will be shown in log report. + foreach ($this->rawdata as $event) { + $logextra = $event->get_logextra(); + if (!empty($event->userid) && !in_array($event->userid, $userids)) { + $userids[] = $event->userid; + } + if (!empty($logextra['realuserid']) && !in_array($logextra['realuserid'], $userids)) { + $userids[] = $logextra['realuserid']; + } + if (!empty($event->relateduserid) && !in_array($event->relateduserid, $userids)) { + $userids[] = $event->relateduserid; + } + + if (!empty($event->courseid) && ($event->courseid != $SITE->id) && !in_array($event->courseid, $courseids)) { + $courseids[] = $event->courseid; + } + } + + // Get user fullname and put that in return list. + if (!empty($userids)) { + list($usql, $uparams) = $DB->get_in_or_equal($userids); + $users = $DB->get_records_sql("SELECT id," . get_all_user_name_fields(true) . " FROM {user} WHERE id " . $usql, + $uparams); + foreach ($users as $userid => $user) { + $this->userfullnames[$userid] = fullname($user); + } + } + + // Get course shortname and put that in return list. + if (!empty($courseids)) { // If all logs don't belog to site level then get course info. + list($coursesql, $courseparams) = $DB->get_in_or_equal($courseids); + $courses = $DB->get_records_sql("SELECT id,shortname FROM {course} WHERE id " . $coursesql, $courseparams); + foreach ($courses as $courseid => $course) { + $url = new moodle_url("/course/view.php", array('id' => $courseid)); + $this->courseshortnames[$courseid] = html_writer::link($url, format_string($course->shortname)); + } + } + } +} \ No newline at end of file diff --git a/report/log/graph.php b/report/log/graph.php index 20124a8e074..90e3a745325 100644 --- a/report/log/graph.php +++ b/report/log/graph.php @@ -28,20 +28,22 @@ require("../../config.php"); require_once("$CFG->libdir/graphlib.php"); require_once($CFG->dirroot.'/report/log/locallib.php'); -$id = required_param('id', PARAM_INT); // Course ID -$type = required_param('type', PARAM_FILE); // Graph Type -$user = required_param('user', PARAM_INT); // Student ID -$date = optional_param('date', 0, PARAM_INT); // A time of a day (in GMT) +$id = required_param('id', PARAM_INT); // Course ID. +$type = required_param('type', PARAM_FILE); // Graph Type. +$user = required_param('user', PARAM_INT); // Student ID. +$date = optional_param('date', 0, PARAM_INT); // A time of a day (in GMT). +$logreader = optional_param('logreader', '', PARAM_COMPONENT); -$url = new moodle_url('/report/log/graph.php', array('id'=>$id,'type'=>$type,'user'=>$user,'date'=>$date)); +$url = new moodle_url('/report/log/graph.php', array('id' => $id, 'type' => $type, 'user' => $user, 'date' => $date, + 'logreader' => $logreader)); $PAGE->set_url($url); if ($type !== "usercourse.png" and $type !== "userday.png") { $type = 'userday.png'; } -$course = $DB->get_record("course", array("id"=>$id), '*', MUST_EXIST); -$user = $DB->get_record("user", array("id"=>$user, 'deleted'=>0), '*', MUST_EXIST); +$course = $DB->get_record("course", array("id" => $id), '*', MUST_EXIST); +$user = $DB->get_record("user", array("id" => $user, 'deleted' => 0), '*', MUST_EXIST); $coursecontext = context_course::instance($course->id); $personalcontext = context_user::instance($user->id); @@ -73,147 +75,150 @@ $timenow = time(); if ($type === "usercourse.png") { - $site = get_site(); + $site = get_site(); - if ($course->id == $site->id) { - $courseselect = 0; - } else { - $courseselect = $course->id; - } + if ($course->id == $site->id) { + $courseselect = 0; + } else { + $courseselect = $course->id; + } - $maxseconds = REPORT_LOG_MAX_DISPLAY * 3600 * 24; // seconds - //$maxseconds = 60 * 3600 * 24; // seconds - if ($timenow - $course->startdate > $maxseconds) { - $course->startdate = $timenow - $maxseconds; - } + $maxseconds = REPORT_LOG_MAX_DISPLAY * 3600 * 24; // Seconds. + if ($timenow - $course->startdate > $maxseconds) { + $course->startdate = $timenow - $maxseconds; + } - if (!empty($CFG->loglifetime)) { - $maxseconds = $CFG->loglifetime * 3600 * 24; // seconds - if ($timenow - $course->startdate > $maxseconds) { - $course->startdate = $timenow - $maxseconds; - } - } + if (!empty($CFG->loglifetime)) { + $maxseconds = $CFG->loglifetime * 3600 * 24; // Seconds. + if ($timenow - $course->startdate > $maxseconds) { + $course->startdate = $timenow - $maxseconds; + } + } - $timestart = $coursestart = usergetmidnight($course->startdate); + $timestart = $coursestart = usergetmidnight($course->startdate); - if ((($timenow - $timestart)/86400.0) > 40) { - $reducedays = 7; - } else { - $reducedays = 0; - } + if ((($timenow - $timestart) / 86400.0) > 40) { + $reducedays = 7; + } else { + $reducedays = 0; + } - $days = array(); - $i = 0; - while ($timestart < $timenow) { - $timefinish = $timestart + 86400; - if ($reducedays) { - if ($i % $reducedays) { - $days[$i] = ""; - } else { - $days[$i] = userdate($timestart, "%a %d %b"); - } - } else { - $days[$i] = userdate($timestart, "%a %d %b"); - } - $logs[$i] = 0; - $i++; - $timestart = $timefinish; - } + $days = array(); + $i = 0; + while ($timestart < $timenow) { + $timefinish = $timestart + 86400; + if ($reducedays) { + if ($i % $reducedays) { + $days[$i] = ""; + } else { + $days[$i] = userdate($timestart, "%a %d %b"); + } + } else { + $days[$i] = userdate($timestart, "%a %d %b"); + } + $logs[$i] = 0; + $i++; + $timestart = $timefinish; + } - if ($rawlogs = get_logs_usercourse($user->id, $courseselect, $coursestart)) { - foreach ($rawlogs as $rawlog) { - $logs[$rawlog->day] = $rawlog->num; - } - } + $rawlogs = report_log_usercourse($user->id, $courseselect, $coursestart); - $graph = new graph(750, 400); + if (empty($rawlogs)) { + return; + } - $a = new stdClass(); - $a->coursename = format_string($course->shortname, true, array('context' => $coursecontext)); - $a->username = fullname($user, true); - $graph->parameter['title'] = get_string("hitsoncourse", "", $a); + foreach ($rawlogs as $rawlog) { + $logs[$rawlog->day] = $rawlog->num; + } - $graph->x_data = $days; + $graph = new graph(750, 400); - $graph->y_data['logs'] = $logs; - $graph->y_order = array('logs'); + $a = new stdClass(); + $a->coursename = format_string($course->shortname, true, array('context' => $coursecontext)); + $a->username = fullname($user, true); + $graph->parameter['title'] = get_string("hitsoncourse", "", $a); - if (!empty($CFG->preferlinegraphs)) { - $graph->y_format['logs'] = array('colour' => 'blue','line' => 'line'); - } else { - $graph->y_format['logs'] = array('colour' => 'blue','bar' => 'fill','bar_size' => 0.6); - $graph->parameter['bar_spacing'] = 0; - } + $graph->x_data = $days; + + $graph->y_data['logs'] = $logs; + $graph->y_order = array('logs'); + + if (!empty($CFG->preferlinegraphs)) { + $graph->y_format['logs'] = array('colour' => 'blue', 'line' => 'line'); + } else { + $graph->y_format['logs'] = array('colour' => 'blue', 'bar' => 'fill', 'bar_size' => 0.6); + $graph->parameter['bar_spacing'] = 0; + } - $graph->parameter['y_label_left'] = get_string("hits"); - $graph->parameter['label_size'] = "12"; - $graph->parameter['x_axis_angle'] = 90; - $graph->parameter['x_label_angle'] = 0; - $graph->parameter['tick_length'] = 0; + $graph->parameter['y_label_left'] = get_string("hits"); + $graph->parameter['label_size'] = "12"; + $graph->parameter['x_axis_angle'] = 90; + $graph->parameter['x_label_angle'] = 0; + $graph->parameter['tick_length'] = 0; + $graph->parameter['shadow'] = 'none'; - - $graph->parameter['shadow'] = 'none'; - - error_reporting(5); // ignore most warnings such as font problems etc - $graph->draw_stack(); + error_reporting(5); // Ignore most warnings such as font problems etc. + $graph->draw_stack(); } else { - $site = get_site(); + $site = get_site(); - if ($course->id == $site->id) { - $courseselect = 0; - } else { - $courseselect = $course->id; - } + if ($course->id == $site->id) { + $courseselect = 0; + } else { + $courseselect = $course->id; + } - if ($date) { - $daystart = usergetmidnight($date); - } else { - $daystart = usergetmidnight(time()); - } - $dayfinish = $daystart + 86400; + if ($date) { + $daystart = usergetmidnight($date); + } else { + $daystart = usergetmidnight(time()); + } + $dayfinish = $daystart + 86400; - $hours = array(); - for ($i=0; $i<=23; $i++) { - $logs[$i] = 0; - $hour = $daystart + $i * 3600; - $hours[$i] = $i; - } + $hours = array(); + for ($i = 0; $i <= 23; $i++) { + $logs[$i] = 0; + $hour = $daystart + $i * 3600; + $hours[$i] = $i; + } - if ($rawlogs = get_logs_userday($user->id, $courseselect, $daystart)) { - foreach ($rawlogs as $rawlog) { - $logs[$rawlog->hour] = $rawlog->num; - } - } + $rawlogs = report_log_userday($user->id, $courseselect, $daystart); - $graph = new graph(750, 400); + if (empty($rawlogs)) { + return; + } - $a = new stdClass(); - $a->coursename = format_string($course->shortname, true, array('context' => $coursecontext)); - $a->username = fullname($user, true); - $graph->parameter['title'] = get_string("hitsoncoursetoday", "", $a); + foreach ($rawlogs as $rawlog) { + $logs[$rawlog->hour] = $rawlog->num; + } - $graph->x_data = $hours; + $graph = new graph(750, 400); - $graph->y_data['logs'] = $logs; - $graph->y_order = array('logs'); + $a = new stdClass(); + $a->coursename = format_string($course->shortname, true, array('context' => $coursecontext)); + $a->username = fullname($user, true); - if (!empty($CFG->preferlinegraphs)) { - $graph->y_format['logs'] = array('colour' => 'blue','line' => 'line'); - } else { - $graph->y_format['logs'] = array('colour' => 'blue','bar' => 'fill','bar_size' => 0.9); - } + $graph->parameter['title'] = get_string("hitsoncoursetoday", "", $a); + $graph->x_data = $hours; + $graph->y_data['logs'] = $logs; + $graph->y_order = array('logs'); - $graph->parameter['y_label_left'] = get_string("hits"); - $graph->parameter['label_size'] = "12"; - $graph->parameter['x_axis_angle'] = 0; - $graph->parameter['x_label_angle'] = 0; + if (!empty($CFG->preferlinegraphs)) { + $graph->y_format['logs'] = array('colour' => 'blue', 'line' => 'line'); + } else { + $graph->y_format['logs'] = array('colour' => 'blue', 'bar' => 'fill', 'bar_size' => 0.9); + } - $graph->parameter['shadow'] = 'none'; + $graph->parameter['y_label_left'] = get_string("hits"); + $graph->parameter['label_size'] = "12"; + $graph->parameter['x_axis_angle'] = 0; + $graph->parameter['x_label_angle'] = 0; + $graph->parameter['shadow'] = 'none'; - error_reporting(5); // ignore most warnings such as font problems etc - $graph->draw_stack(); + error_reporting(5); // Ignore most warnings such as font problems etc. + $graph->draw_stack(); } diff --git a/report/log/index.php b/report/log/index.php index 832593e1eec..de3c3ef50c9 100644 --- a/report/log/index.php +++ b/report/log/index.php @@ -26,40 +26,24 @@ require('../../config.php'); require_once($CFG->dirroot.'/course/lib.php'); require_once($CFG->dirroot.'/report/log/locallib.php'); require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->dirroot.'/lib/tablelib.php'); -$id = optional_param('id', 0, PARAM_INT);// Course ID -$host_course = optional_param('host_course', '', PARAM_PATH);// Course ID - -if (empty($host_course)) { - $hostid = $CFG->mnet_localhost_id; - if (empty($id)) { - $site = get_site(); - $id = $site->id; - } -} else { - list($hostid, $id) = explode('/', $host_course); -} - -$group = optional_param('group', 0, PARAM_INT); // Group to display -$user = optional_param('user', 0, PARAM_INT); // User to display -$date = optional_param('date', 0, PARAM_INT); // Date to display -$modname = optional_param('modname', '', PARAM_PLUGIN); // course_module->id -$modid = optional_param('modid', 0, PARAM_FILE); // number or 'site_errors' -$modaction = optional_param('modaction', '', PARAM_PATH); // an action as recorded in the logs -$page = optional_param('page', '0', PARAM_INT); // which page to show -$perpage = optional_param('perpage', '100', PARAM_INT); // how many per page -$showcourses = optional_param('showcourses', 0, PARAM_INT); // whether to show courses if we're over our limit. -$showusers = optional_param('showusers', 0, PARAM_INT); // whether to show users if we're over our limit. -$chooselog = optional_param('chooselog', 0, PARAM_INT); -$logformat = optional_param('logformat', 'showashtml', PARAM_ALPHA); +$id = optional_param('id', 0, PARAM_INT);// Course ID. +$group = optional_param('group', 0, PARAM_INT); // Group to display. +$user = optional_param('user', 0, PARAM_INT); // User to display. +$date = optional_param('date', 0, PARAM_INT); // Date to display. +$modid = optional_param('modid', 0, PARAM_ALPHANUMEXT); // Module id or 'site_errors'. +$modaction = optional_param('modaction', '', PARAM_ALPHAEXT); // An action as recorded in the logs. +$page = optional_param('page', '0', PARAM_INT); // Which page to show. +$perpage = optional_param('perpage', '100', PARAM_INT); // How many per page. +$showcourses = optional_param('showcourses', false, PARAM_BOOL); // Whether to show courses if we're over our limit. +$showusers = optional_param('showusers', false, PARAM_BOOL); // Whether to show users if we're over our limit. +$chooselog = optional_param('chooselog', false, PARAM_BOOL); +$logformat = optional_param('download', '', PARAM_ALPHA); +$logreader = optional_param('logreader', '', PARAM_COMPONENT); // Reader which will be used for displaying logs. +$edulevel = optional_param('edulevel', -1, PARAM_INT); // Educational level. $params = array(); -if ($id !== 0) { - $params['id'] = $id; -} -if ($host_course !== '') { - $params['host_course'] = $host_course; -} if ($group !== 0) { $params['group'] = $group; } @@ -69,9 +53,6 @@ if ($user !== 0) { if ($date !== 0) { $params['date'] = $date; } -if ($modname !== '') { - $params['modname'] = $modname; -} if ($modid !== 0) { $params['modid'] = $modid; } @@ -84,45 +65,60 @@ if ($page !== '0') { if ($perpage !== '100') { $params['perpage'] = $perpage; } -if ($showcourses !== 0) { +if ($showcourses) { $params['showcourses'] = $showcourses; } -if ($showusers !== 0) { +if ($showusers) { $params['showusers'] = $showusers; } -if ($chooselog !== 0) { +if ($chooselog) { $params['chooselog'] = $chooselog; } -if ($logformat !== 'showashtml') { - $params['logformat'] = $logformat; +if ($logformat !== '') { + $params['download'] = $logformat; } -$PAGE->set_url('/report/log/index.php', $params); +if ($logreader !== '') { + $params['logreader'] = $logreader; +} +if (($edulevel != -1)) { + $params['edulevel'] = $edulevel; +} + +// Legacy store hack, as edulevel is not supported. +if ($logreader == 'logstore_legacy') { + $params['edulevel'] = -1; + $edulevel = -1; +} +$url = new moodle_url("/report/log/index.php", $params); + +$PAGE->set_url('/report/log/index.php', array('id' => $id)); $PAGE->set_pagelayout('report'); -if ($hostid == $CFG->mnet_localhost_id) { - $course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST); - +// Get course details. +$course = null; +if ($id) { + $course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST); + require_login($course); + $context = context_course::instance($course->id); } else { - $course_stub = $DB->get_record('mnet_log', array('hostid'=>$hostid, 'course'=>$id), '*', true); - $course->id = $id; - $course->shortname = $course_stub->coursename; - $course->fullname = $course_stub->coursename; + require_login(); + $context = context_system::instance(); + $PAGE->set_context($context); } -require_login($course); - -$context = context_course::instance($course->id); - require_capability('report/log:view', $context); -// Trigger a report viewed event. -$event = \report_log\event\report_viewed::create(array('context' => $context, 'relateduserid' => $user, - 'other' => array('groupid' => $group, 'date' => $date, 'modid' => $modid, 'modaction' => $modaction, - 'logformat' => $logformat))); -$event->trigger(); +// When user choose to view logs then only trigger event. +if ($chooselog) { + // Trigger a report viewed event. + $event = \report_log\event\report_viewed::create(array('context' => $context, 'relateduserid' => $user, + 'other' => array('groupid' => $group, 'date' => $date, 'modid' => $modid, 'modaction' => $modaction, + 'logformat' => $logformat))); + $event->trigger(); +} if (!empty($page)) { - $strlogs = get_string('logs'). ": ". get_string('page', 'report_log', $page+1); + $strlogs = get_string('logs'). ": ". get_string('page', 'report_log', $page + 1); } else { $strlogs = get_string('logs'); } @@ -134,83 +130,55 @@ $adminediting = optional_param('adminedit', -1, PARAM_BOOL); if ($PAGE->user_allowed_editing() && $adminediting != -1) { $USER->editing = $adminediting; } + \core\session\manager::write_close(); -if (!empty($chooselog)) { - $userinfo = get_string('allparticipants'); - $dateinfo = get_string('alldays'); - - if ($user) { - $u = $DB->get_record('user', array('id'=>$user, 'deleted'=>0), '*', MUST_EXIST); - $userinfo = fullname($u, has_capability('moodle/site:viewfullnames', $context)); - } - if ($date) { - $dateinfo = userdate($date, get_string('strftimedaydate')); - } - - switch ($logformat) { - case 'showashtml': - if ($hostid != $CFG->mnet_localhost_id || $course->id == SITEID) { - admin_externalpage_setup('reportlog'); - $PAGE->set_title($course->shortname .': '. $strlogs); - echo $OUTPUT->header(); - - } else { - $PAGE->set_title($course->shortname .': '. $strlogs); - $PAGE->set_heading($course->fullname); - $PAGE->navbar->add("$userinfo, $dateinfo"); - echo $OUTPUT->header(); - } - - echo $OUTPUT->heading(format_string($course->fullname) . ": $userinfo, $dateinfo (".usertimezone().")"); - report_log_print_mnet_selector_form($hostid, $course, $user, $date, $modname, $modid, $modaction, $group, $showcourses, $showusers, $logformat); - - if ($hostid == $CFG->mnet_localhost_id) { - print_log($course, $user, $date, 'l.time DESC', $page, $perpage, - "index.php?id=$course->id&chooselog=1&user=$user&date=$date&modid=$modid&modaction=$modaction&group=$group", - $modname, $modid, $modaction, $group); - } else { - print_mnet_log($hostid, $id, $user, $date, 'l.time DESC', $page, $perpage, "", $modname, $modid, $modaction, $group); - } - break; - case 'downloadascsv': - if (!print_log_csv($course, $user, $date, 'l.time DESC', $modname, - $modid, $modaction, $group)) { - echo $OUTPUT->notification("No logs found!"); - echo $OUTPUT->footer(); - } - exit; - case 'downloadasods': - if (!print_log_ods($course, $user, $date, 'l.time DESC', $modname, - $modid, $modaction, $group)) { - echo $OUTPUT->notification("No logs found!"); - echo $OUTPUT->footer(); - } - exit; - case 'downloadasexcel': - if (!print_log_xls($course, $user, $date, 'l.time DESC', $modname, - $modid, $modaction, $group)) { - echo $OUTPUT->notification("No logs found!"); - echo $OUTPUT->footer(); - } - exit; - } - - +if (empty($course) || ($course->id == $SITE->id)) { + admin_externalpage_setup('reportlog', '', null, '', array('pagelayout' => 'report')); + $PAGE->set_title($SITE->shortname .': '. $strlogs); } else { - if ($hostid != $CFG->mnet_localhost_id || $course->id == SITEID) { - admin_externalpage_setup('reportlog', '', null, '', array('pagelayout'=>'report')); - echo $OUTPUT->header(); - } else { - $PAGE->set_title($course->shortname .': '. $strlogs); - $PAGE->set_heading($course->fullname); - echo $OUTPUT->header(); - } - - echo $OUTPUT->heading(get_string('chooselogs') .':'); - - report_log_print_selector_form($course, $user, $date, $modname, $modid, $modaction, $group, $showcourses, $showusers, $logformat); + $PAGE->set_title($course->shortname .': '. $strlogs); + $PAGE->set_heading($course->fullname); } -echo $OUTPUT->footer(); +$reportlog = new report_log_renderable($logreader, $course, $user, $modid, $modaction, $group, $edulevel, $showcourses, $showusers, + $chooselog, true, $url, $date, $logformat, $page, $perpage, 'timecreated DESC'); +$readers = $reportlog->get_readers(); +$output = $PAGE->get_renderer('report_log'); +if (empty($readers)) { + echo $output->header(); + echo $output->heading(get_string('noreaderenabled')); +} else { + if (!empty($chooselog)) { + // Delay creation of table, till called by user with filter. + $reportlog->setup_table(); + + if (empty($logformat)) { + echo $output->header(); + $userinfo = get_string('allparticipants'); + $dateinfo = get_string('alldays'); + + if ($user) { + $u = $DB->get_record('user', array('id' => $user, 'deleted' => 0), '*', MUST_EXIST); + $userinfo = fullname($u, has_capability('moodle/site:viewfullnames', $context)); + } + if ($date) { + $dateinfo = userdate($date, get_string('strftimedaydate')); + } + if (!empty($course) && ($course->id != SITEID)) { + $PAGE->navbar->add("$userinfo, $dateinfo"); + } + echo $output->render($reportlog); + } else { + $reportlog->download(); + exit(); + } + } else { + echo $output->header(); + echo $output->heading(get_string('chooselogs') .':'); + echo $output->render($reportlog); + } +} + +echo $output->footer(); diff --git a/report/log/lang/en/report_log.php b/report/log/lang/en/report_log.php index 9601e1e6065..5985ed604ab 100644 --- a/report/log/lang/en/report_log.php +++ b/report/log/lang/en/report_log.php @@ -24,13 +24,20 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['eventcomponent'] = 'Component'; +$string['eventcontext'] = 'Event context'; +$string['eventloggedas'] = '{$a->realusername} as {$a->asusername}'; +$string['eventorigin'] = 'Origin'; +$string['eventrelatedfullnameuser'] = 'Affected user'; $string['eventreportviewed'] = 'Log report viewed'; $string['eventuserreportviewed'] = 'User log report viewed'; $string['log:view'] = 'View course logs'; $string['log:viewtoday'] = 'View today\'s logs'; $string['page'] = 'Page {$a}'; $string['logsformat'] = 'Logs format'; +$string['nologreaderenabled'] = 'No log reader enabled'; $string['page-report-log-x'] = 'Any log report'; $string['page-report-log-index'] = 'Course log report'; $string['page-report-log-user'] = 'User course log report'; $string['pluginname'] = 'Logs'; +$string['selectlogreader'] = 'Select log reader'; diff --git a/report/log/locallib.php b/report/log/locallib.php index a3bb84132bc..afecee1d9b5 100644 --- a/report/log/locallib.php +++ b/report/log/locallib.php @@ -40,14 +40,127 @@ require_once(dirname(__FILE__).'/lib.php'); * @param int $userid id of the user whose logs are needed * @param string $type type of logs graph needed (usercourse.png/userday.png) * @param int $date timestamp in GMT (seconds since epoch) + * @param string $logreader Log reader. * @return void */ -function report_log_print_graph($course, $userid, $type, $date=0) { - global $CFG; +function report_log_print_graph($course, $userid, $type, $date=0, $logreader='') { + // If reader is not a sql_reader and not legacy store then don't show graph. + $logmanager = get_log_manager(); + $readers = $logmanager->get_readers(); - echo ''; + if (empty($logreader)) { + $reader = reset($readers); + } else { + $reader = $readers[$logreader]; + } + if (!($reader instanceof core\log\sql_select_reader)) { + return; + } + + $url = new moodle_url('/report/log/graph.php', array('id' => $course->id, 'user' => $userid, 'type' => $type, + 'date' => $date, 'logreader' => $logreader)); + echo html_writer::empty_tag('img', array('src' => $url, 'alt' => '')); } + +/** + * Select all log records for a given course and user + * + * @param int $userid The id of the user as found in the 'user' table. + * @param int $courseid The id of the course as found in the 'course' table. + * @param string $coursestart unix timestamp representing course start date and time. + * @param string $logreader log reader to use. + * @return array + */ +function report_log_usercourse($userid, $courseid, $coursestart, $logreader = '') { + global $DB; + + $logmanager = get_log_manager(); + $readers = $logmanager->get_readers(); + if (empty($logreader)) { + $reader = reset($readers); + } else { + $reader = $readers[$logreader]; + } + + // If reader is not a sql_reader and not legacy store then return. + if (!($reader instanceof \core\log\sql_reader) && !($reader instanceof logstore_legacy\log\store)) { + return array(); + } + + $coursestart = (int)$coursestart; // Note: unfortunately pg complains if you use name parameter or column alias in GROUP BY. + if ($reader instanceof logstore_legacy\log\store) { + $logtable = 'log'; + $timefield = 'time'; + $coursefield = 'course'; + } else { + $logtable = $reader->get_log_table(); + $timefield = 'timecreated'; + $coursefield = 'courseid'; + } + + $params = array(); + $courseselect = ''; + if ($courseid) { + $courseselect = "AND $coursefield = :courseid"; + $params['courseid'] = $courseid; + } + $params['userid'] = $userid; + return $DB->get_records_sql("SELECT FLOOR(($timefield - $coursestart)/" . DAYSECS . ") AS day, COUNT(*) AS num + FROM {" . $logtable . "} + WHERE userid = :userid + AND $timefield > $coursestart $courseselect + GROUP BY FLOOR(($timefield - $coursestart)/" . DAYSECS .")", $params); +} + +/** + * Select all log records for a given course, user, and day + * + * @param int $userid The id of the user as found in the 'user' table. + * @param int $courseid The id of the course as found in the 'course' table. + * @param string $daystart unix timestamp of the start of the day for which the logs needs to be retrived + * @param string $logreader log reader to use. + * @return array + */ +function report_log_userday($userid, $courseid, $daystart, $logreader = '') { + global $DB; + $logmanager = get_log_manager(); + $readers = $logmanager->get_readers(); + if (empty($logreader)) { + $reader = reset($readers); + } else { + $reader = $readers[$logreader]; + } + + // If reader is not a sql_reader and not legacy store then return. + if (!($reader instanceof \core\log\sql_reader) && !($reader instanceof logstore_legacy\log\store)) { + return array(); + } + + $daystart = (int)$daystart; // Note: unfortunately pg complains if you use name parameter or column alias in GROUP BY. + + if ($reader instanceof logstore_legacy\log\store) { + $logtable = 'log'; + $timefield = 'time'; + $coursefield = 'course'; + } else { + $logtable = $reader->get_log_table(); + $timefield = 'timecreated'; + $coursefield = 'courseid'; + } + $params = array('userid' => $userid); + + $courseselect = ''; + if ($courseid) { + $courseselect = "AND $coursefield = :courseid"; + $params['courseid'] = $courseid; + } + return $DB->get_records_sql("SELECT FLOOR(($timefield - $daystart)/" . HOURSECS . ") AS hour, COUNT(*) AS num + FROM {" . $logtable . "} + WHERE userid = :userid + AND $timefield > $daystart $courseselect + GROUP BY FLOOR(($timefield - $daystart)/" . HOURSECS . ") ", $params); +} + /** * This function is used to generate and display Mnet selector form * @@ -381,264 +494,3 @@ function report_log_print_mnet_selector_form($hostid, $course, $selecteduser=0, echo ''; echo ''; } -/** - * This function is used to generate and display selector form - * - * @global stdClass $USER - * @global stdClass $CFG - * @global moodle_database $DB - * @global core_renderer $OUTPUT - * @global stdClass $SESSION - * @uses CONTEXT_SYSTEM - * @uses COURSE_MAX_COURSES_PER_DROPDOWN - * @uses CONTEXT_COURSE - * @uses SEPARATEGROUPS - * @param stdClass $course course instance - * @param int $selecteduser id of the selected user - * @param string $selecteddate Date selected - * @param string $modname course_module->id - * @param string $modid number or 'site_errors' - * @param string $modaction an action as recorded in the logs - * @param int $selectedgroup Group to display - * @param int $showcourses whether to show courses if we're over our limit. - * @param int $showusers whether to show users if we're over our limit. - * @param string $logformat Format of the logs (downloadascsv, showashtml, downloadasods, downloadasexcel) - * @return void - */ -function report_log_print_selector_form($course, $selecteduser=0, $selecteddate='today', - $modname="", $modid=0, $modaction='', $selectedgroup=-1, $showcourses=0, $showusers=0, $logformat='showashtml') { - - global $USER, $CFG, $DB, $OUTPUT, $SESSION; - - // first check to see if we can override showcourses and showusers - $numcourses = $DB->count_records("course"); - if ($numcourses < COURSE_MAX_COURSES_PER_DROPDOWN && !$showcourses) { - $showcourses = 1; - } - - $sitecontext = context_system::instance(); - $context = context_course::instance($course->id); - - /// Setup for group handling. - if ($course->groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { - $selectedgroup = -1; - $showgroups = false; - } else if ($course->groupmode) { - $showgroups = true; - } else { - $selectedgroup = 0; - $showgroups = false; - } - - if ($selectedgroup === -1) { - if (isset($SESSION->currentgroup[$course->id])) { - $selectedgroup = $SESSION->currentgroup[$course->id]; - } else { - $selectedgroup = groups_get_all_groups($course->id, $USER->id); - if (is_array($selectedgroup)) { - $selectedgroup = array_shift(array_keys($selectedgroup)); - $SESSION->currentgroup[$course->id] = $selectedgroup; - } else { - $selectedgroup = 0; - } - } - } - - // Get all the possible users - $users = array(); - - // Define limitfrom and limitnum for queries below - // If $showusers is enabled... don't apply limitfrom and limitnum - $limitfrom = empty($showusers) ? 0 : ''; - $limitnum = empty($showusers) ? COURSE_MAX_USERS_PER_DROPDOWN + 1 : ''; - - $courseusers = get_enrolled_users($context, '', $selectedgroup, 'u.id, ' . get_all_user_name_fields(true, 'u'), - null, $limitfrom, $limitnum); - - if (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && !$showusers) { - $showusers = 1; - } - - if ($showusers) { - if ($courseusers) { - foreach ($courseusers as $courseuser) { - $users[$courseuser->id] = fullname($courseuser, has_capability('moodle/site:viewfullnames', $context)); - } - } - $users[$CFG->siteguest] = get_string('guestuser'); - } - - if (has_capability('report/log:view', $sitecontext) && $showcourses) { - if ($ccc = $DB->get_records("course", null, "fullname", "id,shortname,fullname,category")) { - foreach ($ccc as $cc) { - if ($cc->category) { - $courses["$cc->id"] = format_string(get_course_display_name_for_list($cc)); - } else { - $courses["$cc->id"] = format_string($cc->fullname) . ' (Site)'; - } - } - } - asort($courses); - } - - $activities = array(); - $selectedactivity = ""; - - $modinfo = get_fast_modinfo($course); - if (!empty($modinfo->cms)) { - $section = 0; - $thissection = array(); - foreach ($modinfo->cms as $cm) { - if (!$cm->uservisible || !$cm->has_view()) { - continue; - } - if ($cm->sectionnum > 0 and $section <> $cm->sectionnum) { - $activities[] = $thissection; - $thissection = array(); - } - $section = $cm->sectionnum; - $modname = strip_tags($cm->get_formatted_name()); - if (core_text::strlen($modname) > 55) { - $modname = core_text::substr($modname, 0, 50)."..."; - } - if (!$cm->visible) { - $modname = "(".$modname.")"; - } - $key = get_section_name($course, $cm->sectionnum); - if (!isset($thissection[$key])) { - $thissection[$key] = array(); - } - $thissection[$key][$cm->id] = $modname; - - if ($cm->id == $modid) { - $selectedactivity = "$cm->id"; - } - } - if (!empty($thissection)) { - $activities[] = $thissection; - } - } - - if (has_capability('report/log:view', $sitecontext) && ($course->id == SITEID)) { - $activities["site_errors"] = get_string("siteerrors"); - if ($modid === "site_errors") { - $selectedactivity = "site_errors"; - } - } - - $strftimedate = get_string("strftimedate"); - $strftimedaydate = get_string("strftimedaydate"); - - asort($users); - - // Prepare the list of action options. - $actions = array( - 'view' => get_string('view'), - 'add' => get_string('add'), - 'update' => get_string('update'), - 'delete' => get_string('delete'), - '-view' => get_string('allchanges') - ); - - // Get all the possible dates - // Note that we are keeping track of real (GMT) time and user time - // User time is only used in displays - all calcs and passing is GMT - - $timenow = time(); // GMT - - // What day is it now for the user, and when is midnight that day (in GMT). - $timemidnight = $today = usergetmidnight($timenow); - - // Put today up the top of the list - $dates = array("$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate) ); - - if (!$course->startdate or ($course->startdate > $timenow)) { - $course->startdate = $course->timecreated; - } - - $numdates = 1; - while ($timemidnight > $course->startdate and $numdates < 365) { - $timemidnight = $timemidnight - 86400; - $timenow = $timenow - 86400; - $dates["$timemidnight"] = userdate($timenow, $strftimedaydate); - $numdates++; - } - - if ($selecteddate == "today") { - $selecteddate = $today; - } - - echo "
wwwroot/report/log/index.php\" method=\"get\">\n"; - echo "
\n"; - echo "\n"; - echo "\n"; - echo "\n"; - if (has_capability('report/log:view', $sitecontext) && $showcourses) { - echo html_writer::label(get_string('selectacourse'), 'menuid', false, array('class' => 'accesshide')); - echo html_writer::select($courses, "id", $course->id, false); - } else { - // echo ''; - $courses = array(); - $courses[$course->id] = get_course_display_name_for_list($course) . (($course->id == SITEID) ? ' ('.get_string('site').') ' : ''); - echo html_writer::label(get_string('selectacourse'), 'menuid', false, array('class' => 'accesshide')); - echo html_writer::select($courses,"id",$course->id, false); - if (has_capability('report/log:view', $sitecontext)) { - $a = new stdClass(); - $a->url = "$CFG->wwwroot/report/log/index.php?chooselog=0&group=$selectedgroup&user=$selecteduser" - ."&id=$course->id&date=$selecteddate&modid=$selectedactivity&showcourses=1&showusers=$showusers"; - print_string('logtoomanycourses','moodle',$a); - } - } - - if ($showgroups) { - if ($cgroups = groups_get_all_groups($course->id)) { - foreach ($cgroups as $cgroup) { - $groups[$cgroup->id] = $cgroup->name; - } - } - else { - $groups = array(); - } - echo html_writer::label(get_string('selectagroup'), 'menugroup', false, array('class' => 'accesshide')); - echo html_writer::select($groups, "group", $selectedgroup, get_string("allgroups")); - } - - if ($showusers) { - echo html_writer::label(get_string('selctauser'), 'menuuser', false, array('class' => 'accesshide')); - echo html_writer::select($users, "user", $selecteduser, get_string("allparticipants")); - } - else { - $users = array(); - if (!empty($selecteduser)) { - $user = $DB->get_record('user', array('id'=>$selecteduser)); - $users[$selecteduser] = fullname($user); - } - else { - $users[0] = get_string('allparticipants'); - } - echo html_writer::label(get_string('selctauser'), 'menuuser', false, array('class' => 'accesshide')); - echo html_writer::select($users, "user", $selecteduser, false); - $a = new stdClass(); - $a->url = "$CFG->wwwroot/report/log/index.php?chooselog=0&group=$selectedgroup&user=$selecteduser" - ."&id=$course->id&date=$selecteddate&modid=$selectedactivity&showusers=1&showcourses=$showcourses"; - print_string('logtoomanyusers','moodle',$a); - } - echo html_writer::label(get_string('date'), 'menudate', false, array('class' => 'accesshide')); - echo html_writer::select($dates, "date", $selecteddate, get_string("alldays")); - - echo html_writer::label(get_string('activities'), 'menumodid', false, array('class' => 'accesshide')); - echo html_writer::select($activities, "modid", $selectedactivity, get_string("allactivities")); - echo html_writer::label(get_string('actions'), 'menumodaction', false, array('class' => 'accesshide')); - echo html_writer::select($actions, 'modaction', $modaction, get_string("allactions")); - - $logformats = array('showashtml' => get_string('displayonpage'), - 'downloadascsv' => get_string('downloadtext'), - 'downloadasods' => get_string('downloadods'), - 'downloadasexcel' => get_string('downloadexcel')); - - echo html_writer::label(get_string('logsformat', 'report_log'), 'menulogformat', false, array('class' => 'accesshide')); - echo html_writer::select($logformats, 'logformat', $logformat, false); - echo ''; - echo '
'; - echo '
'; -} diff --git a/report/log/settings.php b/report/log/settings.php index 74c22e26be4..24fba830b89 100644 --- a/report/log/settings.php +++ b/report/log/settings.php @@ -26,8 +26,9 @@ defined('MOODLE_INTERNAL') || die; -// just a link to course report -$ADMIN->add('reports', new admin_externalpage('reportlog', get_string('log', 'admin'), "$CFG->wwwroot/report/log/index.php?id=".SITEID, 'report/log:view')); +// Just a link to course report. +$ADMIN->add('reports', new admin_externalpage('reportlog', get_string('log', 'admin'), + $CFG->wwwroot . "/report/log/index.php?id=0", 'report/log:view')); -// no report settings +// No report settings. $settings = null; diff --git a/report/log/user.php b/report/log/user.php index 52b235a9188..31230b52d39 100644 --- a/report/log/user.php +++ b/report/log/user.php @@ -24,19 +24,21 @@ require('../../config.php'); require_once($CFG->dirroot.'/report/log/locallib.php'); +require_once($CFG->dirroot.'/lib/tablelib.php'); $userid = required_param('id', PARAM_INT); $courseid = required_param('course', PARAM_INT); $mode = optional_param('mode', 'today', PARAM_ALPHA); $page = optional_param('page', 0, PARAM_INT); $perpage = optional_param('perpage', 100, PARAM_INT); +$logreader = optional_param('logreader', '', PARAM_COMPONENT); // Log reader which will be used for displaying logs. if ($mode !== 'today' and $mode !== 'all') { $mode = 'today'; } -$user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0), '*', MUST_EXIST); -$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST); +$user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST); +$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); $coursecontext = context_course::instance($course->id); $personalcontext = context_user::instance($user->id); @@ -65,7 +67,7 @@ if ($mode === 'today') { $stractivityreport = get_string('activityreport'); $PAGE->set_pagelayout('report'); -$PAGE->set_url('/report/log/user.php', array('id'=>$user->id, 'course'=>$course->id, 'mode'=>$mode)); +$PAGE->set_url('/report/log/user.php', array('id' => $user->id, 'course' => $course->id, 'mode' => $mode)); $PAGE->navigation->extend_for_user($user); $PAGE->navigation->set_userid_for_parent_checks($user->id); // see MDL-25805 for reasons and for full commit reference for reversal when fixed. $PAGE->set_title("$course->shortname: $stractivityreport"); @@ -78,18 +80,24 @@ $event->trigger(); echo $OUTPUT->header(); +$output = $PAGE->get_renderer('report_log'); +$reportlog = new report_log_renderable($logreader, $course, $user->id, 0, '', -1, -1, false, false, true, false, $PAGE->url, + usergetmidnight(time()), '', $page, $perpage, 'timecreated DESC'); +$reportlog->setup_table(); +$reportlog->tablelog->is_downloadable(false); +echo $output->reader_selector($reportlog); + if ($mode === 'today') { echo '
'; - report_log_print_graph($course, $user->id, "userday.png"); + report_log_print_graph($course, $user->id, "userday.png", 0, $logreader); echo '
'; - print_log($course, $user->id, usergetmidnight(time()), "l.time DESC", $page, $perpage, - "user.php?course=$course->id&id=$user->id&mode=$mode"); + echo $output->render($reportlog); } else { echo '
'; - report_log_print_graph($course, $user->id, "usercourse.png"); + report_log_print_graph($course, $user->id, "usercourse.png", 0, $logreader); echo '
'; - print_log($course, $user->id, 0, "l.time DESC", $page, $perpage, - "user.php?course=$course->id&id=$user->id&mode=$mode"); + $reportlog->selecteddate = 0; + echo $output->render($reportlog); } echo $OUTPUT->footer(); \ No newline at end of file diff --git a/report/log/version.php b/report/log/version.php index 3debb3ed5d7..6f2b1b1d058 100644 --- a/report/log/version.php +++ b/report/log/version.php @@ -26,6 +26,6 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2013110500; // The current plugin version (Date: YYYYMMDDXX) +$plugin->version = 2014031800; // The current plugin version (Date: YYYYMMDDXX) $plugin->requires = 2013110500; // Requires this Moodle version $plugin->component = 'report_log'; // Full name of the plugin (used for diagnostics)