| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | /**
 | 
        
           |  |  | 18 |  * 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 whether events are present for the given select clause.
 | 
        
           |  |  | 267 |      *
 | 
        
           |  |  | 268 |      * @param string $selectwhere select conditions.
 | 
        
           |  |  | 269 |      * @param array $params params.
 | 
        
           |  |  | 270 |      *
 | 
        
           |  |  | 271 |      * @return bool Whether events available for the given conditions
 | 
        
           |  |  | 272 |      */
 | 
        
           |  |  | 273 |     public function get_events_select_exists(string $selectwhere, array $params): bool {
 | 
        
           |  |  | 274 |         if (!$this->init()) {
 | 
        
           |  |  | 275 |             return false;
 | 
        
           |  |  | 276 |         }
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |         if (!$dbtable = $this->get_config('dbtable')) {
 | 
        
           |  |  | 279 |             return false;
 | 
        
           |  |  | 280 |         }
 | 
        
           |  |  | 281 |   | 
        
           |  |  | 282 |         return $this->extdb->record_exists_select($dbtable, $selectwhere, $params);
 | 
        
           |  |  | 283 |     }
 | 
        
           |  |  | 284 |   | 
        
           |  |  | 285 |     /**
 | 
        
           |  |  | 286 |      * Get a config value for the store.
 | 
        
           |  |  | 287 |      *
 | 
        
           |  |  | 288 |      * @param string $name Config name
 | 
        
           |  |  | 289 |      * @param mixed $default default value
 | 
        
           |  |  | 290 |      * @return mixed config value if set, else the default value.
 | 
        
           |  |  | 291 |      */
 | 
        
           |  |  | 292 |     public function get_config_value($name, $default = null) {
 | 
        
           |  |  | 293 |         return $this->get_config($name, $default);
 | 
        
           |  |  | 294 |     }
 | 
        
           |  |  | 295 |   | 
        
           |  |  | 296 |     /**
 | 
        
           |  |  | 297 |      * Get the external database object.
 | 
        
           |  |  | 298 |      *
 | 
        
           |  |  | 299 |      * @return \moodle_database $extdb
 | 
        
           |  |  | 300 |      */
 | 
        
           |  |  | 301 |     public function get_extdb() {
 | 
        
           |  |  | 302 |         if (!$this->init()) {
 | 
        
           |  |  | 303 |             return false;
 | 
        
           |  |  | 304 |         }
 | 
        
           |  |  | 305 |   | 
        
           |  |  | 306 |         return $this->extdb;
 | 
        
           |  |  | 307 |     }
 | 
        
           |  |  | 308 |   | 
        
           |  |  | 309 |     /**
 | 
        
           |  |  | 310 |      * Are the new events appearing in the reader?
 | 
        
           |  |  | 311 |      *
 | 
        
           |  |  | 312 |      * @return bool true means new log events are being added, false means no new data will be added
 | 
        
           |  |  | 313 |      */
 | 
        
           |  |  | 314 |     public function is_logging() {
 | 
        
           |  |  | 315 |         if (!$this->init()) {
 | 
        
           |  |  | 316 |             return false;
 | 
        
           |  |  | 317 |         }
 | 
        
           |  |  | 318 |         return true;
 | 
        
           |  |  | 319 |     }
 | 
        
           |  |  | 320 |   | 
        
           |  |  | 321 |     /**
 | 
        
           |  |  | 322 |      * Dispose off database connection after pushing any buffered events to the database.
 | 
        
           |  |  | 323 |      */
 | 
        
           |  |  | 324 |     public function dispose() {
 | 
        
           |  |  | 325 |         $this->helper_dispose();
 | 
        
           |  |  | 326 |         if ($this->extdb) {
 | 
        
           |  |  | 327 |             $this->extdb->dispose();
 | 
        
           |  |  | 328 |         }
 | 
        
           |  |  | 329 |         $this->extdb = null;
 | 
        
           |  |  | 330 |     }
 | 
        
           |  |  | 331 | }
 |