<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+/**
+ * Execute cron tasks
+ */
function cron_run() {
global $DB, $CFG, $OUTPUT;
set_time_limit(0);
$starttime = microtime();
-/// increase memory limit
+ // Increase memory limit
raise_memory_limit(MEMORY_EXTRA);
-/// emulate normal session
+ // Emulate normal session - we use admin accoutn by default
cron_setup_user();
-/// Start output log
-
+ // Start output log
$timenow = time();
-
mtrace("Server Time: ".date('r',$timenow)."\n\n");
-/// Session gc
+ // Run cleanup core cron jobs, but not every time since they aren't too important.
+ // These don't have a timer to reduce load, so we'll use a random number
+ // to randomly choose the percentage of times we should run these jobs.
+ srand ((double) microtime() * 10000000);
+ $random100 = rand(0,100);
+ if ($random100 < 20) { // Approximately 20% of the time.
+ mtrace("Running clean-up tasks...");
+
+ // Delete users who haven't confirmed within required period
+ if (!empty($CFG->deleteunconfirmed)) {
+ $cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
+ $rs = $DB->get_recordset_sql ("SELECT *
+ FROM {user}
+ WHERE confirmed = 0 AND firstaccess > 0
+ AND firstaccess < ?", array($cuttime));
+ foreach ($rs as $user) {
+ delete_user($user); // we MUST delete user properly first
+ $DB->delete_records('user', array('id'=>$user->id)); // this is a bloody hack, but it might work
+ mtrace(" Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
+ }
+ $rs->close();
+ }
+
+
+ // Delete users who haven't completed profile within required period
+ if (!empty($CFG->deleteincompleteusers)) {
+ $cuttime = $timenow - ($CFG->deleteincompleteusers * 3600);
+ $rs = $DB->get_recordset_sql ("SELECT *
+ FROM {user}
+ WHERE confirmed = 1 AND lastaccess > 0
+ AND lastaccess < ? AND deleted = 0
+ AND (lastname = '' OR firstname = '' OR email = '')",
+ array($cuttime));
+ foreach ($rs as $user) {
+ delete_user($user);
+ mtrace(" Deleted not fully setup user $user->username ($user->id)");
+ }
+ $rs->close();
+ }
+
+
+ // Delete old logs to save space (this might need a timer to slow it down...)
+ if (!empty($CFG->loglifetime)) { // value in days
+ $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
+ $DB->delete_records_select("log", "time < ?", array($loglifetime));
+ mtrace(" Deleted old log records");
+ }
+
+
+ // Delete old backup_controllers and logs
+ if (!empty($CFG->loglifetime)) { // value in days
+ $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
+ // Delete child records from backup_logs
+ $DB->execute("DELETE FROM {backup_logs}
+ WHERE EXISTS (
+ SELECT 'x'
+ FROM {backup_controllers} bc
+ WHERE bc.backupid = {backup_logs}.backupid
+ AND bc.timecreated < ?)", array($loglifetime));
+ // Delete records from backup_controllers
+ $DB->execute("DELETE FROM {backup_controllers}
+ WHERE timecreated < ?", array($loglifetime));
+ mtrace(" Deleted old backup records");
+ }
+
+
+ // Delete old cached texts
+ if (!empty($CFG->cachetext)) { // Defined in config.php
+ $cachelifetime = time() - $CFG->cachetext - 60; // Add an extra minute to allow for really heavy sites
+ $DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime));
+ mtrace(" Deleted old cache_text records");
+ }
+
+
+ if (!empty($CFG->usetags)) {
+ require_once($CFG->dirroot.'/tag/lib.php');
+ tag_cron();
+ mtrace(' Executed tag cron');
+ }
+
+
+ // Context maintenance stuff
+ context_helper::cleanup_instances();
+ mtrace(' Cleaned up context instances');
+ context_helper::build_all_paths(false);
+ // If you suspect that the context paths are somehow corrupt
+ // replace the line below with: context_helper::build_all_paths(true);
+ mtrace(' Built context paths');
+
+
+ // Remove expired cache flags
+ gc_cache_flags();
+ mtrace(' Cleaned cache flags');
+
+
+ // Cleanup messaging
+ if (!empty($CFG->messagingdeletereadnotificationsdelay)) {
+ $notificationdeletetime = time() - $CFG->messagingdeletereadnotificationsdelay;
+ $DB->delete_records_select('message_read', 'notification=1 AND timeread<:notificationdeletetime', array('notificationdeletetime'=>$notificationdeletetime));
+ mtrace(' Cleaned up read notifications');
+ }
+
+ mtrace("...finished clean-up tasks");
+
+ } // End of occasional clean-up tasks
+
+
+ // Send login failures notification - brute force protection in moodle is weak,
+ // we should at least send notices early in each cron execution
+ if (!empty($CFG->notifyloginfailures)) {
+ notify_login_failures();
+ mtrace(' Notified login failured');
+ }
+
+
+ // Make sure all context instances are properly created - they may be required in auth, enrol, etc.
+ context_helper::create_instances();
+ mtrace(' Created missing context instances');
+
- mtrace("Cleaning up stale sessions");
+ // Session gc
session_gc();
+ mtrace("Cleaned up stale user sessions");
-/// Run all cron jobs for each module
+ // Run the auth cron, if any before enrolments
+ // because it might add users that will be needed in enrol plugins
+ $auths = get_enabled_auth_plugins();
+ mtrace("Running auth crons if required...");
+ foreach ($auths as $auth) {
+ $authplugin = get_auth_plugin($auth);
+ if (method_exists($authplugin, 'cron')) {
+ mtrace("Running cron for auth/$auth...");
+ $authplugin->cron();
+ if (!empty($authplugin->log)) {
+ mtrace($authplugin->log);
+ }
+ }
+ unset($authplugin);
+ }
+ // Generate new password emails for users - ppl expect these generated asap
+ if ($DB->count_records('user_preferences', array('name'=>'create_password', 'value'=>'1'))) {
+ mtrace('Creating passwords for new users...');
+ $newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email, u.firstname,
+ u.lastname, u.username,
+ p.id as prefid
+ FROM {user} u
+ JOIN {user_preferences} p ON u.id=p.userid
+ WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
+
+ foreach ($newusers as $newuser) {
+ if (setnew_password_and_mail($newuser)) {
+ unset_user_preference('create_password', $newuser);
+ set_user_preference('auth_forcepasswordchange', 1, $newuser);
+ } else {
+ trigger_error("Could not create and mail new user password!");
+ }
+ }
+ $newusers->close();
+ }
+
+
+ // It is very important to run enrol early
+ // because other plugins depend on correct enrolment info.
+ mtrace("Running enrol crons if required...");
+ $enrols = enrol_get_plugins(true);
+ foreach($enrols as $ename=>$enrol) {
+ // do this for all plugins, disabled plugins might want to cleanup stuff such as roles
+ if (!$enrol->is_cron_required()) {
+ continue;
+ }
+ mtrace("Running cron for enrol_$ename...");
+ $enrol->cron();
+ $enrol->set_config('lastcron', time());
+ }
+
+
+ // Run all cron jobs for each module
mtrace("Starting activity modules");
get_mailer('buffer');
if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
}
- /// Reset possible changes by modules to time_limit. MDL-11597
+ // Reset possible changes by modules to time_limit. MDL-11597
@set_time_limit(0);
mtrace("done.");
}
get_mailer('close');
mtrace("Finished activity modules");
+
mtrace("Starting blocks");
if ($blocks = $DB->get_records_select("block", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
- // we will need the base class.
+ // We will need the base class.
require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
foreach ($blocks as $block) {
$blockfile = $CFG->dirroot.'/blocks/'.$block->name.'/block_'.$block->name.'.php';
if ($blockobj->cron()) {
$DB->set_field('block', 'lastcron', $timenow, array('id'=>$block->id));
}
- /// Reset possible changes by blocks to time_limit. MDL-11597
+ // Reset possible changes by blocks to time_limit. MDL-11597
@set_time_limit(0);
mtrace('done.');
}
}
mtrace('Finished blocks');
+
+ //TODO: get rid of this bloody hardcoded quiz module stuff, this must be done from quiz_cron()!
mtrace("Starting quiz reports");
if ($reports = $DB->get_records_select('quiz_reports', "cron > 0 AND ((? - lastcron) > cron)", array($timenow))) {
foreach ($reports as $report) {
}
mtrace("Finished quiz reports");
+
mtrace('Starting admin reports');
cron_execute_plugin_type('report');
mtrace('Finished admin reports');
- mtrace('Starting main gradebook job ...');
+
+ mtrace('Starting main gradebook job...');
grade_cron();
mtrace('done.');
mtrace('done');
}
+
//now do plagiarism checks
require_once($CFG->libdir.'/plagiarismlib.php');
plagiarism_cron();
-/// Run all core cron jobs, but not every time since they aren't too important.
-/// These don't have a timer to reduce load, so we'll use a random number
-/// to randomly choose the percentage of times we should run these jobs.
-
- srand ((double) microtime() * 10000000);
- $random100 = rand(0,100);
-
- if ($random100 < 20) { // Approximately 20% of the time.
- mtrace("Running clean-up tasks...");
-
- /// Delete users who haven't confirmed within required period
-
- if (!empty($CFG->deleteunconfirmed)) {
- $cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
- $rs = $DB->get_recordset_sql ("SELECT id, firstname, lastname
- FROM {user}
- WHERE confirmed = 0 AND firstaccess > 0
- AND firstaccess < ?", array($cuttime));
- foreach ($rs as $user) {
- if ($DB->delete_records('user', array('id'=>$user->id))) {
- mtrace("Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
- }
- }
- $rs->close();
- }
- flush();
-
-
- /// Delete users who haven't completed profile within required period
-
- if (!empty($CFG->deleteincompleteusers)) {
- $cuttime = $timenow - ($CFG->deleteincompleteusers * 3600);
- $rs = $DB->get_recordset_sql ("SELECT *
- FROM {user}
- WHERE confirmed = 1 AND lastaccess > 0
- AND lastaccess < ? AND deleted = 0
- AND (lastname = '' OR firstname = '' OR email = '')",
- array($cuttime));
- foreach ($rs as $user) {
- if (delete_user($user)) {
- mtrace("Deleted not fully setup user $user->username ($user->id)");
- }
- }
- $rs->close();
- }
- flush();
-
-
- /// Delete old logs to save space (this might need a timer to slow it down...)
-
- if (!empty($CFG->loglifetime)) { // value in days
- $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
- if ($DB->delete_records_select("log", "time < ?", array($loglifetime))) {
- mtrace("Deleted old log records");
- }
- }
- flush();
-
- // Delete old backup_controllers and logs
-
- if (!empty($CFG->loglifetime)) { // value in days
- $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
- // Delete child records from backup_logs
- $DB->execute("DELETE FROM {backup_logs}
- WHERE EXISTS (
- SELECT 'x'
- FROM {backup_controllers} bc
- WHERE bc.backupid = {backup_logs}.backupid
- AND bc.timecreated < ?)", array($loglifetime));
- // Delete records from backup_controllers
- $DB->execute("DELETE FROM {backup_controllers}
- WHERE timecreated < ?", array($loglifetime));
- mtrace("Deleted old backup records");
- }
- flush();
-
-
-
- /// Delete old cached texts
-
- if (!empty($CFG->cachetext)) { // Defined in config.php
- $cachelifetime = time() - $CFG->cachetext - 60; // Add an extra minute to allow for really heavy sites
- if ($DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime))) {
- mtrace("Deleted old cache_text records");
- }
- }
- flush();
-
- if (!empty($CFG->notifyloginfailures)) {
- notify_login_failures();
- mtrace('Notified login failured');
- }
- flush();
-
- //
- // generate new password emails for users
- //
- mtrace('checking for create_password');
- if ($DB->count_records('user_preferences', array('name'=>'create_password', 'value'=>'1'))) {
- mtrace('creating passwords for new users');
- $newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email, u.firstname,
- u.lastname, u.username,
- p.id as prefid
- FROM {user} u
- JOIN {user_preferences} p ON u.id=p.userid
- WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
-
- foreach ($newusers as $newuser) {
- // email user
- if (setnew_password_and_mail($newuser)) {
- unset_user_preference('create_password', $newuser);
- set_user_preference('auth_forcepasswordchange', 1, $newuser);
- } else {
- trigger_error("Could not create and mail new user password!");
- }
- }
- $newusers->close();
- }
-
- if (!empty($CFG->usetags)) {
- require_once($CFG->dirroot.'/tag/lib.php');
- tag_cron();
- mtrace ('Executed tag cron');
- }
-
- // Accesslib stuff
- cleanup_contexts();
- mtrace ('Cleaned up contexts');
- gc_cache_flags();
- mtrace ('Cleaned cache flags');
- // If you suspect that the context paths are somehow corrupt
- // replace the line below with: build_context_path(true);
- build_context_path();
- mtrace ('Built context paths');
-
- if (!empty($CFG->messagingdeletereadnotificationsdelay)) {
- $notificationdeletetime = time() - $CFG->messagingdeletereadnotificationsdelay;
- $DB->delete_records_select('message_read', 'notification=1 AND timeread<:notificationdeletetime', array('notificationdeletetime'=>$notificationdeletetime));
- mtrace('Cleaned up read notifications');
- }
-
- mtrace("Finished clean-up tasks...");
-
- } // End of occasional clean-up tasks
-
- // Run automated backups if required.
- require_once($CFG->dirroot.'/backup/util/includes/backup_includes.php');
- require_once($CFG->dirroot.'/backup/util/helper/backup_cron_helper.class.php');
- backup_cron_automated_helper::run_automated_backup();
-
-/// Run the auth cron, if any
-/// before enrolments because it might add users that will be needed in enrol plugins
- $auths = get_enabled_auth_plugins();
-
- mtrace("Running auth crons if required...");
- foreach ($auths as $auth) {
- $authplugin = get_auth_plugin($auth);
- if (method_exists($authplugin, 'cron')) {
- mtrace("Running cron for auth/$auth...");
- $authplugin->cron();
- if (!empty($authplugin->log)) {
- mtrace($authplugin->log);
- }
- }
- unset($authplugin);
- }
-
- mtrace("Running enrol crons if required...");
- $enrols = enrol_get_plugins(true);
- foreach($enrols as $ename=>$enrol) {
- // do this for all plugins, disabled plugins might want to cleanup stuff such as roles
- if (!$enrol->is_cron_required()) {
- continue;
- }
- mtrace("Running cron for enrol_$ename...");
- $enrol->cron();
- $enrol->set_config('lastcron', time());
- }
-
- if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
- require_once($CFG->dirroot.'/lib/statslib.php');
- // check we're not before our runtime
- $timetocheck = stats_get_base_daily() + $CFG->statsruntimestarthour*60*60 + $CFG->statsruntimestartminute*60;
-
- if (time() > $timetocheck) {
- // process configured number of days as max (defaulting to 31)
- $maxdays = empty($CFG->statsruntimedays) ? 31 : abs($CFG->statsruntimedays);
- if (stats_cron_daily($maxdays)) {
- if (stats_cron_weekly()) {
- if (stats_cron_monthly()) {
- stats_clean_old();
- }
- }
- }
- @set_time_limit(0);
- } else {
- mtrace('Next stats run after:'. userdate($timetocheck));
- }
- }
-
mtrace('Starting course reports');
cron_execute_plugin_type('coursereport');
mtrace('Finished course reports');
+
// run gradebook import/export/report cron
mtrace('Starting gradebook plugins');
cron_execute_plugin_type('gradeimport');
cron_execute_plugin_type('gradereport');
mtrace('Finished gradebook plugins');
+
// Run external blog cron if needed
if ($CFG->useexternalblogs) {
require_once($CFG->dirroot . '/blog/lib.php');
}
mtrace('done.');
}
-
// Run blog associations cleanup
if ($CFG->useblogassociations) {
require_once($CFG->dirroot . '/blog/lib.php');
mtrace('done.');
}
+
//Run registration updated cron
mtrace(get_string('siteupdatesstart', 'hub'));
require_once($CFG->dirroot . '/admin/registration/lib.php');
$registrationmanager->cron();
mtrace(get_string('siteupdatesend', 'hub'));
- // cleanup file trash
- $fs = get_file_storage();
- $fs->cron();
//cleanup old session linked tokens
//deletes the session linked tokens that are over a day old.
array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
mtrace('done.');
+
// all other plugins
cron_execute_plugin_type('message', 'message plugins');
cron_execute_plugin_type('filter', 'filters');
cron_execute_plugin_type('theme', 'themes');
cron_execute_plugin_type('tool', 'admin tools');
+
// and finally run any local cronjobs, if any
if ($locals = get_plugin_list('local')) {
mtrace('Processing customized cron scripts ...', '');
mtrace('done.');
}
+
+ // Run automated backups if required - these may take a long time to execute
+ require_once($CFG->dirroot.'/backup/util/includes/backup_includes.php');
+ require_once($CFG->dirroot.'/backup/util/helper/backup_cron_helper.class.php');
+ backup_cron_automated_helper::run_automated_backup();
+
+
+ // Run stats as at the end because they are known to take very long time on large sites
+ if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
+ require_once($CFG->dirroot.'/lib/statslib.php');
+ // check we're not before our runtime
+ $timetocheck = stats_get_base_daily() + $CFG->statsruntimestarthour*60*60 + $CFG->statsruntimestartminute*60;
+
+ if (time() > $timetocheck) {
+ // process configured number of days as max (defaulting to 31)
+ $maxdays = empty($CFG->statsruntimedays) ? 31 : abs($CFG->statsruntimedays);
+ if (stats_cron_daily($maxdays)) {
+ if (stats_cron_weekly()) {
+ if (stats_cron_monthly()) {
+ stats_clean_old();
+ }
+ }
+ }
+ @set_time_limit(0);
+ } else {
+ mtrace('Next stats run after:'. userdate($timetocheck));
+ }
+ }
+
+
+ // cleanup file trash - not very important
+ $fs = get_file_storage();
+ $fs->cron();
+
+
mtrace("Cron script completed correctly");
$difftime = microtime_diff($starttime, microtime());
/**
* Executes cron functions for a specific type of plugin.
+ *
* @param string $plugintype Plugin type (e.g. 'report')
* @param string $description If specified, will display 'Starting (whatever)'
* and 'Finished (whatever)' lines, otherwise does not display
* Used to add in old-style cron functions within plugins that have not been converted to the
* new standard API. (The standard API is frankenstyle_name_cron() in lib.php; some types used
* cron.php and some used a different name.)
+ *
* @param string $plugintype Plugin type e.g. 'report'
* @param array $plugins Array from plugin name (e.g. 'report_frog') to function name (e.g.
* 'report_frog_cron') for plugin cron functions that were already found using the new API
}
return $plugins;
-}
\ No newline at end of file
+}
+
+
+/**
+ * Notify admin users or admin user of any failed logins (since last notification).
+ *
+ * Note that this function must be only executed from the cron script
+ * It uses the cache_flags system to store temporary records, deleting them
+ * by name before finishing
+ */
+function notify_login_failures() {
+ global $CFG, $DB, $OUTPUT;
+
+ $recip = get_users_from_config($CFG->notifyloginfailures, 'moodle/site:config');
+
+ if (empty($CFG->lastnotifyfailure)) {
+ $CFG->lastnotifyfailure=0;
+ }
+
+ // we need to deal with the threshold stuff first.
+ if (empty($CFG->notifyloginthreshold)) {
+ $CFG->notifyloginthreshold = 10; // default to something sensible.
+ }
+
+ // Get all the IPs with more than notifyloginthreshold failures since lastnotifyfailure
+ // and insert them into the cache_flags temp table
+ $sql = "SELECT ip, COUNT(*)
+ FROM {log}
+ WHERE module = 'login' AND action = 'error'
+ AND time > ?
+ GROUP BY ip
+ HAVING COUNT(*) >= ?";
+ $params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach ($rs as $iprec) {
+ if (!empty($iprec->ip)) {
+ set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
+ }
+ }
+ $rs->close();
+
+ // Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure
+ // and insert them into the cache_flags temp table
+ $sql = "SELECT info, count(*)
+ FROM {log}
+ WHERE module = 'login' AND action = 'error'
+ AND time > ?
+ GROUP BY info
+ HAVING count(*) >= ?";
+ $params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach ($rs as $inforec) {
+ if (!empty($inforec->info)) {
+ set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
+ }
+ }
+ $rs->close();
+
+ // Now, select all the login error logged records belonging to the ips and infos
+ // since lastnotifyfailure, that we have stored in the cache_flags table
+ $sql = "SELECT l.*, u.firstname, u.lastname
+ FROM {log} l
+ JOIN {cache_flags} cf ON l.ip = cf.name
+ LEFT JOIN {user} u ON l.userid = u.id
+ WHERE l.module = 'login' AND l.action = 'error'
+ AND l.time > ?
+ AND cf.flagtype = 'login_failure_by_ip'
+ UNION ALL
+ SELECT l.*, u.firstname, u.lastname
+ FROM {log} l
+ JOIN {cache_flags} cf ON l.info = cf.name
+ LEFT JOIN {user} u ON l.userid = u.id
+ WHERE l.module = 'login' AND l.action = 'error'
+ AND l.time > ?
+ AND cf.flagtype = 'login_failure_by_info'
+ ORDER BY time DESC";
+ $params = array($CFG->lastnotifyfailure, $CFG->lastnotifyfailure);
+
+ // Init some variables
+ $count = 0;
+ $messages = '';
+ // Iterate over the logs recordset
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach ($rs as $log) {
+ $log->time = userdate($log->time);
+ $messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
+ $count++;
+ }
+ $rs->close();
+
+ // If we haven't run in the last hour and
+ // we have something useful to report and we
+ // are actually supposed to be reporting to somebody
+ if ((time() - HOURSECS) > $CFG->lastnotifyfailure && $count > 0 && is_array($recip) && count($recip) > 0) {
+ $site = get_site();
+ $subject = get_string('notifyloginfailuressubject', '', format_string($site->fullname));
+ // Calculate the complete body of notification (start + messages + end)
+ $body = get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot) .
+ (($CFG->lastnotifyfailure != 0) ? '('.userdate($CFG->lastnotifyfailure).')' : '')."\n\n" .
+ $messages .
+ "\n\n".get_string('notifyloginfailuresmessageend','',$CFG->wwwroot)."\n\n";
+
+ // For each destination, send mail
+ mtrace('Emailing admins about '. $count .' failed login attempts');
+ foreach ($recip as $admin) {
+ //emailing the admins directly rather than putting these through the messaging system
+ email_to_user($admin,get_admin(), $subject, $body);
+ }
+
+ // Update lastnotifyfailure with current time
+ set_config('lastnotifyfailure', time());
+ }
+
+ // Finally, delete all the temp records we have created in cache_flags
+ $DB->delete_records_select('cache_flags', "flagtype IN ('login_failure_by_ip', 'login_failure_by_info')");
+}