/** * Regular jobs to execute via cron * * @return boolean true on success, false otherwise */ function workshop_cron() { global $CFG, $DB; $now = time(); mtrace(' processing workshop subplugins ...'); cron_execute_plugin_type('workshopallocation', 'workshop allocation methods'); // now when the scheduled allocator had a chance to do its job, check if there // are some workshops to switch into the assessment phase $workshops = $DB->get_records_select("workshop", "phase = 20 AND phaseswitchassessment = 1 AND submissionend > 0 AND submissionend < ?", array($now)); if (!empty($workshops)) { mtrace('Processing automatic assessment phase switch in '.count($workshops).' workshop(s) ... ', ''); require_once($CFG->dirroot.'/mod/workshop/locallib.php'); foreach ($workshops as $workshop) { $cm = get_coursemodule_from_instance('workshop', $workshop->id, $workshop->course, false, MUST_EXIST); $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); $workshop = new workshop($workshop, $cm, $course); $workshop->switch_phase(workshop::PHASE_ASSESSMENT); $workshop->log('update switch phase', $workshop->view_url(), $workshop->phase); // disable the automatic switching now so that it is not executed again by accident // if the teacher changes the phase back to the submission one $DB->set_field('workshop', 'phaseswitchassessment', 0, array('id' => $workshop->id)); // todo inform the teachers } mtrace('done'); } return true; }
/** * Execute cron tasks */ function cron_run() { global $DB, $CFG, $OUTPUT; if (CLI_MAINTENANCE) { echo "CLI maintenance mode active, cron execution suspended.\n"; exit(1); } if (moodle_needs_upgrading()) { echo "Moodle upgrade pending, cron execution suspended.\n"; exit(1); } require_once $CFG->libdir . '/adminlib.php'; require_once $CFG->libdir . '/gradelib.php'; if (!empty($CFG->showcronsql)) { $DB->set_debug(true); } if (!empty($CFG->showcrondebugging)) { set_debugging(DEBUG_DEVELOPER, true); } set_time_limit(0); $starttime = microtime(); // Increase memory limit raise_memory_limit(MEMORY_EXTRA); // Emulate normal session - we use admin accoutn by default cron_setup_user(); // Start output log $timenow = time(); mtrace("Server Time: " . date('r', $timenow) . "\n\n"); // 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. $random100 = rand(0, 100); if ($random100 < 20) { // Approximately 20% of the time. mtrace("Running clean-up tasks..."); cron_trace_time_and_memory(); // Delete users who haven't confirmed within required period if (!empty($CFG->deleteunconfirmed)) { $cuttime = $timenow - $CFG->deleteunconfirmed * 3600; $rs = $DB->get_recordset_sql("SELECT *\n FROM {user}\n WHERE confirmed = 0 AND firstaccess > 0\n 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 *\n FROM {user}\n WHERE confirmed = 1 AND lastaccess > 0\n AND lastaccess < ? AND deleted = 0\n AND (lastname = '' OR firstname = '' OR email = '')", array($cuttime)); foreach ($rs as $user) { if (isguestuser($user) or is_siteadmin($user)) { continue; } 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. $loglifetime = get_config('backup', 'loglifetime'); if (!empty($loglifetime)) { // Value in days. $loglifetime = $timenow - $loglifetime * 3600 * 24; // Delete child records from backup_logs. $DB->execute("DELETE FROM {backup_logs}\n WHERE EXISTS (\n SELECT 'x'\n FROM {backup_controllers} bc\n WHERE bc.backupid = {backup_logs}.backupid\n AND bc.timecreated < ?)", array($loglifetime)); // Delete records from backup_controllers. $DB->execute("DELETE FROM {backup_controllers}\n 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(' Deleting temporary files...'); cron_delete_from_temp(); // Cleanup user password reset records // Delete any reset request records which are expired by more than a day. // (We keep recently expired requests around so we can give a different error msg to users who // are trying to user a recently expired reset attempt). $pwresettime = isset($CFG->pwresettime) ? $CFG->pwresettime : 1800; $earliestvalid = time() - $pwresettime - DAYSECS; $DB->delete_records_select('user_password_resets', "timerequested < ?", array($earliestvalid)); mtrace(' Cleaned up old password reset records'); 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 (notify_login_failures()) { mtrace(' Notified login failures'); } // 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'); // Session gc. mtrace("Running session gc tasks..."); \core\session\manager::gc(); mtrace("...finished stale session cleanup"); // 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..."); cron_trace_time_and_memory(); 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...'); $usernamefields = get_all_user_name_fields(true, 'u'); $newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email,\n {$usernamefields}, u.username, u.lang,\n p.id as prefid\n FROM {user} u\n JOIN {user_preferences} p ON u.id=p.userid\n WHERE p.name='create_password' AND p.value='1' AND u.email !='' AND u.suspended = 0 AND u.auth != 'nologin' AND u.deleted = 0"); // note: we can not send emails to suspended accounts foreach ($newusers as $newuser) { // Use a low cost factor when generating bcrypt hash otherwise // hashing would be slow when emailing lots of users. Hashes // will be automatically updated to a higher cost factor the first // time the user logs in. if (setnew_password_and_mail($newuser, true)) { 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}..."); cron_trace_time_and_memory(); $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))) { foreach ($mods as $mod) { $libfile = "{$CFG->dirroot}/mod/{$mod->name}/lib.php"; if (file_exists($libfile)) { include_once $libfile; $cron_function = $mod->name . "_cron"; if (function_exists($cron_function)) { mtrace("Processing module function {$cron_function} ...", ''); cron_trace_time_and_memory(); $pre_dbqueries = null; $pre_dbqueries = $DB->perf_get_queries(); $pre_time = microtime(1); if ($cron_function()) { $DB->set_field("modules", "lastcron", $timenow, array("id" => $mod->id)); } if (isset($pre_dbqueries)) { 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 @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. require_once $CFG->dirroot . '/blocks/moodleblock.class.php'; foreach ($blocks as $block) { $blockfile = $CFG->dirroot . '/blocks/' . $block->name . '/block_' . $block->name . '.php'; if (file_exists($blockfile)) { require_once $blockfile; $classname = 'block_' . $block->name; $blockobj = new $classname(); if (method_exists($blockobj, 'cron')) { mtrace("Processing cron function for " . $block->name . '....', ''); cron_trace_time_and_memory(); if ($blockobj->cron()) { $DB->set_field('block', 'lastcron', $timenow, array('id' => $block->id)); } // Reset possible changes by blocks to time_limit. MDL-11597 @set_time_limit(0); mtrace('done.'); } } } } mtrace('Finished blocks'); mtrace('Starting admin reports'); cron_execute_plugin_type('report'); mtrace('Finished admin reports'); mtrace('Starting main gradebook job...'); cron_trace_time_and_memory(); grade_cron(); mtrace('done.'); mtrace('Starting processing the event queue...'); cron_trace_time_and_memory(); events_cron(); mtrace('done.'); if ($CFG->enablecompletion) { // Completion cron mtrace('Starting the completion cron...'); cron_trace_time_and_memory(); require_once $CFG->dirroot . '/completion/cron.php'; completion_cron(); mtrace('done'); } if ($CFG->enableportfolios) { // Portfolio cron mtrace('Starting the portfolio cron...'); cron_trace_time_and_memory(); require_once $CFG->libdir . '/portfoliolib.php'; portfolio_cron(); mtrace('done'); } //now do plagiarism checks require_once $CFG->libdir . '/plagiarismlib.php'; plagiarism_cron(); 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('gradeexport'); cron_execute_plugin_type('gradereport'); mtrace('Finished gradebook plugins'); // run calendar cron require_once "{$CFG->dirroot}/calendar/lib.php"; calendar_cron(); // Run external blog cron if needed if (!empty($CFG->enableblogs) && $CFG->useexternalblogs) { require_once $CFG->dirroot . '/blog/lib.php'; mtrace("Fetching external blog entries...", ''); cron_trace_time_and_memory(); $sql = "timefetched < ? OR timefetched = 0"; $externalblogs = $DB->get_records_select('blog_external', $sql, array(time() - $CFG->externalblogcrontime)); foreach ($externalblogs as $eb) { blog_sync_external_entries($eb); } mtrace('done.'); } // Run blog associations cleanup if (!empty($CFG->enableblogs) && $CFG->useblogassociations) { require_once $CFG->dirroot . '/blog/lib.php'; // delete entries whose contextids no longer exists mtrace("Deleting blog associations linked to non-existent contexts...", ''); cron_trace_time_and_memory(); $DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})'); mtrace('done.'); } // Run question bank clean-up. mtrace("Starting the question bank cron...", ''); cron_trace_time_and_memory(); require_once $CFG->libdir . '/questionlib.php'; question_bank::cron(); mtrace('done.'); //Run registration updated cron mtrace(get_string('siteupdatesstart', 'hub')); cron_trace_time_and_memory(); require_once $CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php'; $registrationmanager = new registration_manager(); $registrationmanager->cron(); mtrace(get_string('siteupdatesend', 'hub')); // If enabled, fetch information about available updates and eventually notify site admins if (empty($CFG->disableupdatenotifications)) { $updateschecker = \core\update\checker::instance(); $updateschecker->cron(); } //cleanup old session linked tokens //deletes the session linked tokens that are over a day old. mtrace("Deleting session linked tokens more than one day old...", ''); cron_trace_time_and_memory(); $DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype', 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('editor', 'editors'); cron_execute_plugin_type('format', 'course formats'); cron_execute_plugin_type('profilefield', 'profile fields'); cron_execute_plugin_type('webservice', 'webservices'); cron_execute_plugin_type('repository', 'repository plugins'); cron_execute_plugin_type('qbehaviour', 'question behaviours'); cron_execute_plugin_type('qformat', 'question import/export formats'); cron_execute_plugin_type('qtype', 'question types'); cron_execute_plugin_type('plagiarism', 'plagiarism plugins'); cron_execute_plugin_type('theme', 'themes'); cron_execute_plugin_type('tool', 'admin tools'); // and finally run any local cronjobs, if any if ($locals = core_component::get_plugin_list('local')) { mtrace('Processing customized cron scripts ...', ''); // new cron functions in lib.php first cron_execute_plugin_type('local'); // legacy cron files are executed directly foreach ($locals as $local => $localdir) { if (file_exists("{$localdir}/cron.php")) { include "{$localdir}/cron.php"; } } mtrace('done.'); } mtrace('Running cache cron routines'); cache_helper::cron(); 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)); } } // Run badges review cron. mtrace("Starting badges cron..."); require_once $CFG->dirroot . '/badges/cron.php'; badge_cron(); mtrace('done.'); // cleanup file trash - not very important $fs = get_file_storage(); $fs->cron(); mtrace("Cron script completed correctly"); gc_collect_cycles(); mtrace('Cron completed at ' . date('H:i:s') . '. Memory used ' . display_size(memory_get_usage()) . '.'); $difftime = microtime_diff($starttime, microtime()); mtrace("Execution took " . $difftime . " seconds"); }
/** * Quiz periodic clean-up tasks. */ function quiz_cron() { global $CFG; require_once $CFG->dirroot . '/mod/quiz/cronlib.php'; mtrace(''); $timenow = time(); $overduehander = new mod_quiz_overdue_attempt_updater(); $processto = $timenow - get_config('quiz', 'graceperiodmin'); mtrace(' Looking for quiz overdue quiz attempts...'); list($count, $quizcount) = $overduehander->update_overdue_attempts($timenow, $processto); mtrace(' Considered ' . $count . ' attempts in ' . $quizcount . ' quizzes.'); // Run cron for our sub-plugin types. cron_execute_plugin_type('quiz', 'quiz reports'); cron_execute_plugin_type('quizaccess', 'quiz access rules'); return true; }
/** * Quiz periodic clean-up tasks. */ function quiz_cron() { global $CFG; mtrace(''); // Since the quiz specifies $module->cron = 60, so that the subplugins can // have frequent cron if they need it, we now need to do our own scheduling. $quizconfig = get_config('quiz'); if (!isset($quizconfig->overduelastrun)) { $quizconfig->overduelastrun = 0; $quizconfig->overduedoneto = 0; } $timenow = time(); if ($timenow > $quizconfig->overduelastrun + 3600) { require_once($CFG->dirroot . '/mod/quiz/cronlib.php'); $overduehander = new mod_quiz_overdue_attempt_updater(); $processto = $timenow - $quizconfig->graceperiodmin; mtrace(' Looking for quiz overdue quiz attempts between ' . userdate($quizconfig->overduedoneto) . ' and ' . userdate($processto) . '...'); list($count, $quizcount) = $overduehander->update_overdue_attempts($timenow, $quizconfig->overduedoneto, $processto); set_config('overduelastrun', $timenow, 'quiz'); set_config('overduedoneto', $processto, 'quiz'); mtrace(' Considered ' . $count . ' attempts in ' . $quizcount . ' quizzes.'); } // Run cron for our sub-plugin types. cron_execute_plugin_type('quiz', 'quiz reports'); cron_execute_plugin_type('quizaccess', 'quiz access rules'); return true; }
/** * Function to be run periodically according to the moodle cron * This function searches for things that need to be done, such * as sending out mail, toggling flags etc ... * * Note: The evaluation of answer forms is done by a separate cron job using the script mod/offlinequiz/cron.php. * **/ function offlinequiz_cron() { global $DB; cron_execute_plugin_type('offlinequiz', 'offlinequiz reports'); // Remove all saved hotspot data that is older than 7 days. $timenow = time(); // We have to make sure we do this atomic for each scanned page. $sql = "SELECT DISTINCT(scannedpageid)\n FROM {offlinequiz_hotspots}\n WHERE time < :expiretime"; $params = array('expiretime' => $timenow - 604800); // First we get the different IDs. $ids = $DB->get_fieldset_sql($sql, $params); if (!empty($ids)) { list($isql, $iparams) = $DB->get_in_or_equal($ids); // Now we delete the records. $DB->delete_records_select('offlinequiz_hotspots', 'scannedpageid ' . $isql, $iparams); } // Delete old temporary files not needed any longer. $keepdays = get_config('offlinequiz', 'keepfilesfordays'); $keepseconds = $keepdays * 24 * 60 * 60; $sql = "SELECT id\n FROM {offlinequiz_queue}\n WHERE timecreated < :expiretime"; $params = array('expiretime' => $timenow - $keepseconds); // First we get the IDs of cronjobs older than the configured number of days. $jobids = $DB->get_fieldset_sql($sql, $params); foreach ($jobids as $jobid) { $dirname = null; // Delete all temporary files and the database entries. if ($files = $DB->get_records('offlinequiz_queue_data', array('queueid' => $jobid))) { foreach ($files as $file) { if (empty($dirname)) { $pathparts = pathinfo($file->filename); $dirname = $pathparts['dirname']; } $DB->delete_records('offlinequiz_queue_data', array('id' => $file->id)); } // Remove the temporary directory. echo "Removing dir " . $dirname . "\n"; remove_dir($dirname); } } return true; }
/** * Do the job. * Throw exceptions on errors (the job will be retried). */ public function execute() { global $CFG, $DB; $timenow = time(); // 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); } // 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"); if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) { foreach ($mods as $mod) { $libfile = "{$CFG->dirroot}/mod/{$mod->name}/lib.php"; if (file_exists($libfile)) { include_once $libfile; $cronfunction = $mod->name . "_cron"; if (function_exists($cronfunction)) { mtrace("Processing module function {$cronfunction} ...\n", ''); $predbqueries = null; $predbqueries = $DB->perf_get_queries(); $pretime = microtime(1); if ($cronfunction()) { $DB->set_field("modules", "lastcron", $timenow, array("id" => $mod->id)); } if (isset($predbqueries)) { mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries"); mtrace("... used " . (microtime(1) - $pretime) . " seconds"); } // Reset possible changes by modules to time_limit. MDL-11597. \core_php_time_limit::raise(); mtrace("done."); } } } } 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. require_once $CFG->dirroot . '/blocks/moodleblock.class.php'; foreach ($blocks as $block) { $blockfile = $CFG->dirroot . '/blocks/' . $block->name . '/block_' . $block->name . '.php'; if (file_exists($blockfile)) { require_once $blockfile; $classname = '\\block_' . $block->name; $blockobj = new $classname(); if (method_exists($blockobj, 'cron')) { mtrace("Processing cron function for " . $block->name . '....', ''); if ($blockobj->cron()) { $DB->set_field('block', 'lastcron', $timenow, array('id' => $block->id)); } // Reset possible changes by blocks to time_limit. MDL-11597. \core_php_time_limit::raise(); mtrace('done.'); } } } } mtrace('Finished blocks'); mtrace('Starting admin reports'); cron_execute_plugin_type('report'); mtrace('Finished admin reports'); 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('gradeexport'); cron_execute_plugin_type('gradereport'); mtrace('Finished gradebook plugins'); // All other plugins. cron_execute_plugin_type('message', 'message plugins'); cron_execute_plugin_type('filter', 'filters'); cron_execute_plugin_type('editor', 'editors'); cron_execute_plugin_type('format', 'course formats'); cron_execute_plugin_type('profilefield', 'profile fields'); cron_execute_plugin_type('webservice', 'webservices'); cron_execute_plugin_type('repository', 'repository plugins'); cron_execute_plugin_type('qbehaviour', 'question behaviours'); cron_execute_plugin_type('qformat', 'question import/export formats'); cron_execute_plugin_type('qtype', 'question types'); cron_execute_plugin_type('plagiarism', 'plagiarism plugins'); cron_execute_plugin_type('theme', 'themes'); cron_execute_plugin_type('tool', 'admin tools'); cron_execute_plugin_type('local', 'local plugins'); }
/** * Cron functions. * * @package core * @subpackage admin * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ function cron_run() { global $DB, $CFG, $OUTPUT; if (CLI_MAINTENANCE) { echo "CLI maintenance mode active, cron execution suspended.\n"; exit(1); } if (moodle_needs_upgrading()) { echo "Moodle upgrade pending, cron execution suspended.\n"; exit(1); } require_once $CFG->libdir . '/adminlib.php'; require_once $CFG->libdir . '/gradelib.php'; if (!empty($CFG->showcronsql)) { $DB->set_debug(true); } if (!empty($CFG->showcrondebugging)) { $CFG->debug = DEBUG_DEVELOPER; $CFG->debugdisplay = true; } set_time_limit(0); $starttime = microtime(); /// increase memory limit raise_memory_limit(MEMORY_EXTRA); /// emulate normal session cron_setup_user(); /// Start output log $timenow = time(); mtrace("Server Time: " . date('r', $timenow) . "\n\n"); /// Session gc mtrace("Cleaning up stale sessions"); session_gc(); /// 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))) { foreach ($mods as $mod) { $libfile = "{$CFG->dirroot}/mod/{$mod->name}/lib.php"; if (file_exists($libfile)) { include_once $libfile; $cron_function = $mod->name . "_cron"; if (function_exists($cron_function)) { mtrace("Processing module function {$cron_function} ...", ''); $pre_dbqueries = null; $pre_dbqueries = $DB->perf_get_queries(); $pre_time = microtime(1); if ($cron_function()) { $DB->set_field("modules", "lastcron", $timenow, array("id" => $mod->id)); } if (isset($pre_dbqueries)) { 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 @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. require_once $CFG->dirroot . '/blocks/moodleblock.class.php'; foreach ($blocks as $block) { $blockfile = $CFG->dirroot . '/blocks/' . $block->name . '/block_' . $block->name . '.php'; if (file_exists($blockfile)) { require_once $blockfile; $classname = 'block_' . $block->name; $blockobj = new $classname(); if (method_exists($blockobj, 'cron')) { mtrace("Processing cron function for " . $block->name . '....', ''); if ($blockobj->cron()) { $DB->set_field('block', 'lastcron', $timenow, array('id' => $block->id)); } /// Reset possible changes by blocks to time_limit. MDL-11597 @set_time_limit(0); mtrace('done.'); } } } } mtrace('Finished blocks'); mtrace("Starting quiz reports"); if ($reports = $DB->get_records_select('quiz_reports', "cron > 0 AND ((? - lastcron) > cron)", array($timenow))) { foreach ($reports as $report) { $cronfile = "{$CFG->dirroot}/mod/quiz/report/{$report->name}/cron.php"; if (file_exists($cronfile)) { include_once $cronfile; $cron_function = 'quiz_report_' . $report->name . "_cron"; if (function_exists($cron_function)) { mtrace("Processing quiz report cron function {$cron_function} ...", ''); $pre_dbqueries = null; $pre_dbqueries = $DB->perf_get_queries(); $pre_time = microtime(1); if ($cron_function()) { $DB->set_field('quiz_reports', "lastcron", $timenow, array("id" => $report->id)); } if (isset($pre_dbqueries)) { mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries"); mtrace("... used " . (microtime(1) - $pre_time) . " seconds"); } mtrace("done."); } } } } mtrace("Finished quiz reports"); mtrace('Starting admin reports'); cron_execute_plugin_type('report'); mtrace('Finished admin reports'); mtrace('Starting main gradebook job ...'); grade_cron(); mtrace('done.'); mtrace('Starting processing the event queue...'); events_cron(); mtrace('done.'); if ($CFG->enablecompletion) { // Completion cron mtrace('Starting the completion cron...'); require_once $CFG->libdir . '/completion/cron.php'; completion_cron(); mtrace('done'); } if ($CFG->enableportfolios) { // Portfolio cron mtrace('Starting the portfolio cron...'); require_once $CFG->libdir . '/portfoliolib.php'; portfolio_cron(); 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\n FROM {user}\n WHERE confirmed = 0 AND firstaccess > 0\n 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 *\n FROM {user}\n WHERE confirmed = 1 AND lastaccess > 0\n AND lastaccess < ? AND deleted = 0\n 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}\n WHERE EXISTS (\n SELECT 'x'\n FROM {backup_controllers} bc\n WHERE bc.backupid = {backup_logs}.backupid\n AND bc.timecreated < ?)", array($loglifetime)); // Delete records from backup_controllers $DB->execute("DELETE FROM {backup_controllers}\n 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,\n u.lastname, u.username,\n p.id as prefid\n FROM {user} u\n JOIN {user_preferences} p ON u.id=p.userid\n 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('gradeexport'); 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("Fetching external blog entries...", ''); $sql = "timefetched < ? OR timefetched = 0"; $externalblogs = $DB->get_records_select('blog_external', $sql, array(mktime() - $CFG->externalblogcrontime)); foreach ($externalblogs as $eb) { blog_sync_external_entries($eb); } mtrace('done.'); } // Run blog associations cleanup if ($CFG->useblogassociations) { require_once $CFG->dirroot . '/blog/lib.php'; // delete entries whose contextids no longer exists mtrace("Deleting blog associations linked to non-existent contexts...", ''); $DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})'); mtrace('done.'); } //Run registration updated cron mtrace(get_string('siteupdatesstart', 'hub')); require_once $CFG->dirroot . '/admin/registration/lib.php'; $registrationmanager = new registration_manager(); $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. mtrace("Deleting session linked tokens more than one day old...", ''); $DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype', 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('editor', 'editors'); cron_execute_plugin_type('format', 'course formats'); cron_execute_plugin_type('profilefield', 'profile fields'); cron_execute_plugin_type('webservice', 'webservices'); // TODO: Repository lib.php files are messed up (include many other files, etc), so it is // currently not possible to implement repository plugin cron using this infrastructure // cron_execute_plugin_type('repository', 'repository plugins'); cron_execute_plugin_type('qtype', 'question types'); cron_execute_plugin_type('plagiarism', 'plagiarism plugins'); cron_execute_plugin_type('theme', 'themes'); // and finally run any local cronjobs, if any if ($locals = get_plugin_list('local')) { mtrace('Processing customized cron scripts ...', ''); // new cron functions in lib.php first cron_execute_plugin_type('local'); // legacy cron files are executed directly foreach ($locals as $local => $localdir) { if (file_exists("{$localdir}/cron.php")) { include "{$localdir}/cron.php"; } } mtrace('done.'); } mtrace("Cron script completed correctly"); $difftime = microtime_diff($starttime, microtime()); mtrace("Execution took " . $difftime . " seconds"); }
/** * Quiz periodic clean-up tasks. */ function quiz_cron() { // Run cron for our sub-plugin types. cron_execute_plugin_type('quiz', 'quiz reports'); cron_execute_plugin_type('quizaccess', 'quiz access rules'); }
/** * Regular jobs to execute via cron * * @return boolean true on success, false otherwise */ function teamwork_cron() { global $CFG, $DB; $now = time(); mtrace(' processing teamwork subplugins ...'); cron_execute_plugin_type('teamworkallocation', 'teamwork allocation methods'); // now when the scheduled allocator had a chance to do its job, check if there // are some teamworks to switch into the assessment phase $teamworks = $DB->get_records_select("teamwork", "phase = 20 AND phaseswitchassessment = 1 AND submissionend > 0 AND submissionend < ?", array($now)); if (!empty($teamworks)) { mtrace('Processing automatic assessment phase switch in ' . count($teamworks) . ' teamwork(s) ... ', ''); require_once $CFG->dirroot . '/mod/teamwork/locallib.php'; foreach ($teamworks as $teamwork) { $cm = get_coursemodule_from_instance('teamwork', $teamwork->id, $teamwork->course, false, MUST_EXIST); $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); $teamwork = new teamwork($teamwork, $cm, $course); $teamwork->switch_phase(teamwork::PHASE_ASSESSMENT); $params = array('objectid' => $teamwork->id, 'context' => $teamwork->context, 'courseid' => $teamwork->course->id, 'other' => array('teamworkphase' => $teamwork->phase)); $event = \mod_teamwork\event\phase_switched::create($params); $event->trigger(); // disable the automatic switching now so that it is not executed again by accident // if the teacher changes the phase back to the submission one $DB->set_field('teamwork', 'phaseswitchassessment', 0, array('id' => $teamwork->id)); // todo inform the teachers } mtrace('done'); } return true; }