MDL-50408 tool_log: Extra deprecation tip
[moodle.git] / admin / tool / log / classes / log / manager.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  * Log store manager.
19  *
20  * @package    tool_log
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 tool_log\log;
27 defined('MOODLE_INTERNAL') || die();
29 class manager implements \core\log\manager {
30     /** @var \core\log\reader[] $readers list of initialised log readers */
31     protected $readers;
33     /** @var \tool_log\log\writer[] $writers list of initialised log writers */
34     protected $writers;
36     /** @var \tool_log\log\store[] $stores list of all enabled stores */
37     protected $stores;
39     /**
40      * Delayed initialisation of singleton.
41      */
42     protected function init() {
43         if (isset($this->stores)) {
44             // Do not bother checking readers and writers here
45             // because everything is init here.
46             return;
47         }
48         $this->stores = array();
49         $this->readers = array();
50         $this->writers = array();
52         // Register shutdown handler - this may be useful for buffering, file handle closing, etc.
53         \core_shutdown_manager::register_function(array($this, 'dispose'));
55         $plugins = get_config('tool_log', 'enabled_stores');
56         if (empty($plugins)) {
57             return;
58         }
60         $plugins = explode(',', $plugins);
61         foreach ($plugins as $plugin) {
62             $classname = "\\$plugin\\log\\store";
63             if (class_exists($classname)) {
64                 $store = new $classname($this);
65                 $this->stores[$plugin] = $store;
66                 if ($store instanceof \tool_log\log\writer) {
67                     $this->writers[$plugin] = $store;
68                 }
69                 if ($store instanceof \core\log\reader) {
70                     $this->readers[$plugin] = $store;
71                 }
72             }
73         }
74     }
76     /**
77      * Called from the observer only.
78      *
79      * @param \core\event\base $event
80      */
81     public function process(\core\event\base $event) {
82         $this->init();
83         foreach ($this->writers as $plugin => $writer) {
84             try {
85                 $writer->write($event, $this);
86             } catch (\Exception $e) {
87                 debugging('Exception detected when logging event ' . $event->eventname . ' in ' . $plugin . ': ' .
88                     $e->getMessage(), DEBUG_NORMAL, $e->getTrace());
89             }
90         }
91     }
93     /**
94      * Returns list of available log readers.
95      *
96      * This way the reports find out available sources of data.
97      *
98      * @param string $interface Returned stores must implement this interface.
99      *
100      * @return \core\log\reader[] list of available log data readers
101      */
102     public function get_readers($interface = null) {
103         $this->init();
104         $return = array();
105         foreach ($this->readers as $plugin => $reader) {
106             if (empty($interface) || ($reader instanceof $interface)) {
107                 $return[$plugin] = $reader;
108             }
109             // TODO MDL-49291 These conditions should be removed as part of the 2nd stage deprecation.
110             if ($reader instanceof \core\log\sql_internal_reader) {
111                 debugging('\core\log\sql_internal_reader has been deprecated in favour of \core\log\sql_internal_table_reader.' .
112                     ' Update ' . get_class($reader) . ' to use the new interface.', DEBUG_DEVELOPER);
113             } else if ($reader instanceof \core\log\sql_select_reader) {
114                 debugging('\core\log\sql_select_reader has been deprecated in favour of \core\log\sql_reader. Update ' .
115                     get_class($reader) . ' to use the new interface.', DEBUG_DEVELOPER);
116             }
117         }
119         // TODO MDL-49291 This section below (until the final return) should be removed as part of the 2nd stage deprecation.
120         $isselectreader = (ltrim($interface, '\\') === 'core\log\sql_select_reader');
121         $isinternalreader = (ltrim($interface, '\\') === 'core\log\sql_internal_reader');
122         if ($isselectreader || $isinternalreader) {
124             if ($isselectreader) {
125                 $alternative = '\core\log\sql_reader';
126             } else {
127                 $alternative = '\core\log\sql_internal_table_reader';
128             }
130             if (count($return) === 0) {
131                 // If there are no classes implementing the provided interface and the provided interface is one of
132                 // the deprecated ones, we return the non-deprecated alternatives. It should be safe as the new interface
133                 // is adding a new method but not changing the existing ones.
134                 debugging($interface . ' has been deprecated in favour of ' . $alternative . '. Returning ' . $alternative .
135                     ' instances instead. Please call get_readers() using the new interface.', DEBUG_DEVELOPER);
136                 $return = $this->get_readers($alternative);
137             } else {
138                 debugging($interface . ' has been deprecated in favour of ' . $alternative .
139                     '. Please call get_readers() using the new interface.', DEBUG_DEVELOPER);
140             }
141         }
143         return $return;
144     }
146     /**
147      * Get a list of reports that support the given store instance.
148      *
149      * @param string $logstore Name of the store.
150      *
151      * @return array List of supported reports
152      */
153     public function get_supported_reports($logstore) {
155         $allstores = self::get_store_plugins();
156         if (empty($allstores[$logstore])) {
157             // Store doesn't exist.
158             return array();
159         }
161         $reports = get_plugin_list_with_function('report', 'supports_logstore', 'lib.php');
162         $enabled = $this->stores;
164         if (empty($enabled[$logstore])) {
165             // Store is not enabled, init an instance.
166             $classname = '\\' . $logstore . '\log\store';
167             $instance = new $classname($this);
168         } else {
169             $instance = $enabled[$logstore];
170         }
172         $return = array();
173         foreach ($reports as $report => $fulldir) {
174             if (component_callback($report, 'supports_logstore', array($instance), false)) {
175                 $return[$report] = get_string('pluginname', $report);
176             }
177         }
179         return $return;
180     }
182     /**
183      * For a given report, returns a list of log stores that are supported.
184      *
185      * @param string $component component.
186      *
187      * @return false|array list of logstores that support the given report. It returns false if the given $component doesn't
188      *      require logstores.
189      */
190     public function get_supported_logstores($component) {
192         $allstores = self::get_store_plugins();
193         $enabled = $this->stores;
195         $function = component_callback_exists($component, 'supports_logstore');
196         if (!$function) {
197             // The report doesn't define the callback, most probably it doesn't need log stores.
198             return false;
199         }
201         $return = array();
202         foreach ($allstores as $store => $logclass) {
203             $instance = empty($enabled[$store]) ? new $logclass($this) : $enabled[$store];
204             if ($function($instance)) {
205                 $return[$store] = get_string('pluginname', $store);
206             }
207         }
208         return $return;
209     }
211     /**
212      * Intended for store management, do not use from reports.
213      *
214      * @return store[] Returns list of available store plugins.
215      */
216     public static function get_store_plugins() {
217         return \core_component::get_plugin_list_with_class('logstore', 'log\store');
218     }
220     /**
221      * Usually called automatically from shutdown manager,
222      * this allows us to implement buffering of write operations.
223      */
224     public function dispose() {
225         if ($this->stores) {
226             foreach ($this->stores as $store) {
227                 $store->dispose();
228             }
229         }
230         $this->stores = null;
231         $this->readers = null;
232         $this->writers = null;
233     }
235     /**
236      * Legacy add_to_log() redirection.
237      *
238      * To be used only from deprecated add_to_log() function and event trigger() method.
239      *
240      * NOTE: this is hardcoded to legacy log store plugin, hopefully we can get rid of it soon.
241      *
242      * @param int $courseid The course id
243      * @param string $module The module name  e.g. forum, journal, resource, course, user etc
244      * @param string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify
245      * @param string $url The file and parameters used to see the results of the action
246      * @param string $info Additional description information
247      * @param int $cm The course_module->id if there is one
248      * @param int|\stdClass $user If log regards $user other than $USER
249      * @param string $ip Override the IP, should only be used for restore.
250      * @param int $time Override the log time, should only be used for restore.
251      */
252     public function legacy_add_to_log($courseid, $module, $action, $url = '', $info = '',
253                                       $cm = 0, $user = 0, $ip = null, $time = null) {
254         $this->init();
255         if (isset($this->stores['logstore_legacy'])) {
256             $this->stores['logstore_legacy']->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user, $ip, $time);
257         }
258     }