moodle/admin/cron.php

595 lines
23 KiB
PHP
Raw Normal View History

<?php
2001-11-22 06:23:56 +00:00
2003-04-27 06:21:15 +00:00
/// This script looks through all the module directories for cron.php files
/// and runs them. These files can contain cleanup functions, email functions
/// or anything that needs to be run on a regular basis.
///
/// This file is best run from cron on the host system (ie outside PHP).
/// The script can either be invoked via the web server or via a standalone
/// version of PHP compiled for CGI.
///
/// eg wget -q -O /dev/null 'http://moodle.somewhere.edu/admin/cron.php'
/// or php /web/moodle/admin/cron.php
2007-05-23 06:02:59 +00:00
set_time_limit(0);
2003-02-01 01:36:31 +00:00
$starttime = microtime();
/// this cron script might be considered to be a CLI script even when accessed over HTTP,
/// we do not want HTML in output and there is no real session ;-)
define('CLI_SCRIPT', true);
/// Following hack used to identify cron and other CLI scripts - use CLI_SCRIPT or $FULLME == "/$CFG->admin/cropn.php" instead
//define('FULLME', 'cron');
/// Do not set moodle cookie because we do not need it here, it is better to emulate session
define('NO_MOODLE_COOKIES', true);
require_once(dirname(__FILE__) . '/../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/gradelib.php');
2001-11-22 06:23:56 +00:00
/// Extra debugging (set in config.php)
if (!empty($CFG->showcronsql)) {
$DB->set_debug(true);
}
if (!empty($CFG->showcrondebugging)) {
$CFG->debug = DEBUG_DEVELOPER;
$CFG->debugdisplay = true;
}
/// extra safety
session_get_instance()->write_close();
/// check if execution allowed
if (isset($_SERVER['REMOTE_ADDR'])) { // if the script is accessed via the web.
if (!empty($CFG->cronclionly)) {
// This script can only be run via the cli.
print_error('cronerrorclionly', 'admin');
exit;
}
// This script is being called via the web, so check the password if there is one.
if (!empty($CFG->cronremotepassword)) {
$pass = optional_param('password', '', PARAM_RAW);
if($pass != $CFG->cronremotepassword) {
// wrong password.
print_error('cronerrorpassword', 'admin');
exit;
}
}
}
/// emulate normal session
cron_setup_user();
/// send mime type and encoding
if (check_browser_version('MSIE')) {
//ugly IE hack to work around downloading instead of viewing
@header('Content-Type: text/html; charset=utf-8');
echo "<xmp>"; //<pre> is not good enough for us here
} else {
//send proper plaintext header
@header('Content-Type: text/plain; charset=utf-8');
}
/// no more headers and buffers
while(@ob_end_flush());
/// increase memory limit (PHP 5.2 does different calculation, we need more memory now)
@raise_memory_limit('128M');
2004-08-07 05:56:03 +00:00
/// Start output log
2001-11-22 06:23:56 +00:00
$timenow = time();
2004-08-07 05:56:03 +00:00
mtrace("Server Time: ".date('r',$timenow)."\n\n");
2001-11-22 06:23:56 +00:00
/// Session gc
mtrace("Cleaning up stale sessions");
2009-01-18 18:00:44 +00:00
session_gc();
/// Run all cron jobs for each module
mtrace("Starting activity modules");
get_mailer('buffer');
2008-05-30 21:16:16 +00:00
if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
2001-11-22 06:23:56 +00:00
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()) {
2008-05-30 21:16:16 +00:00
if (!$DB->set_field("modules", "lastcron", $timenow, array("id"=>$mod->id))) {
mtrace("Error: could not update timestamp for $mod->fullname");
}
}
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.");
2001-11-22 06:23:56 +00:00
}
}
}
}
get_mailer('close');
mtrace("Finished activity modules");
mtrace("Starting blocks");
2008-05-30 21:16:16 +00:00
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()) {
2008-05-30 21:16:16 +00:00
if (!$DB->set_field('block', 'lastcron', $timenow, array('id'=>$block->id))) {
mtrace('Error: could not update timestamp for '.$block->name);
}
}
/// 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_report', "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()) {
if (!$DB->set_field('quiz_report', "lastcron", $timenow, array("id"=>$report->id))) {
mtrace("Error: could not update timestamp for $report->name");
}
}
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');
// Admin reports do not have a database table that lists them. Instead a
// report includes cron.php with function report_reportname_cron() if it wishes
// to be cronned. It is up to cron.php to handle e.g. if it only needs to
// actually do anything occasionally.
$reports = get_plugin_list('report');
foreach($reports as $report => $reportdir) {
$cronfile = $reportdir.'/cron.php';
if (file_exists($cronfile)) {
require_once($cronfile);
$cronfunction = 'report_'.$report.'_cron';
mtrace('Processing cron function for '.$report.'...', '');
$pre_dbqueries = null;
$pre_dbqueries = $DB->perf_get_queries();
$pre_time = microtime(true);
$cronfunction();
if (isset($pre_dbqueries)) {
mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
mtrace("... used " . round(microtime(true) - $pre_time, 2) . " seconds");
}
mtrace('done.');
}
}
mtrace('Finished admin reports');
if (!empty($CFG->langcache)) {
mtrace('Updating languages cache');
get_list_of_languages(true);
}
mtrace('Removing expired enrolments ...', ''); // See MDL-8785
$timenow = time();
$somefound = false;
// The preferred way saves memory, datablib
// find courses where limited enrolment is enabled
2008-05-30 21:16:16 +00:00
$sql = "SELECT ra.roleid, ra.userid, ra.contextid
FROM {course} c
JOIN {context} cx ON cx.instanceid = c.id
JOIN {role_assignments} ra ON ra.contextid = cx.id
WHERE cx.contextlevel = '".CONTEXT_COURSE."'
AND ra.timeend > 0
AND ra.timeend < ?
AND c.enrolperiod > 0";
if ($rs = $DB->get_recordset_sql($sql, array($timenow))) {
foreach ($rs as $oldenrolment) {
role_unassign($oldenrolment->roleid, $oldenrolment->userid, 0, $oldenrolment->contextid);
$somefound = true;
}
$rs->close();
}
2008-05-30 21:16:16 +00:00
if ($somefound) {
mtrace('Done');
} else {
mtrace('none found');
}
mtrace('Starting main gradebook job ...');
grade_cron();
mtrace('done.');
mtrace('Starting processing the event queue...');
events_cron();
mtrace('done.');
if ($CFG->enableportfolios) {
// Portfolio cron
mtrace('Starting the portfolio cron...');
require_once($CFG->libdir . '/portfoliolib.php');
portfolio_cron();
mtrace('done');
}
/// 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...");
/// Unenrol users who haven't logged in for $CFG->longtimenosee
if ($CFG->longtimenosee) { // value in days
$cuttime = $timenow - ($CFG->longtimenosee * 3600 * 24);
2008-05-30 21:16:16 +00:00
$rs = $DB->get_recordset_sql ("SELECT id, userid, courseid
FROM {user_lastaccess}
WHERE courseid != ".SITEID."
AND timeaccess < ?", array($cuttime));
foreach ($rs as $assign) {
if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
if (role_unassign(0, $assign->userid, 0, $context->id)) {
mtrace("removing user $assign->userid from course $assign->courseid as they have not accessed the course for over $CFG->longtimenosee days");
}
}
}
2008-05-30 21:16:16 +00:00
$rs->close();
/// Execute the same query again, looking for remaining records and deleting them
/// if the user hasn't moodle/course:view in the CONTEXT_COURSE context (orphan records)
2008-05-30 21:16:16 +00:00
$rs = $DB->get_recordset_sql ("SELECT id, userid, courseid
FROM {user_lastaccess}
WHERE courseid != ".SITEID."
AND timeaccess < ?", array($cuttime));
foreach ($rs as $assign) {
if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
if (!has_capability('moodle/course:view', $context, $assign->userid)) {
2008-05-30 21:16:16 +00:00
$DB->delete_records('user_lastaccess', array('userid'=>$assign->userid, 'courseid'=>$assign->courseid));
mtrace("Deleted orphan user_lastaccess for user $assign->userid from course $assign->courseid");
}
}
}
2008-05-30 21:16:16 +00:00
$rs->close();
}
flush();
/// Delete users who haven't confirmed within required period
if (!empty($CFG->deleteunconfirmed)) {
$cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
2008-05-30 21:16:16 +00:00
$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)");
2002-08-05 08:40:00 +00:00
}
}
2008-05-30 21:16:16 +00:00
$rs->close();
}
flush();
/// Delete users who haven't completed profile within required period
if (!empty($CFG->deleteincompleteusers)) {
$cuttime = $timenow - ($CFG->deleteincompleteusers * 3600);
2008-05-30 21:16:16 +00:00
$rs = $DB->get_recordset_sql ("SELECT id, username
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)");
}
}
2008-05-30 21:16:16 +00:00
$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);
2008-05-30 21:16:16 +00:00
if ($DB->delete_records_select("log", "time < ?", array($loglifetime))) {
mtrace("Deleted old log records");
}
}
flush();
2004-02-09 12:33:02 +00:00
2004-02-09 12:33:02 +00:00
/// 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
2008-05-30 21:16:16 +00:00
if ($DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime))) {
mtrace("Deleted old cache_text records");
}
2004-02-09 12:33:02 +00:00
}
flush();
if (!empty($CFG->notifyloginfailures)) {
notify_login_failures();
2007-10-07 17:28:46 +00:00
mtrace('Notified login failured');
}
flush();
sync_metacourses();
2007-10-07 17:28:46 +00:00
mtrace('Synchronised metacourses');
//
// generate new password emails for users
//
mtrace('checking for create_password');
2008-05-30 21:16:16 +00:00
if ($DB->count_records('user_preferences', array('name'=>'create_password', 'value'=>'1'))) {
mtrace('creating passwords for new users');
$newusers = $DB->get_records_sql("SELECT u.id as id, u.email, u.firstname,
2008-05-30 21:16:16 +00:00
u.lastname, u.username,
p.id as prefid
FROM {user} u
2008-05-30 21:16:16 +00:00
JOIN {user_preferences} p ON u.id=p.userid
WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
foreach ($newusers as $newuserid => $newuser) {
$newuser->emailstop = 0; // send email regardless
// email user
if (setnew_password_and_mail($newuser)) {
// remove user pref
2008-05-30 21:16:16 +00:00
$DB->delete_records('user_preferences', array('id'=>$newuser->prefid));
} else {
trigger_error("Could not create and mail new user password!");
}
}
}
2008-05-01 05:49:35 +00:00
if (!empty($CFG->usetags)) {
2007-08-04 01:51:18 +00:00
require_once($CFG->dirroot.'/tag/lib.php');
tag_cron();
2007-10-07 17:28:46 +00:00
mtrace ('Executed tag cron');
2007-08-04 01:51:18 +00:00
}
// Accesslib stuff
cleanup_contexts();
2007-10-07 17:28:46 +00:00
mtrace ('Cleaned up contexts');
gc_cache_flags();
2007-10-07 17:28:46 +00:00
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();
2007-10-07 17:28:46 +00:00
mtrace ('Built context paths');
mtrace("Finished clean-up tasks...");
} // End of occasional clean-up tasks
2003-04-27 06:21:15 +00:00
if (empty($CFG->disablescheduledbackups)) { // Defined in config.php
//Execute backup's cron
//Perhaps a long time and memory could help in large sites
@set_time_limit(0);
@raise_memory_limit("192M");
if (function_exists('apache_child_terminate')) {
// if we are running from Apache, give httpd a hint that
// it can recycle the process after it's done. Apache's
// memory management is truly awful but we can help it.
@apache_child_terminate();
}
if (file_exists("$CFG->dirroot/backup/backup_scheduled.php") and
file_exists("$CFG->dirroot/backup/backuplib.php") and
file_exists("$CFG->dirroot/backup/lib.php") and
file_exists("$CFG->libdir/blocklib.php")) {
include_once("$CFG->dirroot/backup/backup_scheduled.php");
include_once("$CFG->dirroot/backup/backuplib.php");
include_once("$CFG->dirroot/backup/lib.php");
mtrace("Running backups if required...");
if (! schedule_backup_cron()) {
mtrace("ERROR: Something went wrong while performing backup tasks!!!");
} else {
mtrace("Backup tasks finished.");
}
}
}
if (!empty($CFG->enablerssfeeds)) { //Defined in admin/variables page
2005-01-25 17:08:05 +00:00
include_once("$CFG->libdir/rsslib.php");
mtrace("Running rssfeeds if required...");
2005-01-25 17:08:05 +00:00
if ( ! cron_rss_feeds()) {
mtrace("Something went wrong while generating rssfeeds!!!");
} else {
mtrace("Rssfeeds finished");
}
}
/// 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);
}
/// Run the enrolment cron, if any
if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
$plugins = array($CFG->enrol);
}
require_once($CFG->dirroot .'/enrol/enrol.class.php');
foreach ($plugins as $p) {
$enrol = enrolment_factory::factory($p);
if (method_exists($enrol, 'cron')) {
$enrol->cron();
}
if (!empty($enrol->log)) {
mtrace($enrol->log);
}
unset($enrol);
}
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);
2008-02-18 03:13:46 +00:00
} else {
mtrace('Next stats run after:'. userdate($timetocheck));
}
}
// run gradebook import/export/report cron
if ($gradeimports = get_plugin_list('gradeimport')) {
foreach ($gradeimports as $gradeimport => $plugindir) {
if (file_exists($plugindir.'/lib.php')) {
require_once($plugindir.'/lib.php');
$cron_function = 'grade_import_'.$gradeimport.'_cron';
if (function_exists($cron_function)) {
mtrace("Processing gradebook import function $cron_function ...", '');
$cron_function;
}
}
}
}
if ($gradeexports = get_plugin_list('gradeexport')) {
foreach ($gradeexports as $gradeexport => $plugindir) {
if (file_exists($plugindir.'/lib.php')) {
require_once($plugindir.'/lib.php');
$cron_function = 'grade_export_'.$gradeexport.'_cron';
if (function_exists($cron_function)) {
mtrace("Processing gradebook export function $cron_function ...", '');
$cron_function;
}
}
}
}
if ($gradereports = get_plugin_list('gradereport')) {
foreach ($gradereports as $gradereport => $plugindir) {
if (file_exists($plugindir.'/lib.php')) {
require_once($plugindir.'/lib.php');
$cron_function = 'grade_report_'.$gradereport.'_cron';
if (function_exists($cron_function)) {
mtrace("Processing gradebook report function $cron_function ...", '');
$cron_function;
}
}
}
}
2009-09-04 00:36:43 +00:00
// 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));
2009-09-04 00:36:43 +00:00
foreach ($external_blogs as $eb) {
blog_sync_external_entries($eb);
2009-09-04 00:36:43 +00:00
}
}
// 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})');
}
// cleanup file trash
$fs = get_file_storage();
$fs->cron();
2007-11-30 02:00:04 +00:00
// run any customized cronjobs, if any
if ($locals = get_plugin_list('local')) {
mtrace('Processing customized cron scripts ...', '');
foreach ($locals as $local => $localdir) {
if (file_exists("$localdir/cron.php")) {
include("$localdir/cron.php");
}
}
2007-11-30 02:00:04 +00:00
mtrace('done.');
}
mtrace("Cron script completed correctly");
2001-11-22 06:23:56 +00:00
2003-02-01 01:36:31 +00:00
$difftime = microtime_diff($starttime, microtime());
mtrace("Execution took ".$difftime." seconds");
2003-02-01 01:36:31 +00:00
2007-11-21 16:28:45 +00:00
/// finish the IE hack
if (check_browser_version('MSIE')) {
echo "</xmp>";
}