MDL-13663 Docs updated. Merging from MOODLE_19_STABLE
[moodle.git] / admin / cron.php
CommitLineData
fce3ef94 1<?php // $Id$
f9903ed0 2
540bab47 3/// This script looks through all the module directories for cron.php files
4/// and runs them. These files can contain cleanup functions, email functions
5/// or anything that needs to be run on a regular basis.
6///
7/// This file is best run from cron on the host system (ie outside PHP).
8/// The script can either be invoked via the web server or via a standalone
9/// version of PHP compiled for CGI.
10///
11/// eg wget -q -O /dev/null 'http://moodle.somewhere.edu/admin/cron.php'
12/// or php /web/moodle/admin/cron.php
168df8f3 13 set_time_limit(0);
498d4032 14 $starttime = microtime();
f9f4d999 15
16/// The following is a hack necessary to allow this script to work well
17/// from the command line.
18
19 define('FULLME', 'cron');
9152fc99 20
21
22/// Do not set moodle cookie because we do not need it here, it is better to emulate session
23 $nomoodlecookie = true;
24
d5f286f1 25/// The current directory in PHP version 4.3.0 and above isn't necessarily the
26/// directory of the script when run from the command line. The require_once()
27/// would fail, so we'll have to chdir()
28
29 if (!isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['argv'][0])) {
30 chdir(dirname($_SERVER['argv'][0]));
31 }
498d4032 32
331ca297 33 require_once(dirname(__FILE__) . '/../config.php');
6e4dc10f 34 require_once($CFG->libdir.'/adminlib.php');
2650c51e 35 require_once($CFG->libdir.'/gradelib.php');
f9903ed0 36
63a11f3c 37/// Extra debugging (set in config.php)
38 if (!empty($CFG->showcronsql)) {
39 $db->debug = true;
40 }
41 if (!empty($CFG->showcrondebugging)) {
42 $CFG->debug = DEBUG_DEVELOPER;
43 $CFG->displaydebug = true;
44 }
45
9152fc99 46/// extra safety
47 @session_write_close();
48
49/// check if execution allowed
e08df351 50 if (isset($_SERVER['REMOTE_ADDR'])) { // if the script is accessed via the web.
51 if (!empty($CFG->cronclionly)) {
52 // This script can only be run via the cli.
53 print_error('cronerrorclionly', 'admin');
54 exit;
55 }
56 // This script is being called via the web, so check the password if there is one.
57 if (!empty($CFG->cronremotepassword)) {
58 $pass = optional_param('password', '', PARAM_RAW);
59 if($pass != $CFG->cronremotepassword) {
60 // wrong password.
61 print_error('cronerrorpassword', 'admin');
62 exit;
63 }
64 }
65 }
66
afabdb14 67
9152fc99 68/// emulate normal session
69 $SESSION = new object();
70 $USER = get_admin(); /// Temporarily, to provide environment for this script
00de82df 71
9152fc99 72/// ignore admins timezone, language and locale - use site deafult instead!
73 $USER->timezone = $CFG->timezone;
5e601222 74 $USER->lang = '';
9152fc99 75 $USER->theme = '';
76 course_setup(SITEID);
5e601222 77
78/// send mime type and encoding
58e8f85b 79 if (check_browser_version('MSIE')) {
80 //ugly IE hack to work around downloading instead of viewing
810944af 81 @header('Content-Type: text/html; charset=utf-8');
58e8f85b 82 echo "<xmp>"; //<pre> is not good enough for us here
83 } else {
84 //send proper plaintext header
810944af 85 @header('Content-Type: text/plain; charset=utf-8');
58e8f85b 86 }
5e601222 87
9152fc99 88/// no more headers and buffers
89 while(@ob_end_flush());
90
4eefee97 91/// increase memory limit (PHP 5.2 does different calculation, we need more memory now)
92 @raise_memory_limit('128M');
93
428b6fec 94/// Start output log
f9903ed0 95
f9903ed0 96 $timenow = time();
428b6fec 97
defaac4c 98 mtrace("Server Time: ".date('r',$timenow)."\n\n");
f9903ed0 99
b2ce450a 100/// Run all cron jobs for each module
c88f014a 101
defaac4c 102 mtrace("Starting activity modules");
c2f6fe2f 103 get_mailer('buffer');
0f0a5c28 104 if ($mods = get_records_select("modules", "cron > 0 AND (($timenow - lastcron) > cron) AND visible = 1 ")) {
f9903ed0 105 foreach ($mods as $mod) {
673de292 106 $libfile = "$CFG->dirroot/mod/$mod->name/lib.php";
107 if (file_exists($libfile)) {
108 include_once($libfile);
109 $cron_function = $mod->name."_cron";
110 if (function_exists($cron_function)) {
defaac4c 111 mtrace("Processing module function $cron_function ...", '');
ff991c05 112 if (!empty($PERF->dbqueries)) {
113 $pre_dbqueries = $PERF->dbqueries;
114 $pre_time = microtime(1);
115 }
673de292 116 if ($cron_function()) {
117 if (! set_field("modules", "lastcron", $timenow, "id", $mod->id)) {
defaac4c 118 mtrace("Error: could not update timestamp for $mod->fullname");
673de292 119 }
120 }
ff991c05 121 if (isset($pre_dbqueries)) {
122 mtrace("... used " . ($PERF->dbqueries - $pre_dbqueries) . " dbqueries");
123 mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
124 }
5e867b23 125 /// Reset possible changes by modules to time_limit. MDL-11597
126 @set_time_limit(0);
defaac4c 127 mtrace("done.");
f9903ed0 128 }
129 }
130 }
131 }
c2f6fe2f 132 get_mailer('close');
defaac4c 133 mtrace("Finished activity modules");
c88f014a 134
f9648e77 135 mtrace("Starting blocks");
0f0a5c28 136 if ($blocks = get_records_select("block", "cron > 0 AND (($timenow - lastcron) > cron) AND visible = 1")) {
f9648e77 137 // we will need the base class.
138 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
139 foreach ($blocks as $block) {
140 $blockfile = $CFG->dirroot.'/blocks/'.$block->name.'/block_'.$block->name.'.php';
141 if (file_exists($blockfile)) {
142 require_once($blockfile);
143 $classname = 'block_'.$block->name;
144 $blockobj = new $classname;
145 if (method_exists($blockobj,'cron')) {
146 mtrace("Processing cron function for ".$block->name.'....','');
147 if ($blockobj->cron()) {
148 if (!set_field('block','lastcron',$timenow,'id',$block->id)) {
149 mtrace('Error: could not update timestamp for '.$block->name);
150 }
151 }
44853b81 152 /// Reset possible changes by blocks to time_limit. MDL-11597
153 @set_time_limit(0);
f9648e77 154 mtrace('done.');
155 }
156 }
157
158 }
159 }
0616d3e8 160 mtrace('Finished blocks');
f9648e77 161
d6ead3a2 162 if (!empty($CFG->langcache)) {
163 mtrace('Updating languages cache');
0a5ce9dd 164 get_list_of_languages(true);
d6ead3a2 165 }
166
0616d3e8 167 mtrace('Removing expired enrolments ...', ''); // See MDL-8785
168 $timenow = time();
cf057534 169 $somefound = false;
c5898fad 170 // The preferred way saves memory, dmllib.php
cf057534 171 // find courses where limited enrolment is enabled
c5898fad 172 global $CFG;
173 $rs_enrol = get_recordset_sql("SELECT ra.roleid, ra.userid, ra.contextid
174 FROM {$CFG->prefix}course c
175 INNER JOIN {$CFG->prefix}context cx ON cx.instanceid = c.id
176 INNER JOIN {$CFG->prefix}role_assignments ra ON ra.contextid = cx.id
177 WHERE cx.contextlevel = '".CONTEXT_COURSE."'
178 AND ra.timeend > 0
179 AND ra.timeend < '$timenow'
180 AND c.enrolperiod > 0
181 ");
182 while ($oldenrolment = rs_fetch_next_record($rs_enrol)) {
183 role_unassign($oldenrolment->roleid, $oldenrolment->userid, 0, $oldenrolment->contextid);
184 $somefound = true;
cf057534 185 }
c5898fad 186 rs_close($rs_enrol);
cf057534 187 if($somefound) {
0616d3e8 188 mtrace('Done');
189 } else {
190 mtrace('none found');
191 }
192
d6ead3a2 193
1ee0df06 194 mtrace('Starting main gradebook job ...');
2650c51e 195 grade_cron();
1ee0df06 196 mtrace('done.');
2650c51e 197
198
b2ce450a 199/// Run all core cron jobs, but not every time since they aren't too important.
200/// These don't have a timer to reduce load, so we'll use a random number
201/// to randomly choose the percentage of times we should run these jobs.
c88f014a 202
b2ce450a 203 srand ((double) microtime() * 10000000);
204 $random100 = rand(0,100);
c88f014a 205
b2ce450a 206 if ($random100 < 20) { // Approximately 20% of the time.
defaac4c 207 mtrace("Running clean-up tasks...");
b2ce450a 208
209 /// Unenrol users who haven't logged in for $CFG->longtimenosee
210
211 if ($CFG->longtimenosee) { // value in days
fce3ef94 212 $cuttime = $timenow - ($CFG->longtimenosee * 3600 * 24);
213 $rs = get_recordset_sql ("SELECT id, userid, courseid
214 FROM {$CFG->prefix}user_lastaccess
215 WHERE courseid != ".SITEID."
216 AND timeaccess < $cuttime ");
217 while ($assign = rs_fetch_next_record($rs)) {
218 if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
219 if (role_unassign(0, $assign->userid, 0, $context->id)) {
220 mtrace("Deleted assignment for user $assign->userid from course $assign->courseid");
b2ce450a 221 }
222 }
223 }
fce3ef94 224 rs_close($rs);
d9420631 225 /// Execute the same query again, looking for remaining records and deleting them
226 /// if the user hasn't moodle/course:view in the CONTEXT_COURSE context (orphan records)
227 $rs = get_recordset_sql ("SELECT id, userid, courseid
228 FROM {$CFG->prefix}user_lastaccess
229 WHERE courseid != ".SITEID."
230 AND timeaccess < $cuttime ");
231 while ($assign = rs_fetch_next_record($rs)) {
232 if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
233 if (!has_capability('moodle/course:view', $context, $assign->userid)) {
234 delete_records('user_lastaccess', 'userid', $assign->userid, 'courseid', $assign->courseid);
235 mtrace("Deleted orphan user_lastaccess for user $assign->userid from course $assign->courseid");
236 }
237 }
238 }
239 rs_close($rs);
b2ce450a 240 }
fce3ef94 241 flush();
242
243
6c61fd04 244 /// Delete users who haven't confirmed within required period
c6c43ae9 245
8c182006 246 if (!empty($CFG->deleteunconfirmed)) {
fce3ef94 247 $cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
248 $rs = get_recordset_sql ("SELECT id, firstname, lastname
249 FROM {$CFG->prefix}user
250 WHERE confirmed = 0
251 AND firstaccess > 0
252 AND firstaccess < $cuttime");
253 while ($user = rs_fetch_next_record($rs)) {
254 if (delete_records('user', 'id', $user->id)) {
255 mtrace("Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
f282e631 256 }
6cb86b39 257 }
fce3ef94 258 rs_close($rs);
6cb86b39 259 }
69f78bcb 260 flush();
7b8cc339 261
262
7b8cc339 263 /// Delete users who haven't completed profile within required period
264
8c182006 265 if (!empty($CFG->deleteunconfirmed)) {
fce3ef94 266 $cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
267 $rs = get_recordset_sql ("SELECT id, username
268 FROM {$CFG->prefix}user
269 WHERE confirmed = 1
270 AND lastaccess > 0
271 AND lastaccess < $cuttime
272 AND deleted = 0
273 AND (lastname = '' OR firstname = '' OR email = '')");
274 while ($user = rs_fetch_next_record($rs)) {
275 if (delete_records('user', 'id', $user->id)) {
276 mtrace("Deleted not fully setup user $user->username ($user->id)");
7b8cc339 277 }
278 }
fce3ef94 279 rs_close($rs);
7b8cc339 280 }
281 flush();
282
283
b2ce450a 284 /// Delete old logs to save space (this might need a timer to slow it down...)
fce3ef94 285
b2ce450a 286 if (!empty($CFG->loglifetime)) { // value in days
287 $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
fce3ef94 288 if (delete_records_select("log", "time < '$loglifetime'")) {
289 mtrace("Deleted old log records");
290 }
b2ce450a 291 }
69f78bcb 292 flush();
cb3a4484 293
fce3ef94 294
cb3a4484 295 /// Delete old cached texts
296
297 if (!empty($CFG->cachetext)) { // Defined in config.php
a9743837 298 $cachelifetime = time() - $CFG->cachetext - 60; // Add an extra minute to allow for really heavy sites
fce3ef94 299 if (delete_records_select('cache_text', "timemodified < '$cachelifetime'")) {
300 mtrace("Deleted old cache_text records");
301 }
cb3a4484 302 }
69f78bcb 303 flush();
d846e50a 304
305 if (!empty($CFG->notifyloginfailures)) {
306 notify_login_failures();
f1dc7614 307 mtrace('Notified login failured');
d846e50a 308 }
69f78bcb 309 flush();
d846e50a 310
b61efafb 311 sync_metacourses();
f1dc7614 312 mtrace('Synchronised metacourses');
b61efafb 313
e3b6b2b0 314 //
315 // generate new password emails for users
316 //
317 mtrace('checking for create_password');
318 if (count_records('user_preferences', 'name', 'create_password', 'value', '1')) {
319 mtrace('creating passwords for new users');
320 $newusers = get_records_sql("SELECT u.id as id, u.email, u.firstname,
321 u.lastname, u.username,
322 p.id as prefid
323 FROM {$CFG->prefix}user u
324 JOIN {$CFG->prefix}user_preferences p ON u.id=p.userid
325 WHERE p.name='create_password' AND p.value=1 AND u.email !='' ");
326
327 foreach ($newusers as $newuserid => $newuser) {
328 $newuser->emailstop = 0; // send email regardless
329 // email user
330 if (setnew_password_and_mail($newuser)) {
331 // remove user pref
332 delete_records('user_preferences', 'id', $newuser->prefid);
333 } else {
334 trigger_error("Could not create and mail new user password!");
335 }
336 }
337 }
7ea9b095 338
19e41f75 339 if(!empty($CFG->usetags)){
340 require_once($CFG->dirroot.'/tag/lib.php');
341 tag_cron();
f1dc7614 342 mtrace ('Executed tag cron');
19e41f75 343 }
344
ad72d0d7 345 // Accesslib stuff
346 cleanup_contexts();
f1dc7614 347 mtrace ('Cleaned up contexts');
fce3ef94 348 gc_cache_flags();
f1dc7614 349 mtrace ('Cleaned cache flags');
ad72d0d7 350 // If you suspect that the context paths are somehow corrupt
351 // replace the line below with: build_context_path(true);
352 build_context_path();
f1dc7614 353 mtrace ('Built context paths');
354
355 mtrace("Finished clean-up tasks...");
ad72d0d7 356
d846e50a 357 } // End of occasional clean-up tasks
540bab47 358
750eeaeb 359
50999a0b 360 if (empty($CFG->disablescheduledbackups)) { // Defined in config.php
74736e49 361 //Execute backup's cron
362 //Perhaps a long time and memory could help in large sites
a71bfa1c 363 @set_time_limit(0);
8891e81c 364 @raise_memory_limit("192M");
a2ce7344 365 if (function_exists('apache_child_terminate')) {
366 // if we are running from Apache, give httpd a hint that
367 // it can recycle the process after it's done. Apache's
368 // memory management is truly awful but we can help it.
369 @apache_child_terminate();
370 }
74736e49 371 if (file_exists("$CFG->dirroot/backup/backup_scheduled.php") and
372 file_exists("$CFG->dirroot/backup/backuplib.php") and
e91e2b6a 373 file_exists("$CFG->dirroot/backup/lib.php") and
374 file_exists("$CFG->libdir/blocklib.php")) {
74736e49 375 include_once("$CFG->dirroot/backup/backup_scheduled.php");
376 include_once("$CFG->dirroot/backup/backuplib.php");
377 include_once("$CFG->dirroot/backup/lib.php");
e91e2b6a 378 require_once ("$CFG->libdir/blocklib.php");
defaac4c 379 mtrace("Running backups if required...");
74736e49 380
381 if (! schedule_backup_cron()) {
04ddd06c 382 mtrace("ERROR: Something went wrong while performing backup tasks!!!");
74736e49 383 } else {
defaac4c 384 mtrace("Backup tasks finished.");
74736e49 385 }
e6d273eb 386 }
d3159192 387 }
388
8adcb49f 389 if (!empty($CFG->enablerssfeeds)) { //Defined in admin/variables page
3c02d134 390 include_once("$CFG->libdir/rsslib.php");
391 mtrace("Running rssfeeds if required...");
8adcb49f 392
3c02d134 393 if ( ! cron_rss_feeds()) {
394 mtrace("Something went wrong while generating rssfeeds!!!");
395 } else {
396 mtrace("Rssfeeds finished");
8adcb49f 397 }
398 }
399
04e44325 400/// Run the enrolment cron, if any
f9667a5a 401 if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
402 $plugins = array($CFG->enrol);
403 }
404 require_once($CFG->dirroot .'/enrol/enrol.class.php');
405 foreach ($plugins as $p) {
406 $enrol = enrolment_factory::factory($p);
407 if (method_exists($enrol, 'cron')) {
408 $enrol->cron();
409 }
410 if (!empty($enrol->log)) {
411 mtrace($enrol->log);
412 }
413 unset($enrol);
7e192c78 414 }
04e44325 415
d14383e0 416/// Run the auth cron, if any
c7b10b5f 417 $auths = get_enabled_auth_plugins();
418
d14383e0 419 mtrace("Running auth crons if required...");
420 foreach ($auths as $auth) {
421 $authplugin = get_auth_plugin($auth);
422 if (method_exists($authplugin, 'cron')) {
423 mtrace("Running cron for auth/$auth...");
424 $authplugin->cron();
425 if (!empty($authplugin->log)) {
426 mtrace($authplugin->log);
427 }
428 }
429 unset($authplugin);
430 }
431
72df7e1e 432 if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
61460dd6 433 require_once($CFG->dirroot.'/lib/statslib.php');
4900433c 434 // check we're not before our runtime
61460dd6 435 $timetocheck = stats_get_base_daily() + $CFG->statsruntimestarthour*60*60 + $CFG->statsruntimestartminute*60;
4900433c 436
437 if (time() > $timetocheck) {
61460dd6 438 // process max 31 days per cron execution
439 if (stats_cron_daily(31)) {
440 if (stats_cron_weekly()) {
441 if (stats_cron_monthly()) {
442 stats_clean_old();
443 }
4900433c 444 }
f3221af9 445 }
61460dd6 446 @set_time_limit(0);
6fdde1d2 447 } else {
5a4b2e8e 448 mtrace('Next stats run after:'. userdate($timetocheck));
f3221af9 449 }
450 }
1ee0df06 451
ce34ed3a 452 // run gradebook import/export/report cron
453 if ($gradeimports = get_list_of_plugins('grade/import')) {
454 foreach ($gradeimports as $gradeimport) {
1ee0df06 455 if (file_exists($CFG->dirroot.'/grade/import/'.$gradeimport.'/lib.php')) {
456 require_once($CFG->dirroot.'/grade/import/'.$gradeimport.'/lib.php');
457 $cron_function = 'grade_import_'.$gradeimport.'_cron';
ce34ed3a 458 if (function_exists($cron_function)) {
459 mtrace("Processing gradebook import function $cron_function ...", '');
460 $cron_function;
461 }
462 }
463 }
464 }
465
466 if ($gradeexports = get_list_of_plugins('grade/export')) {
467 foreach ($gradeexports as $gradeexport) {
1ee0df06 468 if (file_exists($CFG->dirroot.'/grade/export/'.$gradeexport.'/lib.php')) {
469 require_once($CFG->dirroot.'/grade/export/'.$gradeexport.'/lib.php');
470 $cron_function = 'grade_export_'.$gradeexport.'_cron';
ce34ed3a 471 if (function_exists($cron_function)) {
472 mtrace("Processing gradebook export function $cron_function ...", '');
473 $cron_function;
474 }
475 }
476 }
477 }
1ee0df06 478
ce34ed3a 479 if ($gradereports = get_list_of_plugins('grade/report')) {
480 foreach ($gradereports as $gradereport) {
1ee0df06 481 if (file_exists($CFG->dirroot.'/grade/report/'.$gradereport.'/lib.php')) {
482 require_once($CFG->dirroot.'/grade/report/'.$gradereport.'/lib.php');
483 $cron_function = 'grade_report_'.$gradereport.'_cron';
ce34ed3a 484 if (function_exists($cron_function)) {
485 mtrace("Processing gradebook report function $cron_function ...", '');
486 $cron_function;
487 }
488 }
489 }
490 }
ba9b44f9 491
492 // run any customized cronjobs, if any
493 // looking for functions in lib/local/cron.php
40e473bf 494 if (file_exists($CFG->dirroot.'/local/cron.php')) {
ba9b44f9 495 mtrace('Processing customized cron script ...', '');
40e473bf 496 include_once($CFG->dirroot.'/local/cron.php');
ba9b44f9 497 mtrace('done.');
498 }
499
f3221af9 500
db6b033d 501 //Unset session variables and destroy it
502 @session_unset();
503 @session_destroy();
504
defaac4c 505 mtrace("Cron script completed correctly");
f9903ed0 506
498d4032 507 $difftime = microtime_diff($starttime, microtime());
defaac4c 508 mtrace("Execution took ".$difftime." seconds");
498d4032 509
28a082f1 510/// finish the IE hack
58e8f85b 511 if (check_browser_version('MSIE')) {
512 echo "</xmp>";
513 }
514
1afb11e7 515?>