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 | * Observer class containing methods monitoring various events. |
---|
19 | * |
---|
20 | * @package tool_monitor |
---|
21 | * @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com> |
---|
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
---|
23 | */ |
---|
24 | |
---|
25 | namespace tool_monitor; |
---|
26 | |
---|
27 | defined('MOODLE_INTERNAL') || die(); |
---|
28 | |
---|
29 | /** |
---|
30 | * Observer class containing methods monitoring various events. |
---|
31 | * |
---|
32 | * @since Moodle 2.8 |
---|
33 | * @package tool_monitor |
---|
34 | * @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com> |
---|
35 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
---|
36 | */ |
---|
37 | class eventobservers { |
---|
38 | |
---|
39 | /** @var array $buffer buffer of events. */ |
---|
40 | protected $buffer = array(); |
---|
41 | |
---|
42 | /** @var int Number of entries in the buffer. */ |
---|
43 | protected $count = 0; |
---|
44 | |
---|
45 | /** @var eventobservers a reference to a self instance. */ |
---|
46 | protected static $instance; |
---|
47 | |
---|
48 | /** |
---|
49 | * Course delete event observer. |
---|
50 | * This observer monitors course delete event, and when a course is deleted it deletes any rules and subscriptions associated |
---|
51 | * with it, so no orphan data is left behind. |
---|
52 | * |
---|
53 | * @param \core\event\course_deleted $event The course deleted event. |
---|
54 | */ |
---|
55 | public static function course_deleted(\core\event\course_deleted $event) { |
---|
56 | $rules = rule_manager::get_rules_by_courseid($event->courseid, 0, 0, false); |
---|
57 | foreach ($rules as $rule) { |
---|
58 | rule_manager::delete_rule($rule->id, $event->get_context()); |
---|
59 | } |
---|
60 | } |
---|
61 | |
---|
62 | /** |
---|
63 | * The observer monitoring all the events. |
---|
64 | * |
---|
65 | * This observers puts small event objects in buffer for later writing to the database. At the end of the request the buffer |
---|
66 | * is cleaned up and all data dumped into the tool_monitor_events table. |
---|
67 | * |
---|
68 | * @param \core\event\base $event event object |
---|
69 | */ |
---|
70 | public static function process_event(\core\event\base $event) { |
---|
71 | if (!get_config('tool_monitor', 'enablemonitor')) { |
---|
72 | return; // The tool is disabled. Nothing to do. |
---|
73 | } |
---|
74 | |
---|
75 | if (empty(self::$instance)) { |
---|
76 | self::$instance = new static(); |
---|
77 | // Register shutdown handler - this is useful for buffering, processing events, etc. |
---|
78 | \core_shutdown_manager::register_function(array(self::$instance, 'process_buffer')); |
---|
79 | } |
---|
80 | |
---|
81 | self::$instance->buffer_event($event); |
---|
82 | |
---|
83 | if (PHPUNIT_TEST) { |
---|
84 | // Process buffer after every event when unit testing. |
---|
85 | self::$instance->process_buffer(); |
---|
86 | |
---|
87 | } |
---|
88 | } |
---|
89 | |
---|
90 | /** |
---|
91 | * Api to buffer events to store, to reduce db queries. |
---|
92 | * |
---|
93 | * @param \core\event\base $event |
---|
94 | */ |
---|
95 | protected function buffer_event(\core\event\base $event) { |
---|
96 | |
---|
97 | // If there are no subscriptions for this event do not buffer it. |
---|
98 | if (!\tool_monitor\subscription_manager::event_has_subscriptions($event->eventname, $event->courseid)) { |
---|
99 | return; |
---|
100 | } |
---|
101 | |
---|
102 | $eventdata = $event->get_data(); |
---|
103 | $eventobj = new \stdClass(); |
---|
104 | $eventobj->eventname = $eventdata['eventname']; |
---|
105 | $eventobj->contextid = $eventdata['contextid']; |
---|
106 | $eventobj->contextlevel = $eventdata['contextlevel']; |
---|
107 | $eventobj->contextinstanceid = $eventdata['contextinstanceid']; |
---|
108 | if ($event->get_url()) { |
---|
109 | // Get link url if exists. |
---|
110 | $eventobj->link = $event->get_url()->out(); |
---|
111 | } else { |
---|
112 | $eventobj->link = ''; |
---|
113 | } |
---|
114 | $eventobj->courseid = $eventdata['courseid']; |
---|
115 | $eventobj->timecreated = $eventdata['timecreated']; |
---|
116 | |
---|
117 | $this->buffer[] = $eventobj; |
---|
118 | $this->count++; |
---|
119 | } |
---|
120 | |
---|
121 | /** |
---|
122 | * This method process all events stored in the buffer. |
---|
123 | * |
---|
124 | * This is a multi purpose api. It does the following:- |
---|
125 | * 1. Write event data to tool_monitor_events |
---|
126 | * 2. Find out users that need to be notified about rule completion and schedule a task to send them messages. |
---|
127 | */ |
---|
128 | public function process_buffer() { |
---|
129 | global $DB; |
---|
130 | |
---|
131 | $events = $this->flush(); // Flush data. |
---|
132 | |
---|
133 | $select = "SELECT COUNT(id) FROM {tool_monitor_events} "; |
---|
134 | $now = time(); |
---|
135 | $messagestosend = array(); |
---|
136 | $allsubids = array(); |
---|
137 | |
---|
138 | // Let us now process the events and check for subscriptions. |
---|
139 | foreach ($events as $eventobj) { |
---|
140 | $subscriptions = subscription_manager::get_subscriptions_by_event($eventobj); |
---|
141 | $idstosend = array(); |
---|
142 | foreach ($subscriptions as $subscription) { |
---|
143 | $starttime = $now - $subscription->timewindow; |
---|
144 | $starttime = ($starttime > $subscription->lastnotificationsent) ? $starttime : $subscription->lastnotificationsent; |
---|
145 | if ($subscription->courseid == 0) { |
---|
146 | // Site level subscription. Count all events. |
---|
147 | $where = "eventname = :eventname AND timecreated > :starttime"; |
---|
148 | $params = array('eventname' => $eventobj->eventname, 'starttime' => $starttime); |
---|
149 | } else { |
---|
150 | // Course level subscription. |
---|
151 | if ($subscription->cmid == 0) { |
---|
152 | // All modules. |
---|
153 | $where = "eventname = :eventname AND courseid = :courseid AND timecreated > :starttime"; |
---|
154 | $params = array('eventname' => $eventobj->eventname, 'courseid' => $eventobj->courseid, |
---|
155 | 'starttime' => $starttime); |
---|
156 | } else { |
---|
157 | // Specific module. |
---|
158 | $where = "eventname = :eventname AND courseid = :courseid AND contextinstanceid = :cmid |
---|
159 | AND timecreated > :starttime"; |
---|
160 | $params = array('eventname' => $eventobj->eventname, 'courseid' => $eventobj->courseid, |
---|
161 | 'cmid' => $eventobj->contextinstanceid, 'starttime' => $starttime); |
---|
162 | |
---|
163 | } |
---|
164 | } |
---|
165 | $sql = $select . "WHERE " . $where; |
---|
166 | $count = $DB->count_records_sql($sql, $params); |
---|
167 | if (!empty($count) && $count >= $subscription->frequency) { |
---|
168 | $idstosend[] = $subscription->id; |
---|
169 | |
---|
170 | // Trigger a subscription_criteria_met event. |
---|
171 | // It's possible that the course has been deleted since the criteria was met, so in that case use |
---|
172 | // the system context. Set it here and change later if needed. |
---|
173 | $context = \context_system::instance(); |
---|
174 | // We can't perform if (!empty($subscription->courseid)) below as it uses the magic method |
---|
175 | // __get to return the variable, which will always result in being empty. |
---|
176 | $courseid = $subscription->courseid; |
---|
177 | if (!empty($courseid)) { |
---|
178 | if ($coursecontext = \context_course::instance($courseid, IGNORE_MISSING)) { |
---|
179 | $context = $coursecontext; |
---|
180 | } |
---|
181 | } |
---|
182 | |
---|
183 | $params = array( |
---|
184 | 'userid' => $subscription->userid, |
---|
185 | 'courseid' => $subscription->courseid, |
---|
186 | 'context' => $context, |
---|
187 | 'other' => array( |
---|
188 | 'subscriptionid' => $subscription->id |
---|
189 | ) |
---|
190 | ); |
---|
191 | $event = \tool_monitor\event\subscription_criteria_met::create($params); |
---|
192 | $event->trigger(); |
---|
193 | } |
---|
194 | } |
---|
195 | if (!empty($idstosend)) { |
---|
196 | $messagestosend[] = array('subscriptionids' => $idstosend, 'event' => $eventobj); |
---|
197 | $allsubids = array_merge($allsubids, $idstosend); |
---|
198 | } |
---|
199 | } |
---|
200 | |
---|
201 | if (!empty($allsubids)) { |
---|
202 | // Update the last trigger flag. |
---|
203 | list($sql, $params) = $DB->get_in_or_equal($allsubids, SQL_PARAMS_NAMED); |
---|
204 | $params['now'] = $now; |
---|
205 | $sql = "UPDATE {tool_monitor_subscriptions} SET lastnotificationsent = :now WHERE id $sql"; |
---|
206 | $DB->execute($sql, $params); |
---|
207 | } |
---|
208 | |
---|
209 | // Schedule a task to send notification. |
---|
210 | if (!empty($messagestosend)) { |
---|
211 | $adhocktask = new notification_task(); |
---|
212 | $adhocktask->set_custom_data($messagestosend); |
---|
213 | $adhocktask->set_component('tool_monitor'); |
---|
214 | \core\task\manager::queue_adhoc_task($adhocktask); |
---|
215 | } |
---|
216 | } |
---|
217 | |
---|
218 | /** |
---|
219 | * Protected method that flushes the buffer of events and writes them to the database. |
---|
220 | * |
---|
221 | * @return array a copy of the events buffer. |
---|
222 | */ |
---|
223 | protected function flush() { |
---|
224 | global $DB; |
---|
225 | |
---|
226 | // Flush the buffer to the db. |
---|
227 | $events = $this->buffer; |
---|
228 | $DB->insert_records('tool_monitor_events', $events); // Insert the whole chunk into the database. |
---|
229 | $this->buffer = array(); |
---|
230 | $this->count = 0; |
---|
231 | return $events; |
---|
232 | } |
---|
233 | |
---|
234 | /** |
---|
235 | * Observer that monitors user deleted event and delete user subscriptions. |
---|
236 | * |
---|
237 | * @param \core\event\user_deleted $event the event object. |
---|
238 | */ |
---|
239 | public static function user_deleted(\core\event\user_deleted $event) { |
---|
240 | $userid = $event->objectid; |
---|
241 | subscription_manager::delete_user_subscriptions($userid); |
---|
242 | } |
---|
243 | |
---|
244 | /** |
---|
245 | * Observer that monitors course module deleted event and delete user subscriptions. |
---|
246 | * |
---|
247 | * @param \core\event\course_module_deleted $event the event object. |
---|
248 | */ |
---|
249 | public static function course_module_deleted(\core\event\course_module_deleted $event) { |
---|
250 | $cmid = $event->contextinstanceid; |
---|
251 | subscription_manager::delete_cm_subscriptions($cmid); |
---|
252 | } |
---|
253 | } |
---|