MDL-69521 core: Move all comments in code from 4.0 to 3.10
[moodle.git] / admin / tool / log / store / legacy / classes / log / store.php
CommitLineData
7eaca5a8
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 * Legacy log reader.
28d0d12f 19 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 20 * @todo MDL-52805 This is to be removed in Moodle 3.10
7eaca5a8
21 *
22 * @package logstore_legacy
23 * @copyright 2013 Petr Skoda {@link http://skodak.org}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27namespace logstore_legacy\log;
28
29defined('MOODLE_INTERNAL') || die();
30
1cfce08e 31class store implements \tool_log\log\store, \core\log\sql_reader {
ae077051
AA
32 use \tool_log\helper\store,
33 \tool_log\helper\reader;
34
28d0d12f
AG
35 /**
36 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 37 * @todo MDL-52805 This is to be removed in Moodle 3.10
28d0d12f
AG
38 *
39 * @param \tool_log\log\manager $manager
40 */
ae077051
AA
41 public function __construct(\tool_log\log\manager $manager) {
42 $this->helper_setup($manager);
43 }
0852f9c6 44
b1e7be73 45 /** @var array list of db fields which needs to be replaced for legacy log query */
e1b2a0b4 46 protected static $standardtolegacyfields = array(
0852f9c6
47 'timecreated' => 'time',
48 'courseid' => 'course',
49 'contextinstanceid' => 'cmid',
454c7b5d
50 'origin' => 'ip',
51 'anonymous' => 0,
0852f9c6 52 );
b1e7be73 53
dc065005
AA
54 /** @var string Regex to replace the crud params */
55 const CRUD_REGEX = "/(crud).*?(<>|=|!=).*?'(.*?)'/s";
e1b2a0b4 56
536c0865 57 /**
1cfce08e 58 * This method contains mapping required for Moodle core to make legacy store compatible with other sql_reader based
536c0865
AA
59 * queries.
60 *
e1b2a0b4 61 * @param string $selectwhere Select statment
536c0865 62 * @param array $params params for the sql
e1b2a0b4 63 * @param string $sort sort fields
536c0865 64 *
e1b2a0b4 65 * @return array returns an array containing the sql predicate, an array of params and sorting parameter.
536c0865 66 */
e1b2a0b4
AA
67 protected static function replace_sql_legacy($selectwhere, array $params, $sort = '') {
68 // Following mapping is done to make can_delete_course() compatible with legacy store.
69 if ($selectwhere == "userid = :userid AND courseid = :courseid AND eventname = :eventname AND timecreated > :since" and
70 empty($sort)) {
536c0865
AA
71 $replace = "module = 'course' AND action = 'new' AND userid = :userid AND url = :url AND time > :since";
72 $params += array('url' => "view.php?id={$params['courseid']}");
e1b2a0b4 73 return array($replace, $params, $sort);
536c0865 74 }
7eaca5a8 75
b1e7be73 76 // Replace db field names to make it compatible with legacy log.
e1b2a0b4 77 foreach (self::$standardtolegacyfields as $from => $to) {
b1e7be73 78 $selectwhere = str_replace($from, $to, $selectwhere);
e1b2a0b4
AA
79 if (!empty($sort)) {
80 $sort = str_replace($from, $to, $sort);
81 }
b1e7be73
RT
82 if (isset($params[$from])) {
83 $params[$to] = $params[$from];
84 unset($params[$from]);
85 }
c154abd0 86 }
7eaca5a8 87
e1b2a0b4
AA
88 // Replace crud fields.
89 $selectwhere = preg_replace_callback("/(crud).*?(<>|=|!=).*?'(.*?)'/s", 'self::replace_crud', $selectwhere);
90
91 return array($selectwhere, $params, $sort);
92 }
93
28d0d12f
AG
94 /**
95 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 96 * @todo MDL-52805 This will be removed in Moodle 3.10
28d0d12f
AG
97 *
98 * @param string $selectwhere
99 * @param array $params
100 * @param string $sort
101 * @param int $limitfrom
102 * @param int $limitnum
103 * @return array
104 */
e1b2a0b4
AA
105 public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
106 global $DB;
107
a8d7d617
PS
108 $sort = self::tweak_sort_by_id($sort);
109
e1b2a0b4
AA
110 // Replace the query with hardcoded mappings required for core.
111 list($selectwhere, $params, $sort) = self::replace_sql_legacy($selectwhere, $params, $sort);
112
c154abd0 113 $records = array();
7eaca5a8
114
115 try {
7df0d6a7 116 // A custom report + on the fly SQL rewriting = a possible exception.
b175341e 117 $records = $DB->get_recordset_select('log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
7eaca5a8 118 } catch (\moodle_exception $ex) {
0852f9c6 119 debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
7df0d6a7 120 return array();
7eaca5a8
121 }
122
7df0d6a7
AD
123 $events = array();
124
7eaca5a8 125 foreach ($records as $data) {
1cfce08e 126 $events[$data->id] = $this->get_log_event($data);
7eaca5a8
127 }
128
b175341e
TB
129 $records->close();
130
7eaca5a8
131 return $events;
132 }
133
1cfce08e
DM
134 /**
135 * Fetch records using given criteria returning a Traversable object.
28d0d12f 136 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 137 * @todo MDL-52805 This will be removed in Moodle 3.10
1cfce08e
DM
138 *
139 * Note that the traversable object contains a moodle_recordset, so
140 * remember that is important that you call close() once you finish
141 * using it.
142 *
143 * @param string $selectwhere
144 * @param array $params
145 * @param string $sort
146 * @param int $limitfrom
147 * @param int $limitnum
148 * @return \Traversable|\core\event\base[]
149 */
150 public function get_events_select_iterator($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
151 global $DB;
152
153 $sort = self::tweak_sort_by_id($sort);
154
155 // Replace the query with hardcoded mappings required for core.
156 list($selectwhere, $params, $sort) = self::replace_sql_legacy($selectwhere, $params, $sort);
157
158 try {
159 $recordset = $DB->get_recordset_select('log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
160 } catch (\moodle_exception $ex) {
161 debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
162 return new \EmptyIterator;
163 }
164
165 return new \core\dml\recordset_walk($recordset, array($this, 'get_log_event'));
166 }
167
168 /**
169 * Returns an event from the log data.
28d0d12f 170 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 171 * @todo MDL-52805 This will be removed in Moodle 3.10
1cfce08e
DM
172 *
173 * @param stdClass $data Log data
174 * @return \core\event\base
175 */
176 public function get_log_event($data) {
177 return \logstore_legacy\event\legacy_logged::restore_legacy($data);
178 }
179
28d0d12f
AG
180 /**
181 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 182 * @todo MDL-52805 This will be removed in Moodle 3.10
28d0d12f
AG
183 *
184 * @param string $selectwhere
185 * @param array $params
186 * @return int
187 */
993d8d83 188 public function get_events_select_count($selectwhere, array $params) {
7eaca5a8 189 global $DB;
c154abd0 190
e1b2a0b4
AA
191 // Replace the query with hardcoded mappings required for core.
192 list($selectwhere, $params) = self::replace_sql_legacy($selectwhere, $params);
c154abd0 193
7eaca5a8
194 try {
195 return $DB->count_records_select('log', $selectwhere, $params);
196 } catch (\moodle_exception $ex) {
0852f9c6 197 debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
7eaca5a8
198 return 0;
199 }
200 }
201
bdae738e
202 /**
203 * Are the new events appearing in the reader?
28d0d12f 204 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 205 * @todo MDL-52805 This will be removed in Moodle 3.10
bdae738e
206 *
207 * @return bool true means new log events are being added, false means no new data will be added
208 */
209 public function is_logging() {
210 return (bool)$this->get_config('loglegacy', true);
211 }
212
28d0d12f
AG
213 /**
214 * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
709b46db 215 * @todo MDL-52805 This will be removed in Moodle 3.10
28d0d12f 216 */
7eaca5a8
217 public function dispose() {
218 }
219
220 /**
221 * Legacy add_to_log() code.
61f7b10b
AG
222 * @deprecated since Moodle 3.1 MDL-45104 - Please use supported log stores such as "standard" or "external" instead.
223 * @todo MDL-52805 This will be removed in Moodle 3.3
7eaca5a8 224 *
0852f9c6
225 * @param int $courseid The course id
226 * @param string $module The module name e.g. forum, journal, resource, course, user etc
227 * @param string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify.
228 * @param string $url The file and parameters used to see the results of the action
229 * @param string $info Additional description information
230 * @param int $cm The course_module->id if there is one
7eaca5a8 231 * @param int|\stdClass $user If log regards $user other than $USER
362bc070
MN
232 * @param string $ip Override the IP, should only be used for restore.
233 * @param int $time Override the log time, should only be used for restore.
7eaca5a8 234 */
362bc070 235 public function legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user, $ip = null, $time = null) {
7eaca5a8
236 // Note that this function intentionally does not follow the normal Moodle DB access idioms.
237 // This is for a good reason: it is the most frequently used DB update function,
238 // so it has been optimised for speed.
239 global $DB, $CFG, $USER;
bdae738e 240 if (!$this->is_logging()) {
7eaca5a8
241 return;
242 }
243
244 if ($cm === '' || is_null($cm)) { // Postgres won't translate empty string to its default.
245 $cm = 0;
246 }
247
248 if ($user) {
249 $userid = $user;
250 } else {
0852f9c6 251 if (\core\session\manager::is_loggedinas()) { // Don't log.
7eaca5a8
252 return;
253 }
254 $userid = empty($USER->id) ? '0' : $USER->id;
255 }
256
257 if (isset($CFG->logguests) and !$CFG->logguests) {
258 if (!$userid or isguestuser($userid)) {
259 return;
260 }
261 }
262
362bc070 263 $remoteaddr = (is_null($ip)) ? getremoteaddr() : $ip;
7eaca5a8 264
362bc070 265 $timenow = (is_null($time)) ? time() : $time;
7eaca5a8
266 if (!empty($url)) { // Could break doing html_entity_decode on an empty var.
267 $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
268 } else {
269 $url = '';
270 }
271
272 // Restrict length of log lines to the space actually available in the
273 // database so that it doesn't cause a DB error. Log a warning so that
274 // developers can avoid doing things which are likely to cause this on a
275 // routine basis.
a4496bdd
JL
276 if (\core_text::strlen($action) > 40) {
277 $action = \core_text::substr($action, 0, 37) . '...';
278 debugging('Warning: logged very long action', DEBUG_DEVELOPER);
279 }
280
0852f9c6
281 if (!empty($info) && \core_text::strlen($info) > 255) {
282 $info = \core_text::substr($info, 0, 252) . '...';
283 debugging('Warning: logged very long info', DEBUG_DEVELOPER);
7eaca5a8
284 }
285
286 // If the 100 field size is changed, also need to alter print_log in course/lib.php.
0852f9c6
287 if (!empty($url) && \core_text::strlen($url) > 100) {
288 $url = \core_text::substr($url, 0, 97) . '...';
289 debugging('Warning: logged very long URL', DEBUG_DEVELOPER);
7eaca5a8
290 }
291
0852f9c6
292 if (defined('MDL_PERFDB')) {
293 global $PERF;
294 $PERF->logwrites++;
295 };
7eaca5a8 296
0852f9c6
297 $log = array('time' => $timenow, 'userid' => $userid, 'course' => $courseid, 'ip' => $remoteaddr,
298 'module' => $module, 'cmid' => $cm, 'action' => $action, 'url' => $url, 'info' => $info);
7eaca5a8
299
300 try {
301 $DB->insert_record_raw('log', $log, false);
302 } catch (\dml_exception $e) {
0852f9c6 303 debugging('Error: Could not insert a new entry to the Moodle log. ' . $e->errorcode, DEBUG_ALL);
7eaca5a8
304
305 // MDL-11893, alert $CFG->supportemail if insert into log failed.
306 if ($CFG->supportemail and empty($CFG->noemailever)) {
307 // Function email_to_user is not usable because email_to_user tries to write to the logs table,
308 // and this will get caught in an infinite loop, if disk is full.
309 $site = get_site();
0852f9c6
310 $subject = 'Insert into log failed at your moodle site ' . $site->fullname;
311 $message = "Insert into log table failed at " . date('l dS \of F Y h:i:s A') .
312 ".\n It is possible that your disk is full.\n\n";
7eaca5a8
313 $message .= "The failed query parameters are:\n\n" . var_export($log, true);
314
315 $lasttime = get_config('admin', 'lastloginserterrormail');
0852f9c6 316 if (empty($lasttime) || time() - $lasttime > 60 * 60 * 24) { // Limit to 1 email per day.
7eaca5a8
317 // Using email directly rather than messaging as they may not be able to log in to access a message.
318 mail($CFG->supportemail, $subject, $message);
319 set_config('lastloginserterrormail', time(), 'admin');
320 }
321 }
322 }
323 }
dc065005
AA
324
325 /**
326 * Generate a replace string for crud related sql conditions. This function is called as callback to preg_replace_callback()
327 * on the actual sql.
328 *
329 * @param array $match matched string for the passed pattern
330 *
331 * @return string The sql string to use instead of original
332 */
333 protected static function replace_crud($match) {
334 $return = '';
335 unset($match[0]); // The first entry is the whole string.
336 foreach ($match as $m) {
25239798 337 // We hard code LIKE here because we are not worried about case sensitivity and want this to be fast.
dc065005
AA
338 switch ($m) {
339 case 'crud' :
340 $replace = 'action';
341 break;
342 case 'c' :
343 switch ($match[2]) {
344 case '=' :
345 $replace = " LIKE '%add%'";
346 break;
347 case '!=' :
348 case '<>' :
349 $replace = " NOT LIKE '%add%'";
350 break;
351 default:
352 $replace = '';
353 }
354 break;
355 case 'r' :
356 switch ($match[2]) {
357 case '=' :
358 $replace = " LIKE '%view%' OR action LIKE '%report%'";
359 break;
360 case '!=' :
361 case '<>' :
362 $replace = " NOT LIKE '%view%' AND action NOT LIKE '%report%'";
363 break;
364 default:
365 $replace = '';
366 }
367 break;
368 case 'u' :
369 switch ($match[2]) {
370 case '=' :
371 $replace = " LIKE '%update%'";
372 break;
373 case '!=' :
374 case '<>' :
375 $replace = " NOT LIKE '%update%'";
376 break;
377 default:
378 $replace = '';
379 }
380 break;
381 case 'd' :
382 switch ($match[2]) {
383 case '=' :
384 $replace = " LIKE '%delete%'";
385 break;
386 case '!=' :
387 case '<>' :
388 $replace = " NOT LIKE '%delete%'";
389 break;
390 default:
391 $replace = '';
392 }
393 break;
394 default :
395 $replace = '';
396 }
397 $return .= $replace;
398 }
399 return $return;
400 }
7eaca5a8 401}