Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • Differences Between: [Versions 28 and 29] [Versions 28 and 30] [Versions 28 and 31] [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

       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   * Table log for displaying logs.
      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  
      27  /**
      28   * Table log class for displaying logs.
      29   *
      30   * @package    report_log
      31   * @copyright  2014 Rajesh Taneja <rajesh.taneja@gmail.com>
      32   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      33   */
      34  class report_log_table_log extends table_sql {
      35  
      36      /** @var array list of user fullnames shown in report */
      37      private $userfullnames = array();
      38  
      39      /** @var array list of course short names shown in report */
      40      private $courseshortnames = array();
      41  
      42      /** @var array list of context name shown in report */
      43      private $contextname = array();
      44  
      45      /** @var stdClass filters parameters */
      46      private $filterparams;
      47  
      48      /**
      49       * Sets up the table_log parameters.
      50       *
      51       * @param string $uniqueid unique id of form.
      52       * @param stdClass $filterparams (optional) filter params.
      53       *     - int courseid: id of course
      54       *     - int userid: user id
      55       *     - int|string modid: Module id or "site_errors" to view site errors
      56       *     - int groupid: Group id
      57       *     - \core\log\sql_select_reader logreader: reader from which data will be fetched.
      58       *     - int edulevel: educational level.
      59       *     - string action: view action
      60       *     - int date: Date from which logs to be viewed.
      61       */
      62      public function __construct($uniqueid, $filterparams = null) {
      63          parent::__construct($uniqueid);
      64  
      65          $this->set_attribute('class', 'reportlog generaltable generalbox');
      66          $this->filterparams = $filterparams;
      67          // Add course column if logs are displayed for site.
      68          $cols = array();
      69          $headers = array();
      70          if (empty($filterparams->courseid)) {
      71              $cols = array('course');
      72              $headers = array(get_string('course'));
      73          }
      74  
      75          $this->define_columns(array_merge($cols, array('time', 'fullnameuser', 'relatedfullnameuser', 'context', 'component',
      76                  'eventname', 'description', 'origin', 'ip')));
      77          $this->define_headers(array_merge($headers, array(
      78                  get_string('time'),
      79                  get_string('fullnameuser'),
      80                  get_string('eventrelatedfullnameuser', 'report_log'),
      81                  get_string('eventcontext', 'report_log'),
      82                  get_string('eventcomponent', 'report_log'),
      83                  get_string('eventname'),
      84                  get_string('description'),
      85                  get_string('eventorigin', 'report_log'),
      86                  get_string('ip_address')
      87                  )
      88              ));
      89          $this->collapsible(false);
      90          $this->sortable(false);
      91          $this->pageable(true);
      92      }
      93  
      94      /**
      95       * Generate the course column.
      96       *
      97       * @param stdClass $event event data.
      98       * @return string HTML for the course column.
      99       */
     100      public function col_course($event) {
     101          if (empty($event->courseid) || empty($this->courseshortnames[$event->courseid])) {
     102              return '-';
     103          } else {
     104              return $this->courseshortnames[$event->courseid];
     105          }
     106      }
     107  
     108      /**
     109       * Generate the time column.
     110       *
     111       * @param stdClass $event event data.
     112       * @return string HTML for the time column
     113       */
     114      public function col_time($event) {
     115  
     116          if (empty($this->download)) {
     117              $dateformat = get_string('strftimerecent', 'core_langconfig');
     118          } else {
     119              $dateformat = get_string('strftimedatetimeshort', 'core_langconfig');
     120          }
     121          return userdate($event->timecreated, $dateformat);
     122      }
     123  
     124      /**
     125       * Generate the username column.
     126       *
     127       * @param stdClass $event event data.
     128       * @return string HTML for the username column
     129       */
     130      public function col_fullnameuser($event) {
     131          // Get extra event data for origin and realuserid.
     132          $logextra = $event->get_logextra();
     133  
     134          // Add username who did the action.
     135          if (!empty($logextra['realuserid'])) {
     136              $a = new stdClass();
     137              $params = array('id' => $logextra['realuserid']);
     138              if ($event->courseid) {
     139                  $params['course'] = $event->courseid;
     140              }
     141              $a->realusername = $this->userfullnames[$logextra['realuserid']];
     142              $a->asusername = $this->userfullnames[$event->userid];
     143              if (empty($this->download)) {
     144                  $a->realusername = html_writer::link(new moodle_url('/user/view.php', $params), $a->realusername);
     145                  $params['id'] = $event->userid;
     146                  $a->asusername = html_writer::link(new moodle_url('/user/view.php', $params), $a->asusername);
     147              }
     148              $username = get_string('eventloggedas', 'report_log', $a);
     149          } else if (!empty($event->userid) && !empty($this->userfullnames[$event->userid])) {
     150              $params = array('id' => $event->userid);
     151              if ($event->courseid) {
     152                  $params['course'] = $event->courseid;
     153              }
     154              $username = $this->userfullnames[$event->userid];
     155              if (empty($this->download)) {
     156                  $username = html_writer::link(new moodle_url('/user/view.php', $params), $username);
     157              }
     158          } else {
     159              $username = '-';
     160          }
     161          return $username;
     162      }
     163  
     164      /**
     165       * Generate the related username column.
     166       *
     167       * @param stdClass $event event data.
     168       * @return string HTML for the related username column
     169       */
     170      public function col_relatedfullnameuser($event) {
     171          // Add affected user.
     172          if (!empty($event->relateduserid) && isset($this->userfullnames[$event->relateduserid])) {
     173              $params = array('id' => $event->relateduserid);
     174              if ($event->courseid) {
     175                  $params['course'] = $event->courseid;
     176              }
     177              $username = $this->userfullnames[$event->relateduserid];
     178              if (empty($this->download)) {
     179                  $username = html_writer::link(new moodle_url('/user/view.php', $params), $username);
     180              }
     181          } else {
     182              $username = '-';
     183          }
     184          return $username;
     185      }
     186  
     187      /**
     188       * Generate the context column.
     189       *
     190       * @param stdClass $event event data.
     191       * @return string HTML for the context column
     192       */
     193      public function col_context($event) {
     194          // Add context name.
     195          if ($event->contextid) {
     196              // If context name was fetched before then return, else get one.
     197              if (isset($this->contextname[$event->contextid])) {
     198                  return $this->contextname[$event->contextid];
     199              } else {
     200                  $context = context::instance_by_id($event->contextid, IGNORE_MISSING);
     201                  if ($context) {
     202                      $contextname = $context->get_context_name(true);
     203                      if (empty($this->download) && $url = $context->get_url()) {
     204                          $contextname = html_writer::link($url, $contextname);
     205                      }
     206                  } else {
     207                      $contextname = get_string('other');
     208                  }
     209              }
     210          } else {
     211              $contextname = get_string('other');
     212          }
     213  
     214          $this->contextname[$event->contextid] = $contextname;
     215          return $contextname;
     216      }
     217  
     218      /**
     219       * Generate the component column.
     220       *
     221       * @param stdClass $event event data.
     222       * @return string HTML for the component column
     223       */
     224      public function col_component($event) {
     225          // Component.
     226          $componentname = $event->component;
     227          if (($event->component === 'core') || ($event->component === 'legacy')) {
     228              return  get_string('coresystem');
     229          } else if (get_string_manager()->string_exists('pluginname', $event->component)) {
     230              return get_string('pluginname', $event->component);
     231          } else {
     232              return $componentname;
     233          }
     234      }
     235  
     236      /**
     237       * Generate the event name column.
     238       *
     239       * @param stdClass $event event data.
     240       * @return string HTML for the event name column
     241       */
     242      public function col_eventname($event) {
     243          // Event name.
     244          if ($this->filterparams->logreader instanceof logstore_legacy\log\store) {
     245              // Hack for support of logstore_legacy.
     246              $eventname = $event->eventname;
     247          } else {
     248              $eventname = $event->get_name();
     249          }
     250          // Only encode as an action link if we're not downloading.
     251          if (($url = $event->get_url()) && empty($this->download)) {
     252              $eventname = $this->action_link($url, $eventname, 'action');
     253          }
     254          return $eventname;
     255      }
     256  
     257      /**
     258       * Generate the description column.
     259       *
     260       * @param stdClass $event event data.
     261       * @return string HTML for the description column
     262       */
     263      public function col_description($event) {
     264          // Description.
     265          return $event->get_description();
     266      }
     267  
     268      /**
     269       * Generate the origin column.
     270       *
     271       * @param stdClass $event event data.
     272       * @return string HTML for the origin column
     273       */
     274      public function col_origin($event) {
     275          // Get extra event data for origin and realuserid.
     276          $logextra = $event->get_logextra();
     277  
     278          // Add event origin, normally IP/cron.
     279          return $logextra['origin'];
     280      }
     281  
     282      /**
     283       * Generate the ip column.
     284       *
     285       * @param stdClass $event event data.
     286       * @return string HTML for the ip column
     287       */
     288      public function col_ip($event) {
     289          // Get extra event data for origin and realuserid.
     290          $logextra = $event->get_logextra();
     291          $ip = $logextra['ip'];
     292  
     293          if (empty($this->download)) {
     294              $url = new moodle_url("/iplookup/index.php?ip={$ip}&user={$event->userid}");
     295              $ip = $this->action_link($url, $ip, 'ip');
     296          }
     297          return $ip;
     298      }
     299  
     300      /**
     301       * Method to create a link with popup action.
     302       *
     303       * @param moodle_url $url The url to open.
     304       * @param string $text Anchor text for the link.
     305       * @param string $name Name of the popup window.
     306       *
     307       * @return string html to use.
     308       */
     309      protected function action_link(moodle_url $url, $text, $name = 'popup') {
     310          global $OUTPUT;
     311          $link = new action_link($url, $text, new popup_action('click', $url, $name, array('height' => 440, 'width' => 700)));
     312          return $OUTPUT->render($link);
     313      }
     314  
     315      /**
     316       * Helper function to get legacy crud action.
     317       *
     318       * @param string $crud crud action
     319       * @return string legacy action.
     320       */
     321      public function get_legacy_crud_action($crud) {
     322          $legacyactionmap = array('c' => 'add', 'r' => 'view', 'u' => 'update', 'd' => 'delete');
     323          if (array_key_exists($crud, $legacyactionmap)) {
     324              return $legacyactionmap[$crud];
     325          } else {
     326              // From old legacy log.
     327              return '-view';
     328          }
     329      }
     330  
     331      /**
     332       * Helper function which is used by build logs to get action sql and param.
     333       *
     334       * @return array sql and param for action.
     335       */
     336      public function get_action_sql() {
     337          global $DB;
     338  
     339          // In new logs we have a field to pick, and in legacy try get this from action.
     340          if ($this->filterparams->logreader instanceof logstore_legacy\log\store) {
     341              $action = $this->get_legacy_crud_action($this->filterparams->action);
     342              $firstletter = substr($action, 0, 1);
     343              if ($firstletter == '-') {
     344                  $sql = $DB->sql_like('action', ':action', false, true, true);
     345                  $params['action'] = '%'.substr($action, 1).'%';
     346              } else {
     347                  $sql = $DB->sql_like('action', ':action', false);
     348                  $params['action'] = '%'.$action.'%';
     349              }
     350          } else if (!empty($this->filterparams->action)) {
     351              $sql = "crud = :crud";
     352              $params['crud'] = $this->filterparams->action;
     353          } else {
     354              // Add condition for all possible values of crud (to use db index).
     355              list($sql, $params) = $DB->get_in_or_equal(array('c', 'r', 'u', 'd'),
     356                      SQL_PARAMS_NAMED, 'crud');
     357              $sql = "crud ".$sql;
     358          }
     359          return array($sql, $params);
     360      }
     361  
     362      /**
     363       * Helper function which is used by build logs to get course module sql and param.
     364       *
     365       * @return array sql and param for action.
     366       */
     367      public function get_cm_sql() {
     368          $joins = array();
     369          $params = array();
     370  
     371          if ($this->filterparams->logreader instanceof logstore_legacy\log\store) {
     372              // The legacy store doesn't support context level.
     373              $joins[] = "cmid = :cmid";
     374              $params['cmid'] = $this->filterparams->modid;
     375          } else {
     376              $joins[] = "contextinstanceid = :contextinstanceid";
     377              $joins[] = "contextlevel = :contextmodule";
     378              $params['contextinstanceid'] = $this->filterparams->modid;
     379              $params['contextmodule'] = CONTEXT_MODULE;
     380          }
     381  
     382          $sql = implode(' AND ', $joins);
     383          return array($sql, $params);
     384      }
     385  
     386      /**
     387       * Query the reader. Store results in the object for use by build_table.
     388       *
     389       * @param int $pagesize size of page for paginated displayed table.
     390       * @param bool $useinitialsbar do you want to use the initials bar.
     391       */
     392      public function query_db($pagesize, $useinitialsbar = true) {
     393          global $DB;
     394  
     395          $joins = array();
     396          $params = array();
     397  
     398          // If we filter by userid and module id we also need to filter by crud and edulevel to ensure DB index is engaged.
     399          $useextendeddbindex = !($this->filterparams->logreader instanceof logstore_legacy\log\store)
     400                  && !empty($this->filterparams->userid) && !empty($this->filterparams->modid);
     401  
     402          $groupid = 0;
     403          if (!empty($this->filterparams->courseid) && $this->filterparams->courseid != SITEID) {
     404              if (!empty($this->filterparams->groupid)) {
     405                  $groupid = $this->filterparams->groupid;
     406              }
     407  
     408              $joins[] = "courseid = :courseid";
     409              $params['courseid'] = $this->filterparams->courseid;
     410          }
     411  
     412          if (!empty($this->filterparams->siteerrors)) {
     413              $joins[] = "( action='error' OR action='infected' OR action='failed' )";
     414          }
     415  
     416          if (!empty($this->filterparams->modid)) {
     417              list($actionsql, $actionparams) = $this->get_cm_sql();
     418              $joins[] = $actionsql;
     419              $params = array_merge($params, $actionparams);
     420          }
     421  
     422          if (!empty($this->filterparams->action) || $useextendeddbindex) {
     423              list($actionsql, $actionparams) = $this->get_action_sql();
     424              $joins[] = $actionsql;
     425              $params = array_merge($params, $actionparams);
     426          }
     427  
     428          // Getting all members of a group.
     429          if ($groupid and empty($this->filterparams->userid)) {
     430              if ($gusers = groups_get_members($groupid)) {
     431                  $gusers = array_keys($gusers);
     432                  $joins[] = 'userid IN (' . implode(',', $gusers) . ')';
     433              } else {
     434                  $joins[] = 'userid = 0'; // No users in groups, so we want something that will always be false.
     435              }
     436          } else if (!empty($this->filterparams->userid)) {
     437              $joins[] = "userid = :userid";
     438              $params['userid'] = $this->filterparams->userid;
     439          }
     440  
     441          if (!empty($this->filterparams->date)) {
     442              $joins[] = "timecreated > :date AND timecreated < :enddate";
     443              $params['date'] = $this->filterparams->date;
     444              $params['enddate'] = $this->filterparams->date + DAYSECS; // Show logs only for the selected date.
     445          }
     446  
     447          if (isset($this->filterparams->edulevel) && ($this->filterparams->edulevel >= 0)) {
     448              $joins[] = "edulevel = :edulevel";
     449              $params['edulevel'] = $this->filterparams->edulevel;
     450          } else if ($useextendeddbindex) {
     451              list($edulevelsql, $edulevelparams) = $DB->get_in_or_equal(array(\core\event\base::LEVEL_OTHER,
     452                  \core\event\base::LEVEL_PARTICIPATING, \core\event\base::LEVEL_TEACHING), SQL_PARAMS_NAMED, 'edulevel');
     453              $joins[] = "edulevel ".$edulevelsql;
     454              $params = array_merge($params, $edulevelparams);
     455          }
     456  
     457          if (!($this->filterparams->logreader instanceof logstore_legacy\log\store)) {
     458              // Filter out anonymous actions, this is N/A for legacy log because it never stores them.
     459              $joins[] = "anonymous = 0";
     460          }
     461  
     462          $selector = implode(' AND ', $joins);
     463  
     464          if (!$this->is_downloading()) {
     465              $total = $this->filterparams->logreader->get_events_select_count($selector, $params);
     466              $this->pagesize($pagesize, $total);
     467          } else {
     468              $this->pageable(false);
     469          }
     470  
     471          $this->rawdata = $this->filterparams->logreader->get_events_select($selector, $params, $this->filterparams->orderby,
     472                  $this->get_page_start(), $this->get_page_size());
     473  
     474          // Set initial bars.
     475          if ($useinitialsbar && !$this->is_downloading()) {
     476              $this->initialbars($total > $pagesize);
     477          }
     478  
     479          // Update list of users and courses list which will be displayed on log page.
     480          $this->update_users_and_courses_used();
     481      }
     482  
     483      /**
     484       * Helper function to create list of course shortname and user fullname shown in log report.
     485       * This will update $this->userfullnames and $this->courseshortnames array with userfullname and courseshortname (with link),
     486       * which will be used to render logs in table.
     487       */
     488      public function update_users_and_courses_used() {
     489          global $SITE, $DB;
     490  
     491          $this->userfullnames = array();
     492          $this->courseshortnames = array($SITE->id => $SITE->shortname);
     493          $userids = array();
     494          $courseids = array();
     495          // For each event cache full username and course.
     496          // Get list of userids and courseids which will be shown in log report.
     497          foreach ($this->rawdata as $event) {
     498              $logextra = $event->get_logextra();
     499              if (!empty($event->userid) && !in_array($event->userid, $userids)) {
     500                  $userids[] = $event->userid;
     501              }
     502              if (!empty($logextra['realuserid']) && !in_array($logextra['realuserid'], $userids)) {
     503                  $userids[] = $logextra['realuserid'];
     504              }
     505              if (!empty($event->relateduserid) && !in_array($event->relateduserid, $userids)) {
     506                  $userids[] = $event->relateduserid;
     507              }
     508  
     509              if (!empty($event->courseid) && ($event->courseid != $SITE->id) && !in_array($event->courseid, $courseids)) {
     510                  $courseids[] = $event->courseid;
     511              }
     512          }
     513  
     514          // Get user fullname and put that in return list.
     515          if (!empty($userids)) {
     516              list($usql, $uparams) = $DB->get_in_or_equal($userids);
     517              $users = $DB->get_records_sql("SELECT id," . get_all_user_name_fields(true) . " FROM {user} WHERE id " . $usql,
     518                      $uparams);
     519              foreach ($users as $userid => $user) {
     520                  $this->userfullnames[$userid] = fullname($user);
     521              }
     522          }
     523  
     524          // Get course shortname and put that in return list.
     525          if (!empty($courseids)) { // If all logs don't belog to site level then get course info.
     526              list($coursesql, $courseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
     527              $ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
     528              $ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
     529              $courseparams['contextlevel'] = CONTEXT_COURSE;
     530              $sql = "SELECT c.id,c.shortname $ccselect FROM {course} c
     531                     $ccjoin
     532                       WHERE c.id " . $coursesql;
     533  
     534              $courses = $DB->get_records_sql($sql, $courseparams);
     535              foreach ($courses as $courseid => $course) {
     536                  $url = new moodle_url("/course/view.php", array('id' => $courseid));
     537                  context_helper::preload_from_record($course);
     538                  $context = context_course::instance($courseid, IGNORE_MISSING);
     539                  // Method format_string() takes care of missing contexts.
     540                  $this->courseshortnames[$courseid] = html_writer::link($url, format_string($course->shortname, true,
     541                          array('context' => $context)));
     542              }
     543          }
     544      }
     545  }
    

    Search This Site: