Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   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  
  93      /** @var table_log table log which will be used for rendering logs */
  94      public $tablelog;
  95  
  96      /** @var array group ids */
  97      public $grouplist;
  98  
  99      /**
 100       * Constructor.
 101       *
 102       * @param string $logreader (optional)reader pluginname from which logs will be fetched.
 103       * @param stdClass|int $course (optional) course record or id
 104       * @param int $userid (optional) id of user to filter records for.
 105       * @param int|string $modid (optional) module id or site_errors for filtering errors.
 106       * @param string $action (optional) action name to filter.
 107       * @param int $groupid (optional) groupid of user.
 108       * @param int $edulevel (optional) educational level.
 109       * @param bool $showcourses (optional) show courses.
 110       * @param bool $showusers (optional) show users.
 111       * @param bool $showreport (optional) show report.
 112       * @param bool $showselectorform (optional) show selector form.
 113       * @param moodle_url|string $url (optional) page url.
 114       * @param int $date date (optional) timestamp of start of the day for which logs will be displayed.
 115       * @param string $logformat log format.
 116       * @param int $page (optional) page number.
 117       * @param int $perpage (optional) number of records to show per page.
 118       * @param string $order (optional) sortorder of fetched records
 119       */
 120      public function __construct($logreader = "", $course = 0, $userid = 0, $modid = 0, $action = "", $groupid = 0, $edulevel = -1,
 121              $showcourses = false, $showusers = false, $showreport = true, $showselectorform = true, $url = "", $date = 0,
 122              $logformat='showashtml', $page = 0, $perpage = 100, $order = "timecreated ASC", $origin ='') {
 123  
 124          global $PAGE;
 125  
 126          // Use first reader as selected reader, if not passed.
 127          if (empty($logreader)) {
 128              $readers = $this->get_readers();
 129              if (!empty($readers)) {
 130                  reset($readers);
 131                  $logreader = key($readers);
 132              } else {
 133                  $logreader = null;
 134              }
 135          }
 136          // Use page url if empty.
 137          if (empty($url)) {
 138              $url = new moodle_url($PAGE->url);
 139          } else {
 140              $url = new moodle_url($url);
 141          }
 142          $this->selectedlogreader = $logreader;
 143          $url->param('logreader', $logreader);
 144  
 145          // Use site course id, if course is empty.
 146          if (!empty($course) && is_int($course)) {
 147              $course = get_course($course);
 148          }
 149          $this->course = $course;
 150  
 151          $this->userid = $userid;
 152          $this->date = $date;
 153          $this->page = $page;
 154          $this->perpage = $perpage;
 155          $this->url = $url;
 156          $this->order = $order;
 157          $this->modid = $modid;
 158          $this->action = $action;
 159          $this->groupid = $groupid;
 160          $this->edulevel = $edulevel;
 161          $this->showcourses = $showcourses;
 162          $this->showusers = $showusers;
 163          $this->showreport = $showreport;
 164          $this->showselectorform = $showselectorform;
 165          $this->logformat = $logformat;
 166          $this->origin = $origin;
 167      }
 168  
 169      /**
 170       * Get a list of enabled sql_reader objects/name
 171       *
 172       * @param bool $nameonly if true only reader names will be returned.
 173       * @return array core\log\sql_reader object or name.
 174       */
 175      public function get_readers($nameonly = false) {
 176          if (!isset($this->logmanager)) {
 177              $this->logmanager = get_log_manager();
 178          }
 179  
 180          $readers = $this->logmanager->get_readers('core\log\sql_reader');
 181          if ($nameonly) {
 182              foreach ($readers as $pluginname => $reader) {
 183                  $readers[$pluginname] = $reader->get_name();
 184              }
 185          }
 186          return $readers;
 187      }
 188  
 189      /**
 190       * Helper function to return list of activities to show in selection filter.
 191       *
 192       * @return array list of activities.
 193       */
 194      public function get_activities_list() {
 195          $activities = array();
 196  
 197          // For site just return site errors option.
 198          $sitecontext = context_system::instance();
 199          if ($this->course->id == SITEID && has_capability('report/log:view', $sitecontext)) {
 200              $activities["site_errors"] = get_string("siteerrors");
 201              return $activities;
 202          }
 203  
 204          $modinfo = get_fast_modinfo($this->course);
 205          if (!empty($modinfo->cms)) {
 206              $section = 0;
 207              $thissection = array();
 208              foreach ($modinfo->cms as $cm) {
 209                  // Exclude activities that aren't visible or have no view link (e.g. label). Account for folders displayed inline.
 210                  if (!$cm->uservisible || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0)) {
 211                      continue;
 212                  }
 213                  if ($cm->sectionnum > 0 and $section <> $cm->sectionnum) {
 214                      $activities[] = $thissection;
 215                      $thissection = array();
 216                  }
 217                  $section = $cm->sectionnum;
 218                  $modname = strip_tags($cm->get_formatted_name());
 219                  if (core_text::strlen($modname) > 55) {
 220                      $modname = core_text::substr($modname, 0, 50)."...";
 221                  }
 222                  if (!$cm->visible) {
 223                      $modname = "(".$modname.")";
 224                  }
 225                  $key = get_section_name($this->course, $cm->sectionnum);
 226                  if (!isset($thissection[$key])) {
 227                      $thissection[$key] = array();
 228                  }
 229                  $thissection[$key][$cm->id] = $modname;
 230              }
 231              if (!empty($thissection)) {
 232                  $activities[] = $thissection;
 233              }
 234          }
 235          return $activities;
 236      }
 237  
 238      /**
 239       * Helper function to get selected group.
 240       *
 241       * @return int selected group.
 242       */
 243      public function get_selected_group() {
 244          global $SESSION, $USER;
 245  
 246          // No groups for system.
 247          if (empty($this->course)) {
 248              return 0;
 249          }
 250  
 251          $context = context_course::instance($this->course->id);
 252  
 253          $selectedgroup = 0;
 254          // Setup for group handling.
 255          $groupmode = groups_get_course_groupmode($this->course);
 256          if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
 257              $selectedgroup = -1;
 258          } else if ($groupmode) {
 259              $selectedgroup = $this->groupid;
 260          } else {
 261              $selectedgroup = 0;
 262          }
 263  
 264          if ($selectedgroup === -1) {
 265              if (isset($SESSION->currentgroup[$this->course->id])) {
 266                  $selectedgroup = $SESSION->currentgroup[$this->course->id];
 267              } else {
 268                  $selectedgroup = groups_get_all_groups($this->course->id, $USER->id);
 269                  if (is_array($selectedgroup)) {
 270                      $groupids = array_keys($selectedgroup);
 271                      $selectedgroup = array_shift($groupids);
 272                      $SESSION->currentgroup[$this->course->id] = $selectedgroup;
 273                  } else {
 274                      $selectedgroup = 0;
 275                  }
 276              }
 277          }
 278          return $selectedgroup;
 279      }
 280  
 281      /**
 282       * Return list of actions for log reader.
 283       *
 284       * @todo MDL-44528 Get list from log_store.
 285       * @return array list of action options.
 286       */
 287      public function get_actions() {
 288          $actions = array(
 289                  'c' => get_string('create'),
 290                  'r' => get_string('view'),
 291                  'u' => get_string('update'),
 292                  'd' => get_string('delete'),
 293                  'cud' => get_string('allchanges')
 294                  );
 295          return $actions;
 296      }
 297  
 298      /**
 299       * Return selected user fullname.
 300       *
 301       * @return string user fullname.
 302       */
 303      public function get_selected_user_fullname() {
 304          $user = core_user::get_user($this->userid);
 305          if (empty($this->course)) {
 306              // We are in system context.
 307              $context = context_system::instance();
 308          } else {
 309              // We are in course context.
 310              $context = context_course::instance($this->course->id);
 311          }
 312          return fullname($user, has_capability('moodle/site:viewfullnames', $context));
 313      }
 314  
 315      /**
 316       * Return list of courses to show in selector.
 317       *
 318       * @return array list of courses.
 319       */
 320      public function get_course_list() {
 321          global $DB, $SITE;
 322  
 323          $courses = array();
 324  
 325          $sitecontext = context_system::instance();
 326          // First check to see if we can override showcourses and showusers.
 327          $numcourses = $DB->count_records("course");
 328          if ($numcourses < COURSE_MAX_COURSES_PER_DROPDOWN && !$this->showcourses) {
 329              $this->showcourses = 1;
 330          }
 331  
 332          // Check if course filter should be shown.
 333          if (has_capability('report/log:view', $sitecontext) && $this->showcourses) {
 334              if ($courserecords = $DB->get_records("course", null, "fullname", "id,shortname,fullname,category")) {
 335                  foreach ($courserecords as $course) {
 336                      if ($course->id == SITEID) {
 337                          $courses[$course->id] = format_string($course->fullname) . ' (' . get_string('site') . ')';
 338                      } else {
 339                          $courses[$course->id] = format_string(get_course_display_name_for_list($course));
 340                      }
 341                  }
 342              }
 343              core_collator::asort($courses);
 344          }
 345          return $courses;
 346      }
 347  
 348      /**
 349       * Return list of groups that are used in this course. This is done when groups are used in the course
 350       * and the user is allowed to see all groups or groups are visible anyway. If groups are used but the
 351       * mode is separate groups and the user is not allowed to see all groups, the list contains the groups
 352       * only, where the user is member.
 353       * If the course uses no groups, the list is empty.
 354       *
 355       * @return array list of groups.
 356       */
 357      public function get_group_list() {
 358          global $USER;
 359  
 360          if ($this->grouplist !== null) {
 361              return $this->grouplist;
 362          }
 363  
 364          // No groups for system.
 365          if (empty($this->course)) {
 366              $this->grouplist = [];
 367              return $this->grouplist;
 368          }
 369  
 370          $context = context_course::instance($this->course->id);
 371          $this->grouplist = [];
 372          $groupmode = groups_get_course_groupmode($this->course);
 373          $cgroups = [];
 374          if (($groupmode == VISIBLEGROUPS) ||
 375              ($groupmode == SEPARATEGROUPS && has_capability('moodle/site:accessallgroups', $context))) {
 376              $cgroups = groups_get_all_groups($this->course->id);
 377          } else if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) {
 378              $cgroups = groups_get_all_groups($this->course->id, $USER->id);
 379          }
 380          foreach ($cgroups as $cgroup) {
 381              $this->grouplist[$cgroup->id] = $cgroup->name;
 382          }
 383          return $this->grouplist;
 384      }
 385  
 386      /**
 387       * Return list of users.
 388       *
 389       * @return array list of users.
 390       */
 391      public function get_user_list() {
 392          global $CFG, $SITE;
 393  
 394          $courseid = $SITE->id;
 395          if (!empty($this->course)) {
 396              $courseid = $this->course->id;
 397          }
 398          $context = context_course::instance($courseid);
 399          $limitfrom = empty($this->showusers) ? 0 : '';
 400          $limitnum  = empty($this->showusers) ? COURSE_MAX_USERS_PER_DROPDOWN + 1 : '';
 401  
 402          // Get the groups of that course.
 403          $groups = $this->get_group_list();
 404          // Check here if we are not in group mode, or in group mode but narrow the group selection
 405          // to the group of the user.
 406          if (empty($groups) || !empty($this->groupid) && isset($groups[(int)$this->groupid])) {
 407              // No groups are used in that course, therefore get all users (maybe limited to one group).
 408              $courseusers = get_enrolled_users($context, '', $this->groupid, 'u.id, ' . get_all_user_name_fields(true, 'u'),
 409                  null, $limitfrom, $limitnum);
 410          } else {
 411              // The course uses groups, get the users from these groups.
 412              $groupids = array_keys($groups);
 413              try {
 414                  $enrolments = enrol_get_course_users($courseid, false, [], [], $groupids);
 415                  $courseusers = [];
 416                  foreach ($enrolments as $enrolment) {
 417                      $courseusers[$enrolment->id] = $enrolment;
 418                  }
 419              } catch (Exception $e) {
 420                  $courseusers = [];
 421              }
 422          }
 423  
 424          if (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && !$this->showusers) {
 425              $this->showusers = 1;
 426          }
 427  
 428          $users = array();
 429          if ($this->showusers) {
 430              if ($courseusers) {
 431                  foreach ($courseusers as $courseuser) {
 432                       $users[$courseuser->id] = fullname($courseuser, has_capability('moodle/site:viewfullnames', $context));
 433                  }
 434              }
 435              $users[$CFG->siteguest] = get_string('guestuser');
 436          }
 437          return $users;
 438      }
 439  
 440      /**
 441       * Return list of date options.
 442       *
 443       * @return array date options.
 444       */
 445      public function get_date_options() {
 446          global $SITE;
 447  
 448          $strftimedate = get_string("strftimedate");
 449          $strftimedaydate = get_string("strftimedaydate");
 450  
 451          // Get all the possible dates.
 452          // Note that we are keeping track of real (GMT) time and user time.
 453          // User time is only used in displays - all calcs and passing is GMT.
 454          $timenow = time(); // GMT.
 455  
 456          // What day is it now for the user, and when is midnight that day (in GMT).
 457          $timemidnight = usergetmidnight($timenow);
 458  
 459          // Put today up the top of the list.
 460          $dates = array("$timemidnight" => get_string("today").", ".userdate($timenow, $strftimedate) );
 461  
 462          // If course is empty, get it from frontpage.
 463          $course = $SITE;
 464          if (!empty($this->course)) {
 465              $course = $this->course;
 466          }
 467          if (!$course->startdate or ($course->startdate > $timenow)) {
 468              $course->startdate = $course->timecreated;
 469          }
 470  
 471          $numdates = 1;
 472          while ($timemidnight > $course->startdate and $numdates < 365) {
 473              $timemidnight = $timemidnight - 86400;
 474              $timenow = $timenow - 86400;
 475              $dates["$timemidnight"] = userdate($timenow, $strftimedaydate);
 476              $numdates++;
 477          }
 478          return $dates;
 479      }
 480  
 481      /**
 482       * Return list of components to show in selector.
 483       *
 484       * @return array list of origins.
 485       */
 486      public function get_origin_options() {
 487          $ret = array();
 488          $ret[''] = get_string('allsources', 'report_log');
 489          $ret['cli'] = get_string('cli', 'report_log');
 490          $ret['restore'] = get_string('restore', 'report_log');
 491          $ret['web'] = get_string('web', 'report_log');
 492          $ret['ws'] = get_string('ws', 'report_log');
 493          $ret['---'] = get_string('other', 'report_log');
 494          return $ret;
 495      }
 496  
 497      /**
 498       * Return list of edulevel.
 499       *
 500       * @todo MDL-44528 Get list from log_store.
 501       * @return array list of edulevels.
 502       */
 503      public function get_edulevel_options() {
 504          $edulevels = array(
 505                      -1 => get_string("edulevel"),
 506                      1 => get_string('edulevelteacher'),
 507                      2 => get_string('edulevelparticipating'),
 508                      0 => get_string('edulevelother')
 509                      );
 510          return $edulevels;
 511      }
 512  
 513      /**
 514       * Setup table log.
 515       */
 516      public function setup_table() {
 517          $readers = $this->get_readers();
 518  
 519          $filter = new \stdClass();
 520          if (!empty($this->course)) {
 521              $filter->courseid = $this->course->id;
 522          } else {
 523              $filter->courseid = 0;
 524          }
 525  
 526          $filter->userid = $this->userid;
 527          $filter->modid = $this->modid;
 528          $filter->groupid = $this->get_selected_group();
 529          $filter->logreader = $readers[$this->selectedlogreader];
 530          $filter->edulevel = $this->edulevel;
 531          $filter->action = $this->action;
 532          $filter->date = $this->date;
 533          $filter->orderby = $this->order;
 534          $filter->origin = $this->origin;
 535          // If showing site_errors.
 536          if ('site_errors' === $this->modid) {
 537              $filter->siteerrors = true;
 538              $filter->modid = 0;
 539          }
 540  
 541          $this->tablelog = new report_log_table_log('report_log', $filter);
 542          $this->tablelog->define_baseurl($this->url);
 543          $this->tablelog->is_downloadable(true);
 544          $this->tablelog->show_download_buttons_at(array(TABLE_P_BOTTOM));
 545      }
 546  
 547      /**
 548       * Download logs in specified format.
 549       */
 550      public function download() {
 551          $filename = 'logs_' . userdate(time(), get_string('backupnameformat', 'langconfig'), 99, false);
 552          if ($this->course->id !== SITEID) {
 553              $courseshortname = format_string($this->course->shortname, true,
 554                      array('context' => context_course::instance($this->course->id)));
 555              $filename = clean_filename('logs_' . $courseshortname . '_' . userdate(time(),
 556                      get_string('backupnameformat', 'langconfig'), 99, false));
 557          }
 558          $this->tablelog->is_downloading($this->logformat, $filename);
 559          $this->tablelog->out($this->perpage, false);
 560      }
 561  }