PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/admin/tool/log/store/legacy/classes/log/store.php

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