source: moodle/trunk/fuentes/admin/tool/log/store/legacy/classes/log/store.php @ 136

Last change on this file since 136 was 136, checked in by mabarracus, 4 years ago

Ported code to xenial

File size: 12.3 KB
Line 
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.
19 *
20 * @package    logstore_legacy
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
25namespace logstore_legacy\log;
26
27defined('MOODLE_INTERNAL') || die();
28
29class store implements \tool_log\log\store, \core\log\sql_select_reader {
30    use \tool_log\helper\store,
31        \tool_log\helper\reader;
32
33    public function __construct(\tool_log\log\manager $manager) {
34        $this->helper_setup($manager);
35    }
36
37    /** @var array list of db fields which needs to be replaced for legacy log query */
38    protected static $standardtolegacyfields = array(
39        'timecreated'       => 'time',
40        'courseid'          => 'course',
41        'contextinstanceid' => 'cmid',
42        'origin'            => 'ip',
43        'anonymous'         => 0,
44    );
45
46    /** @var string Regex to replace the crud params */
47    const CRUD_REGEX = "/(crud).*?(<>|=|!=).*?'(.*?)'/s";
48
49    /**
50     * This method contains mapping required for Moodle core to make legacy store compatible with other sql_select_reader based
51     * queries.
52     *
53     * @param string $selectwhere Select statment
54     * @param array $params params for the sql
55     * @param string $sort sort fields
56     *
57     * @return array returns an array containing the sql predicate, an array of params and sorting parameter.
58     */
59    protected static function replace_sql_legacy($selectwhere, array $params, $sort = '') {
60        // Following mapping is done to make can_delete_course() compatible with legacy store.
61        if ($selectwhere == "userid = :userid AND courseid = :courseid AND eventname = :eventname AND timecreated > :since" and
62                empty($sort)) {
63            $replace = "module = 'course' AND action = 'new' AND userid = :userid AND url = :url AND time > :since";
64            $params += array('url' => "view.php?id={$params['courseid']}");
65            return array($replace, $params, $sort);
66        }
67
68        // Replace db field names to make it compatible with legacy log.
69        foreach (self::$standardtolegacyfields as $from => $to) {
70            $selectwhere = str_replace($from, $to, $selectwhere);
71            if (!empty($sort)) {
72                $sort = str_replace($from, $to, $sort);
73            }
74            if (isset($params[$from])) {
75                $params[$to] = $params[$from];
76                unset($params[$from]);
77            }
78        }
79
80        // Replace crud fields.
81        $selectwhere = preg_replace_callback("/(crud).*?(<>|=|!=).*?'(.*?)'/s", 'self::replace_crud', $selectwhere);
82
83        return array($selectwhere, $params, $sort);
84    }
85
86    public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
87        global $DB;
88
89        $sort = self::tweak_sort_by_id($sort);
90
91        // Replace the query with hardcoded mappings required for core.
92        list($selectwhere, $params, $sort) = self::replace_sql_legacy($selectwhere, $params, $sort);
93
94        $events = array();
95        $records = array();
96
97        try {
98            $records = $DB->get_records_select('log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
99        } catch (\moodle_exception $ex) {
100            debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
101        }
102
103        foreach ($records as $data) {
104            $events[$data->id] = \logstore_legacy\event\legacy_logged::restore_legacy($data);
105        }
106
107        return $events;
108    }
109
110    public function get_events_select_count($selectwhere, array $params) {
111        global $DB;
112
113        // Replace the query with hardcoded mappings required for core.
114        list($selectwhere, $params) = self::replace_sql_legacy($selectwhere, $params);
115
116        try {
117            return $DB->count_records_select('log', $selectwhere, $params);
118        } catch (\moodle_exception $ex) {
119            debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
120            return 0;
121        }
122    }
123
124    /**
125     * Are the new events appearing in the reader?
126     *
127     * @return bool true means new log events are being added, false means no new data will be added
128     */
129    public function is_logging() {
130        return (bool)$this->get_config('loglegacy', true);
131    }
132
133    public function dispose() {
134    }
135
136    /**
137     * Legacy add_to_log() code.
138     *
139     * @param    int $courseid The course id
140     * @param    string $module The module name  e.g. forum, journal, resource, course, user etc
141     * @param    string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify.
142     * @param    string $url The file and parameters used to see the results of the action
143     * @param    string $info Additional description information
144     * @param    int $cm The course_module->id if there is one
145     * @param    int|\stdClass $user If log regards $user other than $USER
146     */
147    public function legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user) {
148        // Note that this function intentionally does not follow the normal Moodle DB access idioms.
149        // This is for a good reason: it is the most frequently used DB update function,
150        // so it has been optimised for speed.
151        global $DB, $CFG, $USER;
152        if (!$this->is_logging()) {
153            return;
154        }
155
156        if ($cm === '' || is_null($cm)) { // Postgres won't translate empty string to its default.
157            $cm = 0;
158        }
159
160        if ($user) {
161            $userid = $user;
162        } else {
163            if (\core\session\manager::is_loggedinas()) { // Don't log.
164                return;
165            }
166            $userid = empty($USER->id) ? '0' : $USER->id;
167        }
168
169        if (isset($CFG->logguests) and !$CFG->logguests) {
170            if (!$userid or isguestuser($userid)) {
171                return;
172            }
173        }
174
175        $remoteaddr = getremoteaddr();
176
177        $timenow = time();
178        if (!empty($url)) { // Could break doing html_entity_decode on an empty var.
179            $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
180        } else {
181            $url = '';
182        }
183
184        // Restrict length of log lines to the space actually available in the
185        // database so that it doesn't cause a DB error. Log a warning so that
186        // developers can avoid doing things which are likely to cause this on a
187        // routine basis.
188        if (\core_text::strlen($action) > 40) {
189            $action = \core_text::substr($action, 0, 37) . '...';
190            debugging('Warning: logged very long action', DEBUG_DEVELOPER);
191        }
192
193        if (!empty($info) && \core_text::strlen($info) > 255) {
194            $info = \core_text::substr($info, 0, 252) . '...';
195            debugging('Warning: logged very long info', DEBUG_DEVELOPER);
196        }
197
198        // If the 100 field size is changed, also need to alter print_log in course/lib.php.
199        if (!empty($url) && \core_text::strlen($url) > 100) {
200            $url = \core_text::substr($url, 0, 97) . '...';
201            debugging('Warning: logged very long URL', DEBUG_DEVELOPER);
202        }
203
204        if (defined('MDL_PERFDB')) {
205            global $PERF;
206            $PERF->logwrites++;
207        };
208
209        $log = array('time' => $timenow, 'userid' => $userid, 'course' => $courseid, 'ip' => $remoteaddr,
210                     'module' => $module, 'cmid' => $cm, 'action' => $action, 'url' => $url, 'info' => $info);
211
212        try {
213            $DB->insert_record_raw('log', $log, false);
214        } catch (\dml_exception $e) {
215            debugging('Error: Could not insert a new entry to the Moodle log. ' . $e->errorcode, DEBUG_ALL);
216
217            // MDL-11893, alert $CFG->supportemail if insert into log failed.
218            if ($CFG->supportemail and empty($CFG->noemailever)) {
219                // Function email_to_user is not usable because email_to_user tries to write to the logs table,
220                // and this will get caught in an infinite loop, if disk is full.
221                $site = get_site();
222                $subject = 'Insert into log failed at your moodle site ' . $site->fullname;
223                $message = "Insert into log table failed at " . date('l dS \of F Y h:i:s A') .
224                    ".\n It is possible that your disk is full.\n\n";
225                $message .= "The failed query parameters are:\n\n" . var_export($log, true);
226
227                $lasttime = get_config('admin', 'lastloginserterrormail');
228                if (empty($lasttime) || time() - $lasttime > 60 * 60 * 24) { // Limit to 1 email per day.
229                    // Using email directly rather than messaging as they may not be able to log in to access a message.
230                    mail($CFG->supportemail, $subject, $message);
231                    set_config('lastloginserterrormail', time(), 'admin');
232                }
233            }
234        }
235    }
236
237    /**
238     * Generate a replace string for crud related sql conditions. This function is called as callback to preg_replace_callback()
239     * on the actual sql.
240     *
241     * @param array $match matched string for the passed pattern
242     *
243     * @return string The sql string to use instead of original
244     */
245    protected static function replace_crud($match) {
246        $return = '';
247        unset($match[0]); // The first entry is the whole string.
248        foreach ($match as $m) {
249            // We hard code LIKE here because we are not worried about case sensitivity and want this to be fast.
250            switch ($m) {
251                case 'crud' :
252                    $replace = 'action';
253                    break;
254                case 'c' :
255                    switch ($match[2]) {
256                        case '=' :
257                            $replace = " LIKE '%add%'";
258                            break;
259                        case '!=' :
260                        case '<>' :
261                            $replace = " NOT LIKE '%add%'";
262                            break;
263                        default:
264                            $replace = '';
265                    }
266                    break;
267                case 'r' :
268                    switch ($match[2]) {
269                        case '=' :
270                            $replace = " LIKE '%view%' OR action LIKE '%report%'";
271                            break;
272                        case '!=' :
273                        case '<>' :
274                            $replace = " NOT LIKE '%view%' AND action NOT LIKE '%report%'";
275                            break;
276                        default:
277                            $replace = '';
278                    }
279                    break;
280                case 'u' :
281                    switch ($match[2]) {
282                        case '=' :
283                            $replace = " LIKE '%update%'";
284                            break;
285                        case '!=' :
286                        case '<>' :
287                            $replace = " NOT LIKE '%update%'";
288                            break;
289                        default:
290                            $replace = '';
291                    }
292                    break;
293                case 'd' :
294                    switch ($match[2]) {
295                        case '=' :
296                            $replace = " LIKE '%delete%'";
297                            break;
298                        case '!=' :
299                        case '<>' :
300                            $replace = " NOT LIKE '%delete%'";
301                            break;
302                        default:
303                            $replace = '';
304                    }
305                    break;
306                default :
307                    $replace = '';
308            }
309            $return .= $replace;
310        }
311        return $return;
312    }
313}
Note: See TracBrowser for help on using the repository browser.