PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/admin/cron.php

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