MDL-30985 Fixed up event API phpdocs
[moodle.git] / lib / eventslib.php
CommitLineData
0856223c 1<?php
117bd748
PS
2// This file is part of Moodle - http://moodle.org/
3//
11e1f828
MH
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.
117bd748 13//
11e1f828
MH
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
0856223c 17/**
18 * Library of functions for events manipulation.
117bd748 19 *
583e93ca 20 * The public API is all at the end of this file.
d77717d7 21 *
1d1719f5
SH
22 * @package core_event
23 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
0856223c 25 */
26
78bfb562
PS
27defined('MOODLE_INTERNAL') || die();
28
0856223c 29/**
30 * Loads the events definitions for the component (from file). If no
31 * events are defined for the component, we simply return an empty array.
d77717d7 32 *
1d1719f5 33 * @access protected INTERNAL - to be used from eventslib only
11e1f828 34 *
650462df 35 * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
11e1f828 36 * @return array of capabilities or empty array if not exists
0856223c 37 */
38function events_load_def($component) {
650462df
PS
39 global $CFG;
40 if ($component === 'unittest') {
41 $defpath = $CFG->dirroot.'/lib/simpletest/fixtures/events.php';
42 } else {
43 $defpath = get_component_directory($component).'/db/events.php';
44 }
0856223c 45
de420c11 46 $handlers = array();
d77717d7 47
0856223c 48 if (file_exists($defpath)) {
49 require($defpath);
0856223c 50 }
d77717d7 51
650462df
PS
52 // make sure the definitions are valid and complete; tell devs what is wrong
53 foreach ($handlers as $eventname => $handler) {
54 if ($eventname === 'reset') {
55 debugging("'reset' can not be used as event name.");
56 unset($handlers['reset']);
57 continue;
58 }
59 if (!is_array($handler)) {
60 debugging("Handler of '$eventname' must be specified as array'");
61 unset($handlers[$eventname]);
62 continue;
63 }
64 if (!isset($handler['handlerfile'])) {
65 debugging("Handler of '$eventname' must include 'handlerfile' key'");
66 unset($handlers[$eventname]);
67 continue;
68 }
69 if (!isset($handler['handlerfunction'])) {
70 debugging("Handler of '$eventname' must include 'handlerfunction' key'");
71 unset($handlers[$eventname]);
72 continue;
73 }
74 if (!isset($handler['schedule'])) {
75 $handler['schedule'] = 'instant';
76 }
77 if ($handler['schedule'] !== 'instant' and $handler['schedule'] !== 'cron') {
78 debugging("Handler of '$eventname' must include valid 'schedule' type (instant or cron)'");
79 unset($handlers[$eventname]);
80 continue;
81 }
82 if (!isset($handler['internal'])) {
83 $handler['internal'] = 1;
84 }
85 $handlers[$eventname] = $handler;
86 }
87
de420c11 88 return $handlers;
0856223c 89}
90
91/**
92 * Gets the capabilities that have been cached in the database for this
93 * component.
d77717d7 94 *
1d1719f5 95 * @access protected INTERNAL - to be used from eventslib only
11e1f828 96 *
650462df 97 * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
11e1f828 98 * @return array of events
0856223c 99 */
d46306de 100function events_get_cached($component) {
25c6a19d 101 global $DB;
102
d46306de 103 $cachedhandlers = array();
104
650462df 105 if ($storedhandlers = $DB->get_records('events_handlers', array('component'=>$component))) {
d46306de 106 foreach ($storedhandlers as $handler) {
107 $cachedhandlers[$handler->eventname] = array (
108 'id' => $handler->id,
109 'handlerfile' => $handler->handlerfile,
110 'handlerfunction' => $handler->handlerfunction,
650462df
PS
111 'schedule' => $handler->schedule,
112 'internal' => $handler->internal);
0856223c 113 }
0856223c 114 }
0856223c 115
d46306de 116 return $cachedhandlers;
d77717d7 117}
0856223c 118
119/**
9e2e5943 120 * We can not removed all event handlers in table, then add them again
121 * because event handlers could be referenced by queued items
122 *
9e2e5943 123 * Note that the absence of the db/events.php event definition file
d46306de 124 * will cause any queued events for the component to be removed from
0856223c 125 * the database.
126 *
1d1719f5
SH
127 * @category event
128 *
17da2e6f 129 * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
650462df 130 * @return boolean always returns true
0856223c 131 */
0856223c 132function events_update_definition($component='moodle') {
25c6a19d 133 global $DB;
0856223c 134
9e2e5943 135 // load event definition from events.php
d46306de 136 $filehandlers = events_load_def($component);
d77717d7 137
0856223c 138 // load event definitions from db tables
139 // if we detect an event being already stored, we discard from this array later
140 // the remaining needs to be removed
d46306de 141 $cachedhandlers = events_get_cached($component);
d77717d7 142
d46306de 143 foreach ($filehandlers as $eventname => $filehandler) {
144 if (!empty($cachedhandlers[$eventname])) {
650462df
PS
145 if ($cachedhandlers[$eventname]['handlerfile'] === $filehandler['handlerfile'] &&
146 $cachedhandlers[$eventname]['handlerfunction'] === serialize($filehandler['handlerfunction']) &&
147 $cachedhandlers[$eventname]['schedule'] === $filehandler['schedule'] &&
148 $cachedhandlers[$eventname]['internal'] == $filehandler['internal']) {
d77717d7 149 // exact same event handler already present in db, ignore this entry
150
d46306de 151 unset($cachedhandlers[$eventname]);
d77717d7 152 continue;
153
154 } else {
155 // same event name matches, this event has been updated, update the datebase
365a5941 156 $handler = new stdClass();
d46306de 157 $handler->id = $cachedhandlers[$eventname]['id'];
158 $handler->handlerfile = $filehandler['handlerfile'];
159 $handler->handlerfunction = serialize($filehandler['handlerfunction']); // static class methods stored as array
160 $handler->schedule = $filehandler['schedule'];
650462df 161 $handler->internal = $filehandler['internal'];
d77717d7 162
25c6a19d 163 $DB->update_record('events_handlers', $handler);
d77717d7 164
d46306de 165 unset($cachedhandlers[$eventname]);
d77717d7 166 continue;
167 }
168
169 } else {
170 // if we are here, this event handler is not present in db (new)
171 // add it
365a5941 172 $handler = new stdClass();
d46306de 173 $handler->eventname = $eventname;
650462df 174 $handler->component = $component;
d46306de 175 $handler->handlerfile = $filehandler['handlerfile'];
176 $handler->handlerfunction = serialize($filehandler['handlerfunction']); // static class methods stored as array
177 $handler->schedule = $filehandler['schedule'];
650462df
PS
178 $handler->status = 0;
179 $handler->internal = $filehandler['internal'];
d46306de 180
25c6a19d 181 $DB->insert_record('events_handlers', $handler);
0856223c 182 }
183 }
d77717d7 184
d6e7a057 185 // clean up the left overs, the entries in cached events array at this points are deprecated event handlers
9e2e5943 186 // and should be removed, delete from db
d46306de 187 events_cleanup($component, $cachedhandlers);
0856223c 188
650462df
PS
189 events_get_handlers('reset');
190
0856223c 191 return true;
192}
193
d46306de 194/**
195 * Remove all event handlers and queued events
11e1f828 196 *
1d1719f5 197 * @category event
650462df 198 * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
d46306de 199 */
200function events_uninstall($component) {
201 $cachedhandlers = events_get_cached($component);
202 events_cleanup($component, $cachedhandlers);
650462df
PS
203
204 events_get_handlers('reset');
d46306de 205}
206
0856223c 207/**
208 * Deletes cached events that are no longer needed by the component.
d77717d7 209 *
1d1719f5 210 * @access protected INTERNAL - to be used from eventslib only
11e1f828 211 *
650462df 212 * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
d6e7a057 213 * @param array $cachedhandlers array of the cached events definitions that will be
650462df 214 * @return int number of unused handlers that have been removed
0856223c 215 */
d46306de 216function events_cleanup($component, $cachedhandlers) {
25c6a19d 217 global $DB;
218
0856223c 219 $deletecount = 0;
d46306de 220 foreach ($cachedhandlers as $eventname => $cachedhandler) {
25c6a19d 221 if ($qhandlers = $DB->get_records('events_queue_handlers', array('handlerid'=>$cachedhandler['id']))) {
650462df 222 //debugging("Removing pending events from queue before deleting of event handler: $component - $eventname");
d46306de 223 foreach ($qhandlers as $qhandler) {
224 events_dequeue($qhandler);
225 }
226 }
650462df
PS
227 $DB->delete_records('events_handlers', array('eventname'=>$eventname, 'component'=>$component));
228 $deletecount++;
0856223c 229 }
650462df 230
0856223c 231 return $deletecount;
232}
233
8aaf935a 234/****************** End of Events handler Definition code *******************/
235
8aaf935a 236/**
1d1719f5 237 * Puts a handler on queue
d46306de 238 *
1d1719f5 239 * @access protected INTERNAL - to be used from eventslib only
11e1f828 240 *
1d1719f5
SH
241 * @param stdClass $handler event handler object from db
242 * @param stdClass $event event data object
11e1f828 243 * @param string $errormessage The error message indicating the problem
1d1719f5 244 * @return int id number of new queue handler
8aaf935a 245 */
d46306de 246function events_queue_handler($handler, $event, $errormessage) {
25c6a19d 247 global $DB;
d77717d7 248
25c6a19d 249 if ($qhandler = $DB->get_record('events_queue_handlers', array('queuedeventid'=>$event->id, 'handlerid'=>$handler->id))) {
d46306de 250 debugging("Please check code: Event id $event->id is already queued in handler id $qhandler->id");
251 return $qhandler->id;
252 }
d77717d7 253
d46306de 254 // make a new queue handler
365a5941 255 $qhandler = new stdClass();
d46306de 256 $qhandler->queuedeventid = $event->id;
257 $qhandler->handlerid = $handler->id;
25c6a19d 258 $qhandler->errormessage = $errormessage;
d46306de 259 $qhandler->timemodified = time();
650462df 260 if ($handler->schedule === 'instant' and $handler->status == 1) {
d46306de 261 $qhandler->status = 1; //already one failed attempt to dispatch this event
262 } else {
263 $qhandler->status = 0;
8aaf935a 264 }
d46306de 265
25c6a19d 266 return $DB->insert_record('events_queue_handlers', $qhandler);
8aaf935a 267}
268
269/**
270 * trigger a single event with a specified handler
d46306de 271 *
1d1719f5 272 * @access protected INTERNAL - to be used from eventslib only
11e1f828 273 *
1d1719f5
SH
274 * @param stdClass $hander Row from db
275 * @param stdClass $eventdata dataobject
11e1f828 276 * @param string $errormessage error message indicating problem
1d1719f5 277 * @return bool|null True means event processed, false means retry event later; may throw exception, NULL means internal error
8aaf935a 278 */
d46306de 279function events_dispatch($handler, $eventdata, &$errormessage) {
8aaf935a 280 global $CFG;
d77717d7 281
d46306de 282 $function = unserialize($handler->handlerfunction);
283
284 if (is_callable($function)) {
285 // oki, no need for includes
286
287 } else if (file_exists($CFG->dirroot.$handler->handlerfile)) {
288 include_once($CFG->dirroot.$handler->handlerfile);
289
290 } else {
650462df
PS
291 $errormessage = "Handler file of component $handler->component: $handler->handlerfile can not be found!";
292 return null;
d46306de 293 }
294
295 // checks for handler validity
296 if (is_callable($function)) {
650462df
PS
297 $result = call_user_func($function, $eventdata);
298 if ($result === false) {
299 $errormessage = "Handler function of component $handler->component: $handler->handlerfunction requested resending of event!";
300 return false;
301 }
302 return true;
d77717d7 303
d46306de 304 } else {
650462df
PS
305 $errormessage = "Handler function of component $handler->component: $handler->handlerfunction not callable function or class method!";
306 return null;
d46306de 307 }
8aaf935a 308}
309
310/**
311 * given a queued handler, call the respective event handler to process the event
d46306de 312 *
1d1719f5 313 * @access protected INTERNAL - to be used from eventslib only
11e1f828 314 *
1d1719f5 315 * @param stdClass $qhandler events_queued_handler row from db
650462df 316 * @return boolean true means event processed, false means retry later, NULL means fatal failure
8aaf935a 317 */
d46306de 318function events_process_queued_handler($qhandler) {
650462df 319 global $DB;
d77717d7 320
8aaf935a 321 // get handler
25c6a19d 322 if (!$handler = $DB->get_record('events_handlers', array('id'=>$qhandler->handlerid))) {
d46306de 323 debugging("Error processing queue handler $qhandler->id, missing handler id: $qhandler->handlerid");
324 //irrecoverable error, remove broken queue handler
325 events_dequeue($qhandler);
326 return NULL;
8aaf935a 327 }
d46306de 328
8aaf935a 329 // get event object
25c6a19d 330 if (!$event = $DB->get_record('events_queue', array('id'=>$qhandler->queuedeventid))) {
d46306de 331 // can't proceed with no event object - might happen when two crons running at the same time
332 debugging("Error processing queue handler $qhandler->id, missing event id: $qhandler->queuedeventid");
333 //irrecoverable error, remove broken queue handler
334 events_dequeue($qhandler);
335 return NULL;
336 }
337
338 // call the function specified by the handler
650462df
PS
339 try {
340 $errormessage = 'Unknown error';
341 if (events_dispatch($handler, unserialize(base64_decode($event->eventdata)), $errormessage)) {
342 //everything ok
343 events_dequeue($qhandler);
344 return true;
345 }
346 } catch (Exception $e) {
347 // the problem here is that we do not want one broken handler to stop all others,
348 // cron handlers are very tricky because the needed data might have been deleted before the cron execution
99fa9745
TH
349 $errormessage = "Handler function of component $handler->component: $handler->handlerfunction threw exception :" .
350 $e->getMessage() . "\n" . format_backtrace($e->getTrace(), true);
351 if (!empty($e->debuginfo)) {
352 $errormessage .= $e->debuginfo;
353 }
8aaf935a 354 }
650462df
PS
355
356 //dispatching failed
365a5941 357 $qh = new stdClass();
650462df
PS
358 $qh->id = $qhandler->id;
359 $qh->errormessage = $errormessage;
360 $qh->timemodified = time();
361 $qh->status = $qhandler->status + 1;
362 $DB->update_record('events_queue_handlers', $qh);
363
364 return false;
d46306de 365}
366
367/**
d6e7a057 368 * Removes this queued handler from the events_queued_handler table
11e1f828 369 *
d6e7a057 370 * Removes events_queue record from events_queue if no more references to this event object exists
d46306de 371 *
1d1719f5 372 * @access protected INTERNAL - to be used from eventslib only
11e1f828 373 *
1d1719f5 374 * @param stdClass $qhandler events_queued_handler row from db
d46306de 375 */
376function events_dequeue($qhandler) {
25c6a19d 377 global $DB;
378
d46306de 379 // first delete the queue handler
25c6a19d 380 $DB->delete_records('events_queue_handlers', array('id'=>$qhandler->id));
8aaf935a 381
d46306de 382 // if no more queued handler is pointing to the same event - delete the event too
25c6a19d 383 if (!$DB->record_exists('events_queue_handlers', array('queuedeventid'=>$qhandler->queuedeventid))) {
384 $DB->delete_records('events_queue', array('id'=>$qhandler->queuedeventid));
d46306de 385 }
8aaf935a 386}
387
1336c96e 388/**
d6e7a057 389 * Returns handlers for given event. Uses caching for better perf.
1336c96e 390 *
1d1719f5 391 * @access protected INTERNAL - to be used from eventslib only
11e1f828 392 *
11e1f828
MH
393 * @staticvar array $handlers
394 * @param string $eventanme name of even or 'reset'
1d1719f5 395 * @return array|false array of handlers or false otherwise
1336c96e 396 */
397function events_get_handlers($eventname) {
a2d51ba8 398 global $DB;
1336c96e 399 static $handlers = array();
400
650462df 401 if ($eventname === 'reset') {
1336c96e 402 $handlers = array();
403 return false;
404 }
d46306de 405
1336c96e 406 if (!array_key_exists($eventname, $handlers)) {
407 $handlers[$eventname] = $DB->get_records('events_handlers', array('eventname'=>$eventname));
408 }
409
410 return $handlers[$eventname];
411}
d46306de 412
413/****** Public events API starts here, do not use functions above in 3rd party code ******/
414
415
8aaf935a 416/**
417 * Events cron will try to empty the events queue by processing all the queued events handlers
d46306de 418 *
1d1719f5
SH
419 * @access public Part of the public API
420 * @category event
11e1f828 421 *
11e1f828 422 * @param string $eventname empty means all
1d1719f5 423 * @return int number of dispatched events
8aaf935a 424 */
d46306de 425function events_cron($eventname='') {
25c6a19d 426 global $DB;
d77717d7 427
d46306de 428 $failed = array();
429 $processed = 0;
430
431 if ($eventname) {
25c6a19d 432 $sql = "SELECT qh.*
433 FROM {events_queue_handlers} qh, {events_handlers} h
434 WHERE qh.handlerid = h.id AND h.eventname=?
435 ORDER BY qh.id";
436 $params = array($eventname);
d46306de 437 } else {
25c6a19d 438 $sql = "SELECT *
439 FROM {events_queue_handlers}
440 ORDER BY id";
441 $params = array();
d46306de 442 }
443
b967c541
EL
444 $rs = $DB->get_recordset_sql($sql, $params);
445 foreach ($rs as $qhandler) {
446 if (isset($failed[$qhandler->handlerid])) {
447 // do not try to dispatch any later events when one already asked for retry or ended with exception
448 continue;
449 }
450 $status = events_process_queued_handler($qhandler);
451 if ($status === false) {
452 // handler is asking for retry, do not send other events to this handler now
453 $failed[$qhandler->handlerid] = $qhandler->handlerid;
454 } else if ($status === NULL) {
455 // means completely broken handler, event data was purged
456 $failed[$qhandler->handlerid] = $qhandler->handlerid;
457 } else {
458 $processed++;
d77717d7 459 }
8aaf935a 460 }
b967c541 461 $rs->close();
650462df
PS
462
463 // remove events that do not have any handlers waiting
464 $sql = "SELECT eq.id
465 FROM {events_queue} eq
466 LEFT JOIN {events_queue_handlers} qh ON qh.queuedeventid = eq.id
467 WHERE qh.id IS NULL";
468 $rs = $DB->get_recordset_sql($sql);
469 foreach ($rs as $event) {
470 //debugging('Purging stale event '.$event->id);
471 $DB->delete_records('events_queue', array('id'=>$event->id));
472 }
473 $rs->close();
474
d46306de 475 return $processed;
8aaf935a 476}
477
d46306de 478
8aaf935a 479/**
d6e7a057 480 * Function to call all event handlers when triggering an event
d46306de 481 *
1d1719f5
SH
482 * @access public Part of the public API.
483 * @category event
11e1f828 484 *
11e1f828
MH
485 * @param string $eventname name of the event
486 * @param object $eventdata event data object
487 * @return int number of failed events
8aaf935a 488 */
d46306de 489function events_trigger($eventname, $eventdata) {
25c6a19d 490 global $CFG, $USER, $DB;
d77717d7 491
d46306de 492 $failedcount = 0; // number of failed events.
d46306de 493
494 // pull out all registered event handlers
1336c96e 495 if ($handlers = events_get_handlers($eventname)) {
d46306de 496 foreach ($handlers as $handler) {
650462df 497 $errormessage = '';
d46306de 498
650462df 499 if ($handler->schedule === 'instant') {
d46306de 500 if ($handler->status) {
501 //check if previous pending events processed
25c6a19d 502 if (!$DB->record_exists('events_queue_handlers', array('handlerid'=>$handler->id))) {
d46306de 503 // ok, queue is empty, lets reset the status back to 0 == ok
504 $handler->status = 0;
25c6a19d 505 $DB->set_field('events_handlers', 'status', 0, array('id'=>$handler->id));
779ad29f 506 // reset static handler cache
507 events_get_handlers('reset');
d46306de 508 }
509 }
510
511 // dispatch the event only if instant schedule and status ok
650462df 512 if ($handler->status or (!$handler->internal and $DB->is_transaction_started())) {
d46306de 513 // increment the error status counter
514 $handler->status++;
25c6a19d 515 $DB->set_field('events_handlers', 'status', $handler->status, array('id'=>$handler->id));
779ad29f 516 // reset static handler cache
517 events_get_handlers('reset');
650462df
PS
518
519 } else {
520 $errormessage = 'Unknown error';;
521 $result = events_dispatch($handler, $eventdata, $errormessage);
522 if ($result === true) {
523 // everything is fine - event dispatched
524 continue;
525 } else if ($result === false) {
526 // retry later - set error count to 1 == send next instant into cron queue
527 $DB->set_field('events_handlers', 'status', 1, array('id'=>$handler->id));
528 // reset static handler cache
529 events_get_handlers('reset');
530 } else {
531 // internal problem - ignore the event completely
532 $failedcount ++;
533 continue;
534 }
d46306de 535 }
536
537 // update the failed counter
538 $failedcount ++;
539
650462df 540 } else if ($handler->schedule === 'cron') {
d6e7a057 541 //ok - use queueing of events only
d46306de 542
543 } else {
650462df 544 // unknown schedule - ignore event completely
d46306de 545 debugging("Unknown handler schedule type: $handler->schedule");
650462df
PS
546 $failedcount ++;
547 continue;
d46306de 548 }
549
650462df 550 // if even type is not instant, or dispatch asked for retry, queue it
365a5941 551 $event = new stdClass();
650462df
PS
552 $event->userid = $USER->id;
553 $event->eventdata = base64_encode(serialize($eventdata));
554 $event->timecreated = time();
555 if (debugging()) {
556 $dump = '';
557 $callers = debug_backtrace();
558 foreach ($callers as $caller) {
559 if (!isset($caller['line'])) {
560 $caller['line'] = '?';
561 }
562 if (!isset($caller['file'])) {
563 $caller['file'] = '?';
564 }
565 $dump .= 'line ' . $caller['line'] . ' of ' . substr($caller['file'], strlen($CFG->dirroot) + 1);
566 if (isset($caller['function'])) {
567 $dump .= ': call to ';
568 if (isset($caller['class'])) {
569 $dump .= $caller['class'] . $caller['type'];
d46306de 570 }
650462df 571 $dump .= $caller['function'] . '()';
d46306de 572 }
650462df 573 $dump .= "\n";
d46306de 574 }
650462df
PS
575 $event->stackdump = $dump;
576 } else {
577 $event->stackdump = '';
d46306de 578 }
650462df 579 $event->id = $DB->insert_record('events_queue', $event);
d46306de 580 events_queue_handler($handler, $event, $errormessage);
8aaf935a 581 }
8aaf935a 582 } else {
650462df 583 // No handler found for this event name - this is ok!
8aaf935a 584 }
d46306de 585
586 return $failedcount;
8aaf935a 587}
9e2e5943 588
589/**
590 * checks if an event is registered for this component
d46306de 591 *
1d1719f5
SH
592 * @access public Part of the public API
593 *
11e1f828
MH
594 * @param string $eventname name of the event
595 * @param string $component component name, can be mod/data or moodle
596 * @return bool
9e2e5943 597 */
d46306de 598function events_is_registered($eventname, $component) {
25c6a19d 599 global $DB;
650462df 600 return $DB->record_exists('events_handlers', array('component'=>$component, 'eventname'=>$eventname));
9e2e5943 601}
d46306de 602
603/**
604 * checks if an event is queued for processing - either cron handlers attached or failed instant handlers
d46306de 605 *
1d1719f5 606 * @access public Part of the public API
11e1f828 607 *
11e1f828
MH
608 * @param string $eventname name of the event
609 * @return int number of queued events
d46306de 610 */
611function events_pending_count($eventname) {
650462df 612 global $DB;
d46306de 613
25c6a19d 614 $sql = "SELECT COUNT('x')
650462df
PS
615 FROM {events_queue_handlers} qh
616 JOIN {events_handlers} h ON h.id = qh.handlerid
617 WHERE h.eventname = ?";
618
25c6a19d 619 return $DB->count_records_sql($sql, array($eventname));
1d1719f5 620}