Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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   * External database store.
  19   *
  20   * @package    logstore_database
  21   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace logstore_database\log;
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  class store implements \tool_log\log\writer, \core\log\sql_reader {
  29      use \tool_log\helper\store,
  30          \tool_log\helper\reader,
  31          \tool_log\helper\buffered_writer {
  32          dispose as helper_dispose;
  33      }
  34  
  35      /** @var \moodle_database $extdb */
  36      protected $extdb;
  37  
  38      /** @var bool $logguests true if logging guest access */
  39      protected $logguests;
  40  
  41      /** @var array $includelevels An array of education levels to include */
  42      protected $includelevels = array();
  43  
  44      /** @var array $includeactions An array of actions types to include */
  45      protected $includeactions = array();
  46  
  47      /**
  48       * Construct
  49       *
  50       * @param \tool_log\log\manager $manager
  51       */
  52      public function __construct(\tool_log\log\manager $manager) {
  53          $this->helper_setup($manager);
  54          $this->buffersize = $this->get_config('buffersize', 50);
  55          $this->logguests = $this->get_config('logguests', 1);
  56          $actions = $this->get_config('includeactions', '');
  57          $levels = $this->get_config('includelevels', '');
  58          $this->includeactions = $actions === '' ? array() : explode(',', $actions);
  59          $this->includelevels = $levels === '' ? array() : explode(',', $levels);
  60          // JSON writing defaults to false (table format compatibility with older versions).
  61          // Note: This variable is defined in the buffered_writer trait.
  62          $this->jsonformat = (bool)$this->get_config('jsonformat', false);
  63      }
  64  
  65      /**
  66       * Setup the Database.
  67       *
  68       * @return bool
  69       */
  70      protected function init() {
  71          if (isset($this->extdb)) {
  72              return !empty($this->extdb);
  73          }
  74  
  75          $dbdriver = $this->get_config('dbdriver');
  76          if (empty($dbdriver)) {
  77              $this->extdb = false;
  78              return false;
  79          }
  80          list($dblibrary, $dbtype) = explode('/', $dbdriver);
  81  
  82          if (!$db = \moodle_database::get_driver_instance($dbtype, $dblibrary, true)) {
  83              debugging("Unknown driver $dblibrary/$dbtype", DEBUG_DEVELOPER);
  84              $this->extdb = false;
  85              return false;
  86          }
  87  
  88          $dboptions = array();
  89          $dboptions['dbpersist'] = $this->get_config('dbpersist', '0');
  90          $dboptions['dbsocket'] = $this->get_config('dbsocket', '');
  91          $dboptions['dbport'] = $this->get_config('dbport', '');
  92          $dboptions['dbschema'] = $this->get_config('dbschema', '');
  93          $dboptions['dbcollation'] = $this->get_config('dbcollation', '');
  94          $dboptions['dbhandlesoptions'] = $this->get_config('dbhandlesoptions', false);
  95          try {
  96              $db->connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'),
  97                  $this->get_config('dbname'), false, $dboptions);
  98              $tables = $db->get_tables();
  99              if (!in_array($this->get_config('dbtable'), $tables)) {
 100                  debugging('Cannot find the specified table', DEBUG_DEVELOPER);
 101                  $this->extdb = false;
 102                  return false;
 103              }
 104          } catch (\moodle_exception $e) {
 105              debugging('Cannot connect to external database: ' . $e->getMessage(), DEBUG_DEVELOPER);
 106              $this->extdb = false;
 107              return false;
 108          }
 109  
 110          $this->extdb = $db;
 111          return true;
 112      }
 113  
 114      /**
 115       * Should the event be ignored (== not logged)?
 116       * @param \core\event\base $event
 117       * @return bool
 118       */
 119      protected function is_event_ignored(\core\event\base $event) {
 120          if (!in_array($event->crud, $this->includeactions) &&
 121              !in_array($event->edulevel, $this->includelevels)
 122          ) {
 123              // Ignore event if the store settings do not want to store it.
 124              return true;
 125          }
 126          if ((!CLI_SCRIPT or PHPUNIT_TEST) and !$this->logguests) {
 127              // Always log inside CLI scripts because we do not login there.
 128              if (!isloggedin() or isguestuser()) {
 129                  return true;
 130              }
 131          }
 132          return false;
 133      }
 134  
 135      /**
 136       * Insert events in bulk to the database.
 137       *
 138       * @param array $evententries raw event data
 139       */
 140      protected function insert_event_entries($evententries) {
 141          if (!$this->init()) {
 142              return;
 143          }
 144          if (!$dbtable = $this->get_config('dbtable')) {
 145              return;
 146          }
 147          try {
 148              $this->extdb->insert_records($dbtable, $evententries);
 149          } catch (\moodle_exception $e) {
 150              debugging('Cannot write to external database: ' . $e->getMessage(), DEBUG_DEVELOPER);
 151          }
 152      }
 153  
 154      /**
 155       * Get an array of events based on the passed on params.
 156       *
 157       * @param string $selectwhere select conditions.
 158       * @param array $params params.
 159       * @param string $sort sortorder.
 160       * @param int $limitfrom limit constraints.
 161       * @param int $limitnum limit constraints.
 162       *
 163       * @return array|\core\event\base[] array of events.
 164       */
 165      public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
 166          if (!$this->init()) {
 167              return array();
 168          }
 169  
 170          if (!$dbtable = $this->get_config('dbtable')) {
 171              return array();
 172          }
 173  
 174          $sort = self::tweak_sort_by_id($sort);
 175  
 176          $events = array();
 177          $records = $this->extdb->get_records_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
 178  
 179          foreach ($records as $data) {
 180              if ($event = $this->get_log_event($data)) {
 181                  $events[$data->id] = $event;
 182              }
 183          }
 184  
 185          return $events;
 186      }
 187  
 188      /**
 189       * Fetch records using given criteria returning a Traversable object.
 190       *
 191       * Note that the traversable object contains a moodle_recordset, so
 192       * remember that is important that you call close() once you finish
 193       * using it.
 194       *
 195       * @param string $selectwhere
 196       * @param array $params
 197       * @param string $sort
 198       * @param int $limitfrom
 199       * @param int $limitnum
 200       * @return \core\dml\recordset_walk|\core\event\base[]
 201       */
 202      public function get_events_select_iterator($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
 203          if (!$this->init()) {
 204              return array();
 205          }
 206  
 207          if (!$dbtable = $this->get_config('dbtable')) {
 208              return array();
 209          }
 210  
 211          $sort = self::tweak_sort_by_id($sort);
 212  
 213          $recordset = $this->extdb->get_recordset_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
 214  
 215          return new \core\dml\recordset_walk($recordset, array($this, 'get_log_event'));
 216      }
 217  
 218      /**
 219       * Returns an event from the log data.
 220       *
 221       * @param stdClass $data Log data
 222       * @return \core\event\base
 223       */
 224      public function get_log_event($data) {
 225  
 226          $extra = array('origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid);
 227          $data = (array)$data;
 228          $id = $data['id'];
 229          $data['other'] = self::decode_other($data['other']);
 230          if ($data['other'] === false) {
 231              $data['other'] = array();
 232          }
 233          unset($data['origin']);
 234          unset($data['ip']);
 235          unset($data['realuserid']);
 236          unset($data['id']);
 237  
 238          if (!$event = \core\event\base::restore($data, $extra)) {
 239              return null;
 240          }
 241  
 242          return $event;
 243      }
 244  
 245      /**
 246       * Get number of events present for the given select clause.
 247       *
 248       * @param string $selectwhere select conditions.
 249       * @param array $params params.
 250       *
 251       * @return int Number of events available for the given conditions
 252       */
 253      public function get_events_select_count($selectwhere, array $params) {
 254          if (!$this->init()) {
 255              return 0;
 256          }
 257  
 258          if (!$dbtable = $this->get_config('dbtable')) {
 259              return 0;
 260          }
 261  
 262          return $this->extdb->count_records_select($dbtable, $selectwhere, $params);
 263      }
 264  
 265      /**
 266       * Get a config value for the store.
 267       *
 268       * @param string $name Config name
 269       * @param mixed $default default value
 270       * @return mixed config value if set, else the default value.
 271       */
 272      public function get_config_value($name, $default = null) {
 273          return $this->get_config($name, $default);
 274      }
 275  
 276      /**
 277       * Get the external database object.
 278       *
 279       * @return \moodle_database $extdb
 280       */
 281      public function get_extdb() {
 282          if (!$this->init()) {
 283              return false;
 284          }
 285  
 286          return $this->extdb;
 287      }
 288  
 289      /**
 290       * Are the new events appearing in the reader?
 291       *
 292       * @return bool true means new log events are being added, false means no new data will be added
 293       */
 294      public function is_logging() {
 295          if (!$this->init()) {
 296              return false;
 297          }
 298          return true;
 299      }
 300  
 301      /**
 302       * Dispose off database connection after pushing any buffered events to the database.
 303       */
 304      public function dispose() {
 305          $this->helper_dispose();
 306          if ($this->extdb) {
 307              $this->extdb->dispose();
 308          }
 309          $this->extdb = null;
 310      }
 311  }