MDL-8323 finished full conversion to proper $COURSE global - no more $CFG->coursethem...
[moodle.git] / admin / cron.php
1 <?PHP // $Id$
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 
14     $starttime = microtime();
16 /// The following is a hack necessary to allow this script to work well 
17 /// from the command line.
19     define('FULLME', 'cron');
22 /// Do not set moodle cookie because we do not need it here, it is better to emulate session
23     $nomoodlecookie = true;
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()
29     if (!isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['argv'][0])) {
30         chdir(dirname($_SERVER['argv'][0]));
31     }
33     require_once(dirname(__FILE__) . '/../config.php');
34     require_once($CFG->libdir.'/adminlib.php');
36 /// extra safety
37     @session_write_close();
39 /// check if execution allowed
40     if (isset($_SERVER['REMOTE_ADDR'])) { // if the script is accessed via the web.
41         if (!empty($CFG->cronclionly)) { 
42             // This script can only be run via the cli.
43             print_error('cronerrorclionly', 'admin');
44             exit;
45         }
46         // This script is being called via the web, so check the password if there is one.
47         if (!empty($CFG->cronremotepassword)) {
48             $pass = optional_param('password', '', PARAM_RAW);
49             if($pass != $CFG->cronremotepassword) {
50                 // wrong password.
51                 print_error('cronerrorpassword', 'admin'); 
52                 exit;
53             }
54         }
55     }
58 /// emulate normal session
59     $SESSION = new object();
60     $USER = get_admin();      /// Temporarily, to provide environment for this script
62 /// ignore admins timezone, language and locale - use site deafult instead!
63     $USER->timezone = $CFG->timezone;
64     $USER->lang = '';
65     $USER->theme = '';
66     course_setup(SITEID);
68 /// send mime type and encoding
69     if (check_browser_version('MSIE')) {
70         //ugly IE hack to work around downloading instead of viewing
71         @header('Content-Type: text/html; charset=utf-8');
72         echo "<xmp>"; //<pre> is not good enough for us here
73     } else {
74         //send proper plaintext header
75         @header('Content-Type: text/plain; charset=utf-8');
76     }
78 /// no more headers and buffers
79     while(@ob_end_flush());
81 /// Start output log
83     $timenow  = time();
85     mtrace("Server Time: ".date('r',$timenow)."\n\n");
87 /// Run all cron jobs for each module
89     mtrace("Starting activity modules");
90     if ($mods = get_records_select("modules", "cron > 0 AND (($timenow - lastcron) > cron)")) {
91         foreach ($mods as $mod) {
92             $libfile = "$CFG->dirroot/mod/$mod->name/lib.php";
93             if (file_exists($libfile)) {
94                 include_once($libfile);
95                 $cron_function = $mod->name."_cron";
96                 if (function_exists($cron_function)) {
97                     mtrace("Processing module function $cron_function ...", '');
98                     if ($cron_function()) {
99                         if (! set_field("modules", "lastcron", $timenow, "id", $mod->id)) {
100                             mtrace("Error: could not update timestamp for $mod->fullname");
101                         }
102                     }
103                     mtrace("done.");
104                 }
105             }
106         }
107     }
108     mtrace("Finished activity modules");
110     mtrace("Starting blocks");
111     if ($blocks = get_records_select("block", "cron > 0 AND (($timenow - lastcron) > cron)")) {
112         // we will need the base class.
113         require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
114         foreach ($blocks as $block) {
115             $blockfile = $CFG->dirroot.'/blocks/'.$block->name.'/block_'.$block->name.'.php';
116             if (file_exists($blockfile)) {
117                 require_once($blockfile);
118                 $classname = 'block_'.$block->name;
119                 $blockobj = new $classname; 
120                 if (method_exists($blockobj,'cron')) {
121                     mtrace("Processing cron function for ".$block->name.'....','');
122                     if ($blockobj->cron()) {
123                         if (!set_field('block','lastcron',$timenow,'id',$block->id)) {
124                             mtrace('Error: could not update timestamp for '.$block->name);
125                         }
126                     }
127                     mtrace('done.');
128                 }
129             }
131         }
132     }
133     mtrace("Finished blocks");
135     if (!empty($CFG->langcache)) {
136         mtrace('Updating languages cache');
137         get_list_of_languages();
138     }
141 /// Run all core cron jobs, but not every time since they aren't too important.
142 /// These don't have a timer to reduce load, so we'll use a random number 
143 /// to randomly choose the percentage of times we should run these jobs.
145     srand ((double) microtime() * 10000000);
146     $random100 = rand(0,100);
148     if ($random100 < 20) {     // Approximately 20% of the time.
149         mtrace("Running clean-up tasks...");
151         /// Unenrol users who haven't logged in for $CFG->longtimenosee
153         if ($CFG->longtimenosee) { // value in days
154             $longtime = $timenow - ($CFG->longtimenosee * 3600 * 24);
155             if ($assigns = get_users_longtimenosee($longtime)) {
156                 foreach ($assigns as $assign) {
157                     if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
158                         if (role_unassign(0, $assign->id, 0, $context->id)) {
159                             mtrace("Deleted assignment for user $assign->id from course $assign->courseid");
160                         }
161                     }
162                 }
163             }
164         }
165     
166     
167         /// Delete users who haven't confirmed within required period
169         if (!empty($CFG->deleteunconfirmed)) {
170             $oneweek = $timenow - ($CFG->deleteunconfirmed * 3600);
171             if ($users = get_users_unconfirmed($oneweek)) {
172                 foreach ($users as $user) {
173                     if (delete_records('user', 'id', $user->id)) {
174                         mtrace("Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
175                     }
176                 }
177             }
178         }
179         flush();
183         /// Delete users who haven't completed profile within required period
185         if (!empty($CFG->deleteunconfirmed)) {
186             $oneweek = $timenow - ($CFG->deleteunconfirmed * 3600);
187             if ($users = get_users_not_fully_set_up($oneweek)) {
188                 foreach ($users as $user) {
189                     if (delete_records('user', 'id', $user->id)) {
190                         mtrace("Deleted not fully setup user $user->username ($user->id)");
191                     }
192                 }
193             }
194         }
195         flush();
198     
199         /// Delete old logs to save space (this might need a timer to slow it down...)
200     
201         if (!empty($CFG->loglifetime)) {  // value in days
202             $loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
203             delete_records_select("log", "time < '$loglifetime'");
204         }
205         flush();
207         /// Delete old cached texts
209         if (!empty($CFG->cachetext)) {   // Defined in config.php
210             $cachelifetime = time() - $CFG->cachetext - 60;  // Add an extra minute to allow for really heavy sites
211             delete_records_select('cache_text', "timemodified < '$cachelifetime'");
212         }
213         flush();
215         if (!empty($CFG->notifyloginfailures)) {
216             notify_login_failures();
217         }
218         flush();
220         sync_metacourses();
222         //
223         // generate new password emails for users 
224         //
225         mtrace('checking for create_password');
226         if (count_records('user_preferences', 'name', 'create_password', 'value', '1')) {
227             mtrace('creating passwords for new users');
228             $newusers = get_records_sql("SELECT  u.id as id, u.email, u.firstname, 
229                                                 u.lastname, u.username,
230                                                 p.id as prefid 
231                                         FROM {$CFG->prefix}user u 
232                                              JOIN {$CFG->prefix}user_preferences p ON u.id=p.userid
233                                         WHERE p.name='create_password' AND p.value=1 AND u.email !='' ");
235             foreach ($newusers as $newuserid => $newuser) {
236                 $newuser->emailstop = 0; // send email regardless
237                 // email user                               
238                 if (setnew_password_and_mail($newuser)) {
239                     // remove user pref
240                     delete_records('user_preferences', 'id', $newuser->prefid);
241                 } else {
242                     trigger_error("Could not create and mail new user password!");
243                 }
244             }
245         }
247     } // End of occasional clean-up tasks
250     if (empty($CFG->disablescheduledbackups)) {   // Defined in config.php
251         //Execute backup's cron
252         //Perhaps a long time and memory could help in large sites
253         @set_time_limit(0);
254         @raise_memory_limit("128M");
255         if (function_exists('apache_child_terminate')) {
256             // if we are running from Apache, give httpd a hint that 
257             // it can recycle the process after it's done. Apache's 
258             // memory management is truly awful but we can help it.
259             @apache_child_terminate();
260         }
261         if (file_exists("$CFG->dirroot/backup/backup_scheduled.php") and
262             file_exists("$CFG->dirroot/backup/backuplib.php") and
263             file_exists("$CFG->dirroot/backup/lib.php") and
264             file_exists("$CFG->libdir/blocklib.php")) {
265             include_once("$CFG->dirroot/backup/backup_scheduled.php");
266             include_once("$CFG->dirroot/backup/backuplib.php");
267             include_once("$CFG->dirroot/backup/lib.php");
268             require_once ("$CFG->libdir/blocklib.php");
269             mtrace("Running backups if required...");
270     
271             if (! schedule_backup_cron()) {
272                 mtrace("ERROR: Something went wrong while performing backup tasks!!!");
273             } else {
274                 mtrace("Backup tasks finished.");
275             }
276         }
277     }
279     if (!empty($CFG->enablerssfeeds)) {  //Defined in admin/variables page
280         include_once("$CFG->libdir/rsslib.php");
281         mtrace("Running rssfeeds if required...");
283         if ( ! cron_rss_feeds()) {
284             mtrace("Something went wrong while generating rssfeeds!!!");
285         } else {
286             mtrace("Rssfeeds finished");
287         }
288     }
290 /// Run the enrolment cron, if any
291     if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
292         $plugins = array($CFG->enrol);
293     }
294     require_once($CFG->dirroot .'/enrol/enrol.class.php');
295     foreach ($plugins as $p) {
296         $enrol = enrolment_factory::factory($p);
297         if (method_exists($enrol, 'cron')) {
298             $enrol->cron();
299         }
300         if (!empty($enrol->log)) {
301             mtrace($enrol->log);
302         }
303         unset($enrol);
304     }
306 /// Run the auth cron, if any
307     if (!($auths = explode(',', $CFG->auth_plugins_enabled))) {
308         $auths = array($user->auth);
309     }
310     mtrace("Running auth crons if required...");
311     foreach ($auths as $auth) {
312         $authplugin = get_auth_plugin($auth);
313         if (method_exists($authplugin, 'cron')) {
314             mtrace("Running cron for auth/$auth...");
315             $authplugin->cron();
316             if (!empty($authplugin->log)) {
317                 mtrace($authplugin->log);
318             }
319         }
320         unset($authplugin);
321     }
323     if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
325         // check we're not before our runtime
326         $timetocheck = strtotime("$CFG->statsruntimestarthour:$CFG->statsruntimestartminute today");
328         if (time() > $timetocheck) {
329             $time = 60*60*20; // set it to 20 here for first run... (overridden by $CFG)
330             $clobber = true;
331             if (!empty($CFG->statsmaxruntime)) {
332                 $time = $CFG->statsmaxruntime+(60*30); // add on half an hour just to make sure (it could take that long to break out of the loop)
333             }
334             if (!get_field_sql('SELECT id FROM '.$CFG->prefix.'stats_daily')) {
335                 // first run, set another lock. we'll check for this in subsequent runs to set the timeout to later for the normal lock.
336                 set_cron_lock('statsfirstrunlock',true,$time,true);
337                 $firsttime = true;
338             }
339             $time = 60*60*2; // this time set to 2.. (overridden by $CFG)
340             if (!empty($CFG->statsmaxruntime)) {
341                 $time = $CFG->statsmaxruntime+(60*30); // add on half an hour to make sure (it could take that long to break out of the loop)
342             }
343             if ($config = get_record('config','name','statsfirstrunlock')) {
344                 if (!empty($config->value)) {
345                     $clobber = false; // if we're on the first run, just don't clobber it.
346                 }
347             }
348             if (set_cron_lock('statsrunning',true,$time, $clobber)) {
349                 require_once($CFG->dirroot.'/lib/statslib.php');
350                 $return = stats_cron_daily();
351                 if (stats_check_runtime() && $return == STATS_RUN_COMPLETE) {
352                     stats_cron_weekly();
353                 }
354                 if (stats_check_runtime() && $return == STATS_RUN_COMPLETE) {
355                     $return = $return && stats_cron_monthly();
356                 }
357                 if (stats_check_runtime() && $return == STATS_RUN_COMPLETE) {
358                     stats_clean_old();
359                 }
360                 set_cron_lock('statsrunning',false);
361                 if (!empty($firsttime)) {
362                     set_cron_lock('statsfirstrunlock',false);
363                 }
364             }
365         }
366     }
368     //Unset session variables and destroy it
369     @session_unset();
370     @session_destroy();
372     mtrace("Cron script completed correctly");
374     $difftime = microtime_diff($starttime, microtime());
375     mtrace("Execution took ".$difftime." seconds"); 
377 /// finishe the IE hack
378     if (check_browser_version('MSIE')) {
379         echo "</xmp>";
380     }
382 ?>