MDL-58651 logstore_database: Add ability to not send database options
[moodle.git] / admin / tool / log / store / database / classes / log / store.php
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/>.
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  */
25 namespace logstore_database\log;
26 defined('MOODLE_INTERNAL') || die();
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     }
35     /** @var \moodle_database $extdb */
36     protected $extdb;
38     /** @var bool $logguests true if logging guest access */
39     protected $logguests;
41     /** @var array $includelevels An array of education levels to include */
42     protected $includelevels = array();
44     /** @var array $includeactions An array of actions types to include */
45     protected $includeactions = array();
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     }
62     /**
63      * Setup the Database.
64      *
65      * @return bool
66      */
67     protected function init() {
68         if (isset($this->extdb)) {
69             return !empty($this->extdb);
70         }
72         $dbdriver = $this->get_config('dbdriver');
73         if (empty($dbdriver)) {
74             $this->extdb = false;
75             return false;
76         }
77         list($dblibrary, $dbtype) = explode('/', $dbdriver);
79         if (!$db = \moodle_database::get_driver_instance($dbtype, $dblibrary, true)) {
80             debugging("Unknown driver $dblibrary/$dbtype", DEBUG_DEVELOPER);
81             $this->extdb = false;
82             return false;
83         }
85         $dboptions = array();
86         $dboptions['dbpersist'] = $this->get_config('dbpersist', '0');
87         $dboptions['dbsocket'] = $this->get_config('dbsocket', '');
88         $dboptions['dbport'] = $this->get_config('dbport', '');
89         $dboptions['dbschema'] = $this->get_config('dbschema', '');
90         $dboptions['dbcollation'] = $this->get_config('dbcollation', '');
91         $dboptions['dbhandlesoptions'] = $this->get_config('dbhandlesoptions', false);
92         try {
93             $db->connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'),
94                 $this->get_config('dbname'), false, $dboptions);
95             $tables = $db->get_tables();
96             if (!in_array($this->get_config('dbtable'), $tables)) {
97                 debugging('Cannot find the specified table', DEBUG_DEVELOPER);
98                 $this->extdb = false;
99                 return false;
100             }
101         } catch (\moodle_exception $e) {
102             debugging('Cannot connect to external database: ' . $e->getMessage(), DEBUG_DEVELOPER);
103             $this->extdb = false;
104             return false;
105         }
107         $this->extdb = $db;
108         return true;
109     }
111     /**
112      * Should the event be ignored (== not logged)?
113      * @param \core\event\base $event
114      * @return bool
115      */
116     protected function is_event_ignored(\core\event\base $event) {
117         if (!in_array($event->crud, $this->includeactions) &&
118             !in_array($event->edulevel, $this->includelevels)
119         ) {
120             // Ignore event if the store settings do not want to store it.
121             return true;
122         }
123         if ((!CLI_SCRIPT or PHPUNIT_TEST) and !$this->logguests) {
124             // Always log inside CLI scripts because we do not login there.
125             if (!isloggedin() or isguestuser()) {
126                 return true;
127             }
128         }
129         return false;
130     }
132     /**
133      * Insert events in bulk to the database.
134      *
135      * @param array $evententries raw event data
136      */
137     protected function insert_event_entries($evententries) {
138         if (!$this->init()) {
139             return;
140         }
141         if (!$dbtable = $this->get_config('dbtable')) {
142             return;
143         }
144         try {
145             $this->extdb->insert_records($dbtable, $evententries);
146         } catch (\moodle_exception $e) {
147             debugging('Cannot write to external database: ' . $e->getMessage(), DEBUG_DEVELOPER);
148         }
149     }
151     /**
152      * Get an array of events based on the passed on params.
153      *
154      * @param string $selectwhere select conditions.
155      * @param array $params params.
156      * @param string $sort sortorder.
157      * @param int $limitfrom limit constraints.
158      * @param int $limitnum limit constraints.
159      *
160      * @return array|\core\event\base[] array of events.
161      */
162     public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
163         if (!$this->init()) {
164             return array();
165         }
167         if (!$dbtable = $this->get_config('dbtable')) {
168             return array();
169         }
171         $sort = self::tweak_sort_by_id($sort);
173         $events = array();
174         $records = $this->extdb->get_records_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
176         foreach ($records as $data) {
177             if ($event = $this->get_log_event($data)) {
178                 $events[$data->id] = $event;
179             }
180         }
182         return $events;
183     }
185     /**
186      * Fetch records using given criteria returning a Traversable object.
187      *
188      * Note that the traversable object contains a moodle_recordset, so
189      * remember that is important that you call close() once you finish
190      * using it.
191      *
192      * @param string $selectwhere
193      * @param array $params
194      * @param string $sort
195      * @param int $limitfrom
196      * @param int $limitnum
197      * @return \core\dml\recordset_walk|\core\event\base[]
198      */
199     public function get_events_select_iterator($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
200         if (!$this->init()) {
201             return array();
202         }
204         if (!$dbtable = $this->get_config('dbtable')) {
205             return array();
206         }
208         $sort = self::tweak_sort_by_id($sort);
210         $recordset = $this->extdb->get_recordset_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
212         return new \core\dml\recordset_walk($recordset, array($this, 'get_log_event'));
213     }
215     /**
216      * Returns an event from the log data.
217      *
218      * @param stdClass $data Log data
219      * @return \core\event\base
220      */
221     public function get_log_event($data) {
223         $extra = array('origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid);
224         $data = (array)$data;
225         $id = $data['id'];
226         $data['other'] = unserialize($data['other']);
227         if ($data['other'] === false) {
228             $data['other'] = array();
229         }
230         unset($data['origin']);
231         unset($data['ip']);
232         unset($data['realuserid']);
233         unset($data['id']);
235         if (!$event = \core\event\base::restore($data, $extra)) {
236             return null;
237         }
239         return $event;
240     }
242     /**
243      * Get number of events present for the given select clause.
244      *
245      * @param string $selectwhere select conditions.
246      * @param array $params params.
247      *
248      * @return int Number of events available for the given conditions
249      */
250     public function get_events_select_count($selectwhere, array $params) {
251         if (!$this->init()) {
252             return 0;
253         }
255         if (!$dbtable = $this->get_config('dbtable')) {
256             return 0;
257         }
259         return $this->extdb->count_records_select($dbtable, $selectwhere, $params);
260     }
262     /**
263      * Get a config value for the store.
264      *
265      * @param string $name Config name
266      * @param mixed $default default value
267      * @return mixed config value if set, else the default value.
268      */
269     public function get_config_value($name, $default = null) {
270         return $this->get_config($name, $default);
271     }
273     /**
274      * Get the external database object.
275      *
276      * @return \moodle_database $extdb
277      */
278     public function get_extdb() {
279         if (!$this->init()) {
280             return false;
281         }
283         return $this->extdb;
284     }
286     /**
287      * Are the new events appearing in the reader?
288      *
289      * @return bool true means new log events are being added, false means no new data will be added
290      */
291     public function is_logging() {
292         if (!$this->init()) {
293             return false;
294         }
295         return true;
296     }
298     /**
299      * Dispose off database connection after pushing any buffered events to the database.
300      */
301     public function dispose() {
302         $this->helper_dispose();
303         if ($this->extdb) {
304             $this->extdb->dispose();
305         }
306         $this->extdb = null;
307     }