Merge branch 'MDL-77186-master' of https://github.com/andrewnicols/moodle
This commit is contained in:
@@ -27,7 +27,6 @@ define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once("{$CFG->libdir}/clilib.php");
|
||||
require_once("{$CFG->libdir}/cronlib.php");
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
[
|
||||
@@ -120,7 +119,7 @@ core_php_time_limit::raise();
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
// Emulate normal session - we use admin account by default.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
$humantimenow = date('r', time());
|
||||
$keepalive = (int)$options['keep-alive'];
|
||||
@@ -128,4 +127,4 @@ $keepalive = (int)$options['keep-alive'];
|
||||
\core\local\cli\shutdown::script_supports_graceful_exit();
|
||||
|
||||
mtrace("Server Time: {$humantimenow}\n");
|
||||
cron_run_adhoc_tasks(time(), $keepalive, $checklimits);
|
||||
\core\cron::run_adhoc_tasks(time(), $keepalive, $checklimits);
|
||||
|
||||
@@ -30,7 +30,6 @@ define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php'); // cli only functions
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
// now get cli options
|
||||
list($options, $unrecognized) = cli_get_params(array('help'=>false),
|
||||
@@ -80,10 +79,10 @@ if (!empty($CFG->showcrondebugging)) {
|
||||
|
||||
$starttime = microtime();
|
||||
|
||||
/// emulate normal session
|
||||
cron_setup_user();
|
||||
// Emulate normal session.
|
||||
\core\cron::setup_user();
|
||||
|
||||
/// Start output log
|
||||
// Start output log.
|
||||
$timenow = time();
|
||||
|
||||
mtrace("Server Time: ".date('r',$timenow)."\n\n");
|
||||
@@ -96,4 +95,4 @@ backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper:
|
||||
mtrace("Automated cron backups completed correctly");
|
||||
|
||||
$difftime = microtime_diff($starttime, microtime());
|
||||
mtrace("Execution took ".$difftime." seconds");
|
||||
mtrace("Execution took ".$difftime." seconds");
|
||||
|
||||
+21
-13
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -30,12 +29,11 @@
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php'); // cli only functions
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
|
||||
// now get cli options
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
// Now get cli option.
|
||||
[$options, $unrecognized] = cli_get_params(
|
||||
[
|
||||
'help' => false,
|
||||
'stop' => false,
|
||||
@@ -44,7 +42,9 @@ list($options, $unrecognized) = cli_get_params(
|
||||
'enable' => false,
|
||||
'disable' => false,
|
||||
'disable-wait' => false,
|
||||
], [
|
||||
'keep-alive' => null,
|
||||
],
|
||||
[
|
||||
'h' => 'help',
|
||||
's' => 'stop',
|
||||
'l' => 'list',
|
||||
@@ -52,6 +52,7 @@ list($options, $unrecognized) = cli_get_params(
|
||||
'e' => 'enable',
|
||||
'd' => 'disable',
|
||||
'w' => 'disable-wait',
|
||||
'k' => 'keep-alive',
|
||||
]
|
||||
);
|
||||
|
||||
@@ -62,7 +63,7 @@ if ($unrecognized) {
|
||||
|
||||
if ($options['help']) {
|
||||
$help =
|
||||
"Execute periodic cron actions.
|
||||
"Execute periodic cron actions.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
@@ -72,6 +73,9 @@ Options:
|
||||
-e, --enable Enable cron
|
||||
-d, --disable Disable cron
|
||||
-w, --disable-wait=600 Disable cron and wait until all tasks finished or fail after N seconds (optional param)
|
||||
-k, --keep-alive=N Keep this script alive for N seconds and poll for new tasks
|
||||
The default value can be set by administrators in:
|
||||
Site administration > Server > Tasks > Task processing > Keep alive time
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/cron.php
|
||||
@@ -105,7 +109,8 @@ if ($options['list']) {
|
||||
$tasks = \core\task\manager::get_running_tasks();
|
||||
mtrace('The list of currently running tasks:');
|
||||
$format = "%7s %-12s %-9s %-20s %-52s\n";
|
||||
printf ($format,
|
||||
printf(
|
||||
$format,
|
||||
'PID',
|
||||
'HOST',
|
||||
'TYPE',
|
||||
@@ -113,7 +118,8 @@ if ($options['list']) {
|
||||
'CLASSNAME'
|
||||
);
|
||||
foreach ($tasks as $task) {
|
||||
printf ($format,
|
||||
printf(
|
||||
$format,
|
||||
$task->pid,
|
||||
substr($task->hostname, 0, 12),
|
||||
$task->type,
|
||||
@@ -137,7 +143,7 @@ if ($wait = $options['disable-wait']) {
|
||||
set_config('cron_enabled', 0);
|
||||
\core\task\manager::clear_static_caches();
|
||||
mtrace('Cron has been disabled for the site.');
|
||||
mtrace('Allocating '. format_time($waitsec) . ' for the tasks to finish.');
|
||||
mtrace('Allocating ' . format_time($waitsec) . ' for the tasks to finish.');
|
||||
|
||||
$lastcount = 0;
|
||||
while ($wait) {
|
||||
@@ -151,7 +157,7 @@ if ($wait = $options['disable-wait']) {
|
||||
|
||||
if (time() - $started >= $waitsec) {
|
||||
mtrace('');
|
||||
mtrace('Wait time ('. format_time($waitsec) . ') elapsed, but ' . count($tasks) . ' task(s) still running.');
|
||||
mtrace('Wait time (' . format_time($waitsec) . ') elapsed, but ' . count($tasks) . ' task(s) still running.');
|
||||
mtrace('Exiting with code 1.');
|
||||
exit(1);
|
||||
}
|
||||
@@ -175,4 +181,6 @@ if (!get_config('core', 'cron_enabled') && !$options['force']) {
|
||||
|
||||
\core\local\cli\shutdown::script_supports_graceful_exit();
|
||||
|
||||
cron_run();
|
||||
|
||||
$keepalive = $options['keep-alive'];
|
||||
\core\cron::run_main_process($keepalive);
|
||||
|
||||
@@ -28,7 +28,6 @@ define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
require_once($CFG->libdir . '/cronlib.php');
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
[
|
||||
|
||||
@@ -27,7 +27,6 @@ define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once("$CFG->libdir/clilib.php");
|
||||
require_once("$CFG->libdir/cronlib.php");
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
[
|
||||
@@ -142,7 +141,7 @@ if ($execute = $options['execute']) {
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
// Emulate normal session - we use admin account by default.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Execute the task.
|
||||
\core\local\cli\shutdown::script_supports_graceful_exit();
|
||||
@@ -164,5 +163,5 @@ if ($execute = $options['execute']) {
|
||||
$task->set_cron_lock($cronlock);
|
||||
}
|
||||
|
||||
cron_run_inner_scheduled_task($task);
|
||||
\core\cron::run_inner_scheduled_task($task);
|
||||
}
|
||||
|
||||
+6
-7
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -36,6 +35,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// phpcs:ignoreFile moodle.Files.MoodleInternal.MoodleInternalGlobalState
|
||||
|
||||
if (defined('STDIN')) {
|
||||
fwrite(STDERR, "ERROR: This script no longer supports CLI, please use admin/cli/cron.php instead\n");
|
||||
@@ -49,8 +49,7 @@ define('WEB_CRON_EMULATED_CLI', 'defined'); // ugly ugly hack, do not use elsewh
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require('../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
|
||||
// extra safety
|
||||
\core\session\manager::write_close();
|
||||
@@ -71,11 +70,11 @@ if (!empty($CFG->cronremotepassword)) {
|
||||
}
|
||||
}
|
||||
|
||||
// send mime type and encoding
|
||||
// Send mime type and encoding.
|
||||
@header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
// we do not want html markup in emulated CLI
|
||||
// We do not want html markup in emulated CLI.
|
||||
@ini_set('html_errors', 'off');
|
||||
|
||||
// execute the cron
|
||||
cron_run();
|
||||
// Execute the cron, disabling keepalive.
|
||||
\core\cron::run_main_process(0);
|
||||
|
||||
@@ -331,6 +331,20 @@ if ($hassiteconfig) {
|
||||
$setting->set_updatedcallback('theme_reset_static_caches');
|
||||
$temp->add($setting);
|
||||
|
||||
$setting = new admin_setting_configduration(
|
||||
'cron_keepalive',
|
||||
new lang_string('cron_keepalive', 'admin'),
|
||||
new lang_string('cron_keepalive_desc', 'admin'),
|
||||
\core\cron::DEFAULT_MAIN_PROCESS_KEEPALIVE,
|
||||
// The default unit is minutes.
|
||||
MINSECS,
|
||||
);
|
||||
|
||||
// Set an upper limit.
|
||||
$setting->set_max_duration(\core\cron::MAX_MAIN_PROCESS_KEEPALIVE);
|
||||
|
||||
$temp->add($setting);
|
||||
|
||||
$temp->add(
|
||||
new admin_setting_configtext(
|
||||
'task_scheduled_concurrency_limit',
|
||||
|
||||
@@ -101,7 +101,7 @@ if (!file_exists($source)) {
|
||||
}
|
||||
|
||||
// Emulate normal session - we use admin account by default.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Get the file list.
|
||||
$files = [];
|
||||
|
||||
@@ -366,7 +366,7 @@ class manager {
|
||||
}
|
||||
|
||||
// Record the user that this script is currently being run as. This is important when re-processing existing
|
||||
// messages, as cron_setup_user is called multiple times.
|
||||
// messages, as \core\cron::setup_user is called multiple times.
|
||||
$originaluser = $USER;
|
||||
|
||||
$envelope = $message->getEnvelope();
|
||||
@@ -433,7 +433,7 @@ class manager {
|
||||
// Process the message as the user.
|
||||
$user = $this->addressmanager->get_data()->user;
|
||||
mtrace("-- Processing the message as user {$user->id} ({$user->username}).");
|
||||
cron_setup_user($user);
|
||||
\core\cron::setup_user($user);
|
||||
|
||||
// Process and retrieve the message data for this message.
|
||||
// This includes fetching the full content, as well as all headers, and attachments.
|
||||
@@ -459,7 +459,7 @@ class manager {
|
||||
|
||||
// Returning to normal cron user.
|
||||
mtrace("-- Returning to the original user.");
|
||||
cron_setup_user($originaluser);
|
||||
\core\cron::setup_user($originaluser);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ class manager {
|
||||
|
||||
// Returning to normal cron user.
|
||||
mtrace("-- Returning to the original user.");
|
||||
cron_setup_user($originaluser);
|
||||
\core\cron::setup_user($originaluser);
|
||||
return;
|
||||
} catch (\Exception $e) {
|
||||
// An unknown error occurred. The user is not informed, but the administrator is.
|
||||
@@ -487,7 +487,7 @@ class manager {
|
||||
|
||||
// Returning to normal cron user.
|
||||
mtrace("-- Returning to the original user.");
|
||||
cron_setup_user($originaluser);
|
||||
\core\cron::setup_user($originaluser);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ class manager {
|
||||
|
||||
// Returning to normal cron user.
|
||||
mtrace("-- Returning to the original user.");
|
||||
cron_setup_user($originaluser);
|
||||
\core\cron::setup_user($originaluser);
|
||||
|
||||
mtrace("-- Finished processing " . $message->getUid());
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require('../../../config.php');
|
||||
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
// Basic security checks.
|
||||
require_admin();
|
||||
$context = context_system::instance();
|
||||
|
||||
@@ -28,8 +28,6 @@ define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require('../../../config.php');
|
||||
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
/**
|
||||
* Function used to handle mtrace by outputting the text to normal browser window.
|
||||
*
|
||||
|
||||
@@ -184,7 +184,7 @@ if ($options['restorefile'] && !file_exists($options['restorefile'])) {
|
||||
$processoroptions['restorefile'] = $options['restorefile'];
|
||||
|
||||
// Emulate normal session.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Let's get started!
|
||||
$content = file_get_contents($options['file']);
|
||||
|
||||
@@ -36,7 +36,7 @@ core_php_time_limit::raise();
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
// Emulate normal session - we use admin account by default, set language to the site language.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$USER->lang = $CFG->lang;
|
||||
|
||||
$clihelper = new \tool_uploaduser\cli_helper();
|
||||
|
||||
@@ -30,7 +30,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php');
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class backup_cron_automated_helper {
|
||||
mtrace("Skipping deleted courses", '...');
|
||||
mtrace(sprintf("%d courses", self::remove_deleted_courses_from_schedule()));
|
||||
mtrace('Running required automated backups...');
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
|
||||
mtrace("Getting admin info");
|
||||
$admin = get_admin();
|
||||
|
||||
@@ -68,7 +68,7 @@ class content_notification_task extends adhoc_task {
|
||||
from user with id {$userfrom->id}.");
|
||||
foreach ($users as $user) {
|
||||
|
||||
cron_setup_user($user, $course);
|
||||
\core\cron::setup_user($user, $course);
|
||||
|
||||
$cm = get_fast_modinfo($course)->cms[$cm->id];
|
||||
|
||||
|
||||
@@ -53,11 +53,11 @@ class course_delete_modules extends \core\task\adhoc_task {
|
||||
// Set the proper user.
|
||||
if ($this->get_custom_data()->userid !== $this->get_custom_data()->realuserid) {
|
||||
$realuser = \core_user::get_user($this->get_custom_data()->realuserid, '*', MUST_EXIST);
|
||||
cron_setup_user($realuser);
|
||||
\core\cron::setup_user($realuser);
|
||||
\core\session\manager::loginas($this->get_custom_data()->userid, \context_system::instance(), false);
|
||||
} else {
|
||||
$user = \core_user::get_user($this->get_custom_data()->userid, '*', MUST_EXIST);
|
||||
cron_setup_user($user);
|
||||
\core\cron::setup_user($user);
|
||||
}
|
||||
|
||||
$cms = $this->get_custom_data()->cms;
|
||||
|
||||
@@ -230,6 +230,8 @@ $string['configdndallowtextandlinks'] = 'Enable or disable the dragging and drop
|
||||
$string['configdoclang'] = 'This language will be used in links for the documentation pages.';
|
||||
$string['configdocroot'] = 'Defines the path to Moodle Docs for providing context-specific documentation via \'Help and documentation\' links in the footer of each page. If the field is left blank, links will not be displayed.';
|
||||
$string['configdoctonewwindow'] = 'If enabled, then links to Moodle Docs will be shown in a new window.';
|
||||
$string['configduration_low'] = 'This value is too low. The minimum value is {$a}.';
|
||||
$string['configduration_high'] = 'This value is too high. The maximum value is {$a}.';
|
||||
$string['configeditordictionary'] = 'This value will be used if aspell doesn\'t have dictionary for users own language.';
|
||||
$string['configeditorfontlist'] = 'Select the fonts that should appear in the editor\'s drop-down list.';
|
||||
$string['configemailchangeconfirmation'] = 'Require an email confirmation step when users change their email address in their profile.';
|
||||
@@ -442,6 +444,8 @@ $string['cron'] = 'Cron';
|
||||
$string['cron_enabled'] = 'Enable cron';
|
||||
$string['cron_enabled_desc'] = 'Cron should normally be enabled, however this setting allows it to be disabled temporarily, for example before a server restart. If disabled, the system is prevented from starting new background tasks. Note that the cron should not be disabled for a long time, as this will prevent important functionality from working.';
|
||||
$string['cron_help'] = 'The cron.php script runs a number of tasks at different scheduled intervals, such as sending forum post notification emails. The script should be run regularly - ideally every minute.';
|
||||
$string['cron_keepalive'] = 'Keep alive';
|
||||
$string['cron_keepalive_desc'] = 'The amount of time to keep polling for additional tasks. This setting is useful for ensuring that cron is always running.<br><br>If you use dedicated task runners then you should set this value to an 0, otherwise we recommend setting this to around a value similar to your adhoc task concurrency limit. Longer values should be avoided and the maximum value is 15 minutes.';
|
||||
$string['cron_link'] = 'admin/cron';
|
||||
$string['cronclionly'] = 'Cron execution via command line only';
|
||||
$string['cronerrorclionly'] = 'Sorry, internet access to this page has been disabled by the administrator.';
|
||||
|
||||
+58
-7
@@ -3874,6 +3874,12 @@ class admin_setting_configduration extends admin_setting {
|
||||
/** @var callable|null Validation function */
|
||||
protected $validatefunction = null;
|
||||
|
||||
/** @var int The minimum allowed value */
|
||||
protected int $minduration = 0;
|
||||
|
||||
/** @var null|int The maximum allowed value */
|
||||
protected null|int $maxduration = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $name unique ascii name, either 'mysetting' for settings that in config,
|
||||
@@ -3896,6 +3902,31 @@ class admin_setting_configduration extends admin_setting {
|
||||
parent::__construct($name, $visiblename, $description, $defaultsetting);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum allowed value.
|
||||
* This must be at least 0.
|
||||
*
|
||||
* @param int $duration
|
||||
*/
|
||||
public function set_min_duration(int $duration): void {
|
||||
if ($duration < 0) {
|
||||
throw new coding_exception('The minimum duration must be at least 0.');
|
||||
}
|
||||
|
||||
$this->minduration = $duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum allowed value.
|
||||
*
|
||||
* A value of null will disable the maximum duration value.
|
||||
*
|
||||
* @param int|null $duration
|
||||
*/
|
||||
public function set_max_duration(?int $duration): void {
|
||||
$this->maxduration = $duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a validate function.
|
||||
*
|
||||
@@ -3918,15 +3949,23 @@ class admin_setting_configduration extends admin_setting {
|
||||
* @since Moodle 3.10
|
||||
*/
|
||||
protected function validate_setting(int $data): string {
|
||||
if ($data < $this->minduration) {
|
||||
return get_string(
|
||||
'configduration_low',
|
||||
'admin',
|
||||
self::get_duration_text($this->minduration, get_string('numseconds', 'core', 0))
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->maxduration && $data > $this->maxduration) {
|
||||
return get_string('configduration_high', 'admin', self::get_duration_text($this->maxduration));
|
||||
}
|
||||
|
||||
// If validation function is specified, call it now.
|
||||
if ($this->validatefunction) {
|
||||
return call_user_func($this->validatefunction, $data);
|
||||
} else {
|
||||
if ($data < 0) {
|
||||
return get_string('errorsetting', 'admin');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3948,10 +3987,14 @@ class admin_setting_configduration extends admin_setting {
|
||||
* Converts seconds to some more user friendly string.
|
||||
* @static
|
||||
* @param int $seconds
|
||||
* @param null|string The value to use when the duration is empty. If not specified, a "None" value is used.
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_duration_text($seconds) {
|
||||
protected static function get_duration_text(int $seconds, ?string $emptyvalue = null): string {
|
||||
if (empty($seconds)) {
|
||||
if ($emptyvalue !== null) {
|
||||
return $emptyvalue;
|
||||
}
|
||||
return get_string('none');
|
||||
}
|
||||
$data = self::parse_seconds($seconds);
|
||||
@@ -4043,7 +4086,15 @@ class admin_setting_configduration extends admin_setting {
|
||||
}
|
||||
|
||||
$inputid = $this->get_id() . 'v';
|
||||
$units = self::get_units();
|
||||
$units = array_filter(self::get_units(), function($unit): bool {
|
||||
if (!$this->maxduration) {
|
||||
// No duration limit. All units are valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
return $unit <= $this->maxduration;
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
$defaultunit = $this->defaultunit;
|
||||
|
||||
$context = (object) [
|
||||
|
||||
@@ -0,0 +1,612 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
use coding_exception;
|
||||
use core_php_time_limit;
|
||||
use moodle_exception;
|
||||
use stdClass;
|
||||
|
||||
// Disable the moodle.PHP.ForbiddenFunctions.FoundWithAlternative sniff for this file.
|
||||
// It detects uses of error_log() which are valid in this file.
|
||||
// phpcs:disable moodle.PHP.ForbiddenFunctions.FoundWithAlternative
|
||||
|
||||
/**
|
||||
* Cron and adhoc task functionality.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2023 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cron {
|
||||
|
||||
/** @var ?stdClass A copy of the standard cron 'user' */
|
||||
protected static ?stdClass $cronuser = null;
|
||||
|
||||
/** @var ?stdClass The cron user's session data */
|
||||
protected static ?stdClass $cronsession = null;
|
||||
|
||||
/**
|
||||
* Use a default value of 3 minutes.
|
||||
* The recommended cron frequency is every minute, and the default adhoc concurrency is 3.
|
||||
* A default value of 3 minutes allows all adhoc tasks to be run concurrently at their default value.
|
||||
*
|
||||
* @var int The default keepalive value for the main cron runner
|
||||
*/
|
||||
public const DEFAULT_MAIN_PROCESS_KEEPALIVE = 3 * MINSECS;
|
||||
|
||||
/**
|
||||
* @var int The max keepalive value for the main cron runner
|
||||
*/
|
||||
public const MAX_MAIN_PROCESS_KEEPALIVE = 15 * MINSECS;
|
||||
|
||||
/**
|
||||
* Execute cron tasks
|
||||
*
|
||||
* @param int|null $keepalive The keepalive time for this cron run.
|
||||
*/
|
||||
public static function run_main_process(?int $keepalive = null): void {
|
||||
global $CFG, $DB;
|
||||
|
||||
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');
|
||||
|
||||
if (!empty($CFG->showcronsql)) {
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
if (!empty($CFG->showcrondebugging)) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
core_php_time_limit::raise();
|
||||
|
||||
// Increase memory limit.
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
// Emulate normal session. - we use admin account by default.
|
||||
self::setup_user();
|
||||
|
||||
// Start output log.
|
||||
$timenow = time();
|
||||
mtrace("Server Time: " . date('r', $timenow) . "\n\n");
|
||||
|
||||
// Record start time and interval between the last cron runs.
|
||||
$laststart = get_config('tool_task', 'lastcronstart');
|
||||
set_config('lastcronstart', $timenow, 'tool_task');
|
||||
if ($laststart) {
|
||||
// Record the interval between last two runs (always store at least 1 second).
|
||||
set_config('lastcroninterval', max(1, $timenow - $laststart), 'tool_task');
|
||||
}
|
||||
|
||||
// Determine the time when the cron should finish.
|
||||
if ($keepalive === null) {
|
||||
$keepalive = get_config('core', 'cron_keepalive');
|
||||
if ($keepalive === false) {
|
||||
$keepalive = self::DEFAULT_MAIN_PROCESS_KEEPALIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if ($keepalive > self::MAX_MAIN_PROCESS_KEEPALIVE) {
|
||||
// Attempt to prevent abnormally long keepalives.
|
||||
mtrace("Cron keepalive time is too long, reducing to 15 minutes.");
|
||||
$keepalive = self::MAX_MAIN_PROCESS_KEEPALIVE;
|
||||
}
|
||||
|
||||
// Calculate the finish time based on the start time and keepalive.
|
||||
$finishtime = $timenow + $keepalive;
|
||||
|
||||
do {
|
||||
$startruntime = microtime();
|
||||
// Run all scheduled tasks.
|
||||
self::run_scheduled_tasks($timenow);
|
||||
|
||||
// Run adhoc tasks.
|
||||
self::run_adhoc_tasks($timenow);
|
||||
|
||||
mtrace("Cron run completed correctly");
|
||||
|
||||
gc_collect_cycles();
|
||||
|
||||
$completiontime = date('H:i:s');
|
||||
$difftime = microtime_diff($startruntime, microtime());
|
||||
$memoryused = display_size(memory_get_usage());
|
||||
|
||||
$message = "Cron completed at {$completiontime} in {$difftime} seconds. Memory used: {$memoryused}.";
|
||||
|
||||
// Check if we should continue to run.
|
||||
// Only continue to run if:
|
||||
// - The finish time has not been reached; and
|
||||
// - The graceful exit flag has not been set; and
|
||||
// - The static caches have not been cleared since the start of the cron run.
|
||||
$remaining = $finishtime - time();
|
||||
$runagain = $remaining > 0;
|
||||
$runagain = $runagain && !\core\local\cli\shutdown::should_gracefully_exit();
|
||||
$runagain = $runagain && !\core\task\manager::static_caches_cleared_since($timenow);
|
||||
|
||||
if ($runagain) {
|
||||
$message .= " Continuing to check for tasks for {$remaining} more seconds.";
|
||||
mtrace($message);
|
||||
sleep(1);
|
||||
|
||||
// Re-check the graceful exit and cache clear flags after sleeping as these may have changed.
|
||||
$runagain = $runagain && !\core\local\cli\shutdown::should_gracefully_exit();
|
||||
$runagain = $runagain && !\core\task\manager::static_caches_cleared_since($timenow);
|
||||
} else {
|
||||
mtrace($message);
|
||||
}
|
||||
} while ($runagain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all queued scheduled tasks, applying necessary concurrency limits and time limits.
|
||||
*
|
||||
* @param int $timenow The time this process started.
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public static function run_scheduled_tasks(int $timenow): void {
|
||||
// Allow a restriction on the number of scheduled task runners at once.
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
$maxruns = get_config('core', 'task_scheduled_concurrency_limit');
|
||||
$maxruntime = get_config('core', 'task_scheduled_max_runtime');
|
||||
|
||||
$scheduledlock = null;
|
||||
for ($run = 0; $run < $maxruns; $run++) {
|
||||
// If we can't get a lock instantly it means runner N is already running
|
||||
// so fail as fast as possible and try N+1 so we don't limit the speed at
|
||||
// which we bring new runners into the pool.
|
||||
if ($scheduledlock = $cronlockfactory->get_lock("scheduled_task_runner_{$run}", 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scheduledlock) {
|
||||
mtrace("Skipping processing of scheduled tasks. Concurrency limit reached.");
|
||||
return;
|
||||
}
|
||||
|
||||
$starttime = time();
|
||||
|
||||
// Run all scheduled tasks.
|
||||
try {
|
||||
while (
|
||||
!\core\local\cli\shutdown::should_gracefully_exit() &&
|
||||
!\core\task\manager::static_caches_cleared_since($timenow) &&
|
||||
$task = \core\task\manager::get_next_scheduled_task($timenow)
|
||||
) {
|
||||
self::run_inner_scheduled_task($task);
|
||||
unset($task);
|
||||
|
||||
if ((time() - $starttime) > $maxruntime) {
|
||||
mtrace("Stopping processing of scheduled tasks as time limit has been reached.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Release the scheduled task runner lock.
|
||||
$scheduledlock->release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all queued adhoc tasks, applying necessary concurrency limits and time limits.
|
||||
*
|
||||
* @param int $timenow The time this process started.
|
||||
* @param int $keepalive Keep this public static function alive for N seconds and poll for new adhoc tasks.
|
||||
* @param bool $checklimits Should we check limits?
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public static function run_adhoc_tasks(int $timenow, $keepalive = 0, $checklimits = true) {
|
||||
// Allow a restriction on the number of adhoc task runners at once.
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
$maxruns = get_config('core', 'task_adhoc_concurrency_limit');
|
||||
$maxruntime = get_config('core', 'task_adhoc_max_runtime');
|
||||
|
||||
$adhoclock = null;
|
||||
if ($checklimits) {
|
||||
for ($run = 0; $run < $maxruns; $run++) {
|
||||
// If we can't get a lock instantly it means runner N is already running
|
||||
// so fail as fast as possible and try N+1 so we don't limit the speed at
|
||||
// which we bring new runners into the pool.
|
||||
if ($adhoclock = $cronlockfactory->get_lock("adhoc_task_runner_{$run}", 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$adhoclock) {
|
||||
mtrace("Skipping processing of adhoc tasks. Concurrency limit reached.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$humantimenow = date('r', $timenow);
|
||||
$finishtime = $timenow + $keepalive;
|
||||
$waiting = false;
|
||||
$taskcount = 0;
|
||||
|
||||
// Run all adhoc tasks.
|
||||
while (
|
||||
!\core\local\cli\shutdown::should_gracefully_exit() &&
|
||||
!\core\task\manager::static_caches_cleared_since($timenow)
|
||||
) {
|
||||
|
||||
if ($checklimits && (time() - $timenow) >= $maxruntime) {
|
||||
if ($waiting) {
|
||||
$waiting = false;
|
||||
mtrace('');
|
||||
}
|
||||
mtrace("Stopping processing of adhoc tasks as time limit has been reached.");
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$task = \core\task\manager::get_next_adhoc_task(time(), $checklimits);
|
||||
} catch (\Throwable $e) {
|
||||
if ($adhoclock) {
|
||||
// Release the adhoc task runner lock.
|
||||
$adhoclock->release();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($task) {
|
||||
if ($waiting) {
|
||||
mtrace('');
|
||||
}
|
||||
$waiting = false;
|
||||
self::run_inner_adhoc_task($task);
|
||||
self::set_process_title("Waiting for next adhoc task");
|
||||
$taskcount++;
|
||||
unset($task);
|
||||
} else {
|
||||
$timeleft = $finishtime - time();
|
||||
if ($timeleft <= 0) {
|
||||
break;
|
||||
}
|
||||
if (!$waiting) {
|
||||
mtrace('Waiting for more adhoc tasks to be queued ', '');
|
||||
} else {
|
||||
mtrace('.', '');
|
||||
}
|
||||
$waiting = true;
|
||||
self::set_process_title("Waiting {$timeleft}s for next adhoc task");
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($waiting) {
|
||||
mtrace('');
|
||||
}
|
||||
|
||||
mtrace("Ran {$taskcount} adhoc tasks found at {$humantimenow}");
|
||||
|
||||
if ($adhoclock) {
|
||||
// Release the adhoc task runner lock.
|
||||
$adhoclock->release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared code that handles running of a single scheduled task within the cron.
|
||||
*
|
||||
* Not intended for calling directly outside of this library!
|
||||
*
|
||||
* @param \core\task\task_base $task
|
||||
*/
|
||||
public static function run_inner_scheduled_task(\core\task\task_base $task) {
|
||||
global $CFG, $DB;
|
||||
$debuglevel = $CFG->debug;
|
||||
|
||||
\core\task\manager::scheduled_task_starting($task);
|
||||
\core\task\logmanager::start_logging($task);
|
||||
|
||||
$fullname = $task->get_name() . ' (' . get_class($task) . ')';
|
||||
mtrace('Execute scheduled task: ' . $fullname);
|
||||
self::set_process_title('Scheduled task: ' . get_class($task));
|
||||
self::trace_time_and_memory();
|
||||
$predbqueries = null;
|
||||
$predbqueries = $DB->perf_get_queries();
|
||||
$pretime = microtime(1);
|
||||
try {
|
||||
get_mailer('buffer');
|
||||
self::prepare_core_renderer();
|
||||
// Temporarily increase debug level if task has failed and debugging isn't already at maximum.
|
||||
if ($debuglevel !== DEBUG_DEVELOPER && $faildelay = $task->get_fail_delay()) {
|
||||
mtrace('Debugging increased temporarily due to faildelay of ' . $faildelay);
|
||||
set_debugging(DEBUG_DEVELOPER);
|
||||
}
|
||||
$task->execute();
|
||||
if ($DB->is_transaction_started()) {
|
||||
throw new coding_exception("Task left transaction open");
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace('Scheduled task complete: ' . $fullname);
|
||||
\core\task\manager::scheduled_task_complete($task);
|
||||
} catch (\Throwable $e) {
|
||||
if ($DB && $DB->is_transaction_started()) {
|
||||
error_log('Database transaction aborted automatically in ' . get_class($task));
|
||||
$DB->force_transaction_rollback();
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace('Scheduled task failed: ' . $fullname . ',' . $e->getMessage());
|
||||
if ($CFG->debugdeveloper) {
|
||||
if (!empty($e->debuginfo)) {
|
||||
mtrace("Debug info:");
|
||||
mtrace($e->debuginfo);
|
||||
}
|
||||
mtrace("Backtrace:");
|
||||
mtrace(format_backtrace($e->getTrace(), true));
|
||||
}
|
||||
\core\task\manager::scheduled_task_failed($task);
|
||||
} finally {
|
||||
// Reset debugging if it changed.
|
||||
if ($CFG->debug !== $debuglevel) {
|
||||
set_debugging($debuglevel);
|
||||
}
|
||||
// Reset back to the standard admin user.
|
||||
self::setup_user();
|
||||
self::set_process_title('Waiting for next scheduled task');
|
||||
self::prepare_core_renderer(true);
|
||||
}
|
||||
get_mailer('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared code that handles running of a single adhoc task within the cron.
|
||||
*
|
||||
* @param \core\task\adhoc_task $task
|
||||
*/
|
||||
public static function run_inner_adhoc_task(\core\task\adhoc_task $task) {
|
||||
global $CFG, $DB;
|
||||
$debuglevel = $CFG->debug;
|
||||
|
||||
\core\task\manager::adhoc_task_starting($task);
|
||||
\core\task\logmanager::start_logging($task);
|
||||
|
||||
mtrace("Execute adhoc task: " . get_class($task));
|
||||
mtrace("Adhoc task id: " . $task->get_id());
|
||||
mtrace("Adhoc task custom data: " . $task->get_custom_data_as_string());
|
||||
self::set_process_title('Adhoc task: ' . $task->get_id() . ' ' . get_class($task));
|
||||
self::trace_time_and_memory();
|
||||
$predbqueries = null;
|
||||
$predbqueries = $DB->perf_get_queries();
|
||||
$pretime = microtime(1);
|
||||
|
||||
if ($userid = $task->get_userid()) {
|
||||
// This task has a userid specified.
|
||||
if ($user = \core_user::get_user($userid)) {
|
||||
// User found. Check that they are suitable.
|
||||
try {
|
||||
\core_user::require_active_user($user, true, true);
|
||||
} catch (moodle_exception $e) {
|
||||
mtrace("User {$userid} cannot be used to run an adhoc task: " . get_class($task) . ". Cancelling task.");
|
||||
$user = null;
|
||||
}
|
||||
} else {
|
||||
// Unable to find the user for this task.
|
||||
// A user missing in the database will never reappear.
|
||||
mtrace("User {$userid} could not be found for adhoc task: " . get_class($task) . ". Cancelling task.");
|
||||
}
|
||||
|
||||
if (empty($user)) {
|
||||
// A user missing in the database will never reappear so the task needs to be failed to ensure that locks are
|
||||
// removed, and then removed to prevent future runs.
|
||||
// A task running as a user should only be run as that user.
|
||||
\core\task\manager::adhoc_task_failed($task);
|
||||
$DB->delete_records('task_adhoc', ['id' => $task->get_id()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self::setup_user($user);
|
||||
}
|
||||
|
||||
try {
|
||||
get_mailer('buffer');
|
||||
self::prepare_core_renderer();
|
||||
// Temporarily increase debug level if task has failed and debugging isn't already at maximum.
|
||||
if ($debuglevel !== DEBUG_DEVELOPER && $faildelay = $task->get_fail_delay()) {
|
||||
mtrace('Debugging increased temporarily due to faildelay of ' . $faildelay);
|
||||
set_debugging(DEBUG_DEVELOPER);
|
||||
}
|
||||
$task->execute();
|
||||
if ($DB->is_transaction_started()) {
|
||||
throw new coding_exception("Task left transaction open");
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace("Adhoc task complete: " . get_class($task));
|
||||
\core\task\manager::adhoc_task_complete($task);
|
||||
} catch (\Throwable $e) {
|
||||
if ($DB && $DB->is_transaction_started()) {
|
||||
error_log('Database transaction aborted automatically in ' . get_class($task));
|
||||
$DB->force_transaction_rollback();
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace("Adhoc task failed: " . get_class($task) . "," . $e->getMessage());
|
||||
if ($CFG->debugdeveloper) {
|
||||
if (!empty($e->debuginfo)) {
|
||||
mtrace("Debug info:");
|
||||
mtrace($e->debuginfo);
|
||||
}
|
||||
mtrace("Backtrace:");
|
||||
mtrace(format_backtrace($e->getTrace(), true));
|
||||
}
|
||||
\core\task\manager::adhoc_task_failed($task);
|
||||
} finally {
|
||||
// Reset debug level if it changed.
|
||||
if ($CFG->debug !== $debuglevel) {
|
||||
set_debugging($debuglevel);
|
||||
}
|
||||
// Reset back to the standard admin user.
|
||||
self::setup_user();
|
||||
self::prepare_core_renderer(true);
|
||||
}
|
||||
get_mailer('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the process title
|
||||
*
|
||||
* This makes it very easy for a sysadmin to immediately see what task
|
||||
* a cron process is running at any given moment.
|
||||
*
|
||||
* @param string $title process status title
|
||||
*/
|
||||
public static function set_process_title(string $title) {
|
||||
global $CFG;
|
||||
if (CLI_SCRIPT) {
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
$datetime = userdate(time(), '%b %d, %H:%M:%S');
|
||||
cli_set_process_title_suffix("$datetime $title");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output some standard information during cron runs. Specifically current time
|
||||
* and memory usage. This method also does gc_collect_cycles() (before displaying
|
||||
* memory usage) to try to help PHP manage memory better.
|
||||
*/
|
||||
public static function trace_time_and_memory() {
|
||||
gc_collect_cycles();
|
||||
mtrace('... started ' . date('H:i:s') . '. Current memory use ' . display_size(memory_get_usage()) . '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the output renderer for the cron run.
|
||||
*
|
||||
* This involves creating a new $PAGE, and $OUTPUT fresh for each task and prevents any one task from influencing
|
||||
* any other.
|
||||
*
|
||||
* @param bool $restore Whether to restore the original PAGE and OUTPUT
|
||||
*/
|
||||
public static function prepare_core_renderer($restore = false) {
|
||||
global $OUTPUT, $PAGE;
|
||||
|
||||
// Store the original PAGE and OUTPUT values so that they can be reset at a later point to the original.
|
||||
// This should not normally be required, but may be used in places such as the scheduled task tool's "Run now"
|
||||
// functionality.
|
||||
static $page = null;
|
||||
static $output = null;
|
||||
|
||||
if (null === $page) {
|
||||
$page = $PAGE;
|
||||
}
|
||||
|
||||
if (null === $output) {
|
||||
$output = $OUTPUT;
|
||||
}
|
||||
|
||||
if (!empty($restore)) {
|
||||
$PAGE = $page;
|
||||
$page = null;
|
||||
|
||||
$OUTPUT = $output;
|
||||
$output = null;
|
||||
} else {
|
||||
// Setup a new General renderer.
|
||||
// Cron tasks may produce output to be used in web, so we must use the appropriate renderer target.
|
||||
// This allows correct use of templates, etc.
|
||||
$PAGE = new \moodle_page();
|
||||
$OUTPUT = new \core_renderer($PAGE, RENDERER_TARGET_GENERAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a user and course environment in cron.
|
||||
* Do not use outside of cron script!
|
||||
*
|
||||
* Please note that this function stores cache data statically.
|
||||
* @see reset_user_cache() to reset this cache.
|
||||
*
|
||||
* @param null|stdClass $user full user object, null means default cron user (admin)
|
||||
* @param null|stdClass $course full course record, null means $SITE
|
||||
* @param null|bool $leavepagealone If specified, stops it messing with global page object
|
||||
*/
|
||||
public static function setup_user(?stdClass $user = null, ?stdClass $course = null, bool $leavepagealone = false): void {
|
||||
// This function uses the $GLOBALS super global. Disable the VariableNameLowerCase sniff for this function.
|
||||
// phpcs:disable moodle.NamingConventions.ValidVariableName.VariableNameLowerCase
|
||||
global $CFG, $SITE, $PAGE;
|
||||
|
||||
if (!CLI_SCRIPT && !$leavepagealone) {
|
||||
throw new coding_exception('It is not possible to use \core\cron\setup_user() in normal requests!');
|
||||
}
|
||||
|
||||
if (empty(self::$cronuser)) {
|
||||
// The cron user is essentially the admin user, but with some value removed.
|
||||
// We ginore the timezone language, and locale preferences - use the site default instead.
|
||||
self::$cronuser = get_admin();
|
||||
self::$cronuser->timezone = $CFG->timezone;
|
||||
self::$cronuser->lang = '';
|
||||
self::$cronuser->theme = '';
|
||||
unset(self::$cronuser->description);
|
||||
|
||||
self::$cronsession = new stdClass();
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
// Cached default cron user (==modified admin for now).
|
||||
\core\session\manager::init_empty_session();
|
||||
\core\session\manager::set_user(self::$cronuser);
|
||||
$GLOBALS['SESSION'] = self::$cronsession;
|
||||
} else {
|
||||
// Emulate real user session - needed for caps in cron.
|
||||
if ($GLOBALS['USER']->id != $user->id) {
|
||||
\core\session\manager::init_empty_session();
|
||||
\core\session\manager::set_user($user);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO MDL-19774 relying on global $PAGE in cron is a bad idea.
|
||||
// Temporary hack so that cron does not give fatal errors.
|
||||
if (!$leavepagealone) {
|
||||
$PAGE = new \moodle_page();
|
||||
$PAGE->set_course($course ?? $SITE);
|
||||
}
|
||||
|
||||
// TODO: it should be possible to improve perf by caching some limited number of users here.
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cache for the cron user used by `setup_user()`.
|
||||
*/
|
||||
public static function reset_user_cache(): void {
|
||||
self::$cronuser = null;
|
||||
self::$cronsession = null;
|
||||
\core\session\manager::init_empty_session();
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class plagiarism_cron_task extends scheduled_task {
|
||||
$plagiarismplugin = new $plagiarismclass;
|
||||
if (method_exists($plagiarismplugin, 'cron')) {
|
||||
mtrace('Processing cron function for plagiarism_plugin_' . $plugin . '...', '');
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
mtrace('It has been detected the class ' . $plagiarismclass . ' has a legacy cron method
|
||||
implemented. Plagiarism plugins should implement their own schedule tasks.', '');
|
||||
$plagiarismplugin->cron();
|
||||
|
||||
+6
-439
@@ -23,443 +23,10 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute cron tasks
|
||||
*/
|
||||
function cron_run() {
|
||||
global $DB, $CFG, $OUTPUT;
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
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');
|
||||
|
||||
if (!empty($CFG->showcronsql)) {
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
if (!empty($CFG->showcrondebugging)) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
core_php_time_limit::raise();
|
||||
$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");
|
||||
|
||||
// Record start time and interval between the last cron runs.
|
||||
$laststart = get_config('tool_task', 'lastcronstart');
|
||||
set_config('lastcronstart', $timenow, 'tool_task');
|
||||
if ($laststart) {
|
||||
// Record the interval between last two runs (always store at least 1 second).
|
||||
set_config('lastcroninterval', max(1, $timenow - $laststart), 'tool_task');
|
||||
}
|
||||
|
||||
// Run all scheduled tasks.
|
||||
cron_run_scheduled_tasks($timenow);
|
||||
|
||||
// Run adhoc tasks.
|
||||
cron_run_adhoc_tasks($timenow);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all queued scheduled tasks, applying necessary concurrency limits and time limits.
|
||||
*
|
||||
* @param int $timenow The time this process started.
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
function cron_run_scheduled_tasks(int $timenow) {
|
||||
// Allow a restriction on the number of scheduled task runners at once.
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
$maxruns = get_config('core', 'task_scheduled_concurrency_limit');
|
||||
$maxruntime = get_config('core', 'task_scheduled_max_runtime');
|
||||
|
||||
$scheduledlock = null;
|
||||
for ($run = 0; $run < $maxruns; $run++) {
|
||||
// If we can't get a lock instantly it means runner N is already running
|
||||
// so fail as fast as possible and try N+1 so we don't limit the speed at
|
||||
// which we bring new runners into the pool.
|
||||
if ($scheduledlock = $cronlockfactory->get_lock("scheduled_task_runner_{$run}", 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scheduledlock) {
|
||||
mtrace("Skipping processing of scheduled tasks. Concurrency limit reached.");
|
||||
return;
|
||||
}
|
||||
|
||||
$starttime = time();
|
||||
|
||||
// Run all scheduled tasks.
|
||||
try {
|
||||
while (!\core\local\cli\shutdown::should_gracefully_exit() &&
|
||||
!\core\task\manager::static_caches_cleared_since($timenow) &&
|
||||
$task = \core\task\manager::get_next_scheduled_task($timenow)) {
|
||||
cron_run_inner_scheduled_task($task);
|
||||
unset($task);
|
||||
|
||||
if ((time() - $starttime) > $maxruntime) {
|
||||
mtrace("Stopping processing of scheduled tasks as time limit has been reached.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Release the scheduled task runner lock.
|
||||
$scheduledlock->release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all queued adhoc tasks, applying necessary concurrency limits and time limits.
|
||||
*
|
||||
* @param int $timenow The time this process started.
|
||||
* @param int $keepalive Keep this function alive for N seconds and poll for new adhoc tasks.
|
||||
* @param bool $checklimits Should we check limits?
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
function cron_run_adhoc_tasks(int $timenow, $keepalive = 0, $checklimits = true) {
|
||||
// Allow a restriction on the number of adhoc task runners at once.
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
$maxruns = get_config('core', 'task_adhoc_concurrency_limit');
|
||||
$maxruntime = get_config('core', 'task_adhoc_max_runtime');
|
||||
|
||||
if ($checklimits) {
|
||||
$adhoclock = null;
|
||||
for ($run = 0; $run < $maxruns; $run++) {
|
||||
// If we can't get a lock instantly it means runner N is already running
|
||||
// so fail as fast as possible and try N+1 so we don't limit the speed at
|
||||
// which we bring new runners into the pool.
|
||||
if ($adhoclock = $cronlockfactory->get_lock("adhoc_task_runner_{$run}", 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$adhoclock) {
|
||||
mtrace("Skipping processing of adhoc tasks. Concurrency limit reached.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$humantimenow = date('r', $timenow);
|
||||
$finishtime = $timenow + $keepalive;
|
||||
$waiting = false;
|
||||
$taskcount = 0;
|
||||
|
||||
// Run all adhoc tasks.
|
||||
while (!\core\local\cli\shutdown::should_gracefully_exit() &&
|
||||
!\core\task\manager::static_caches_cleared_since($timenow)) {
|
||||
|
||||
if ($checklimits && (time() - $timenow) >= $maxruntime) {
|
||||
if ($waiting) {
|
||||
$waiting = false;
|
||||
mtrace('');
|
||||
}
|
||||
mtrace("Stopping processing of adhoc tasks as time limit has been reached.");
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$task = \core\task\manager::get_next_adhoc_task(time(), $checklimits);
|
||||
} catch (\Throwable $e) {
|
||||
if ($adhoclock) {
|
||||
// Release the adhoc task runner lock.
|
||||
$adhoclock->release();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($task) {
|
||||
if ($waiting) {
|
||||
mtrace('');
|
||||
}
|
||||
$waiting = false;
|
||||
cron_run_inner_adhoc_task($task);
|
||||
cron_set_process_title("Waiting for next adhoc task");
|
||||
$taskcount++;
|
||||
unset($task);
|
||||
} else {
|
||||
$timeleft = $finishtime - time();
|
||||
if ($timeleft <= 0) {
|
||||
break;
|
||||
}
|
||||
if (!$waiting) {
|
||||
mtrace('Waiting for more adhoc tasks to be queued ', '');
|
||||
} else {
|
||||
mtrace('.', '');
|
||||
}
|
||||
$waiting = true;
|
||||
cron_set_process_title("Waiting {$timeleft}s for next adhoc task");
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($waiting) {
|
||||
mtrace('');
|
||||
}
|
||||
|
||||
mtrace("Ran {$taskcount} adhoc tasks found at {$humantimenow}");
|
||||
|
||||
if ($adhoclock) {
|
||||
// Release the adhoc task runner lock.
|
||||
$adhoclock->release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared code that handles running of a single scheduled task within the cron.
|
||||
*
|
||||
* Not intended for calling directly outside of this library!
|
||||
*
|
||||
* @param \core\task\task_base $task
|
||||
*/
|
||||
function cron_run_inner_scheduled_task(\core\task\task_base $task) {
|
||||
global $CFG, $DB;
|
||||
$debuglevel = $CFG->debug;
|
||||
|
||||
\core\task\manager::scheduled_task_starting($task);
|
||||
\core\task\logmanager::start_logging($task);
|
||||
|
||||
$fullname = $task->get_name() . ' (' . get_class($task) . ')';
|
||||
mtrace('Execute scheduled task: ' . $fullname);
|
||||
cron_set_process_title('Scheduled task: ' . get_class($task));
|
||||
cron_trace_time_and_memory();
|
||||
$predbqueries = null;
|
||||
$predbqueries = $DB->perf_get_queries();
|
||||
$pretime = microtime(1);
|
||||
try {
|
||||
get_mailer('buffer');
|
||||
cron_prepare_core_renderer();
|
||||
// Temporarily increase debug level if task has failed and debugging isn't already at maximum.
|
||||
if ($debuglevel !== DEBUG_DEVELOPER && $faildelay = $task->get_fail_delay()) {
|
||||
mtrace('Debugging increased temporarily due to faildelay of ' . $faildelay);
|
||||
set_debugging(DEBUG_DEVELOPER);
|
||||
}
|
||||
$task->execute();
|
||||
if ($DB->is_transaction_started()) {
|
||||
throw new coding_exception("Task left transaction open");
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace('Scheduled task complete: ' . $fullname);
|
||||
\core\task\manager::scheduled_task_complete($task);
|
||||
} catch (\Throwable $e) {
|
||||
if ($DB && $DB->is_transaction_started()) {
|
||||
error_log('Database transaction aborted automatically in ' . get_class($task));
|
||||
$DB->force_transaction_rollback();
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace('Scheduled task failed: ' . $fullname . ',' . $e->getMessage());
|
||||
if ($CFG->debugdeveloper) {
|
||||
if (!empty($e->debuginfo)) {
|
||||
mtrace("Debug info:");
|
||||
mtrace($e->debuginfo);
|
||||
}
|
||||
mtrace("Backtrace:");
|
||||
mtrace(format_backtrace($e->getTrace(), true));
|
||||
}
|
||||
\core\task\manager::scheduled_task_failed($task);
|
||||
} finally {
|
||||
// Reset debugging if it changed.
|
||||
if ($CFG->debug !== $debuglevel) {
|
||||
set_debugging($debuglevel);
|
||||
}
|
||||
// Reset back to the standard admin user.
|
||||
cron_setup_user();
|
||||
cron_set_process_title('Waiting for next scheduled task');
|
||||
cron_prepare_core_renderer(true);
|
||||
}
|
||||
get_mailer('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared code that handles running of a single adhoc task within the cron.
|
||||
*
|
||||
* @param \core\task\adhoc_task $task
|
||||
*/
|
||||
function cron_run_inner_adhoc_task(\core\task\adhoc_task $task) {
|
||||
global $CFG, $DB;
|
||||
$debuglevel = $CFG->debug;
|
||||
|
||||
\core\task\manager::adhoc_task_starting($task);
|
||||
\core\task\logmanager::start_logging($task);
|
||||
|
||||
mtrace("Execute adhoc task: " . get_class($task));
|
||||
mtrace("Adhoc task id: " . $task->get_id());
|
||||
mtrace("Adhoc task custom data: " . $task->get_custom_data_as_string());
|
||||
cron_set_process_title('Adhoc task: ' . $task->get_id() . ' ' . get_class($task));
|
||||
cron_trace_time_and_memory();
|
||||
$predbqueries = null;
|
||||
$predbqueries = $DB->perf_get_queries();
|
||||
$pretime = microtime(1);
|
||||
|
||||
if ($userid = $task->get_userid()) {
|
||||
// This task has a userid specified.
|
||||
if ($user = \core_user::get_user($userid)) {
|
||||
// User found. Check that they are suitable.
|
||||
try {
|
||||
\core_user::require_active_user($user, true, true);
|
||||
} catch (moodle_exception $e) {
|
||||
mtrace("User {$userid} cannot be used to run an adhoc task: " . get_class($task) . ". Cancelling task.");
|
||||
$user = null;
|
||||
}
|
||||
} else {
|
||||
// Unable to find the user for this task.
|
||||
// A user missing in the database will never reappear.
|
||||
mtrace("User {$userid} could not be found for adhoc task: " . get_class($task) . ". Cancelling task.");
|
||||
}
|
||||
|
||||
if (empty($user)) {
|
||||
// A user missing in the database will never reappear so the task needs to be failed to ensure that locks are removed,
|
||||
// and then removed to prevent future runs.
|
||||
// A task running as a user should only be run as that user.
|
||||
\core\task\manager::adhoc_task_failed($task);
|
||||
$DB->delete_records('task_adhoc', ['id' => $task->get_id()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cron_setup_user($user);
|
||||
}
|
||||
|
||||
try {
|
||||
get_mailer('buffer');
|
||||
cron_prepare_core_renderer();
|
||||
// Temporarily increase debug level if task has failed and debugging isn't already at maximum.
|
||||
if ($debuglevel !== DEBUG_DEVELOPER && $faildelay = $task->get_fail_delay()) {
|
||||
mtrace('Debugging increased temporarily due to faildelay of ' . $faildelay);
|
||||
set_debugging(DEBUG_DEVELOPER);
|
||||
}
|
||||
$task->execute();
|
||||
if ($DB->is_transaction_started()) {
|
||||
throw new coding_exception("Task left transaction open");
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace("Adhoc task complete: " . get_class($task));
|
||||
\core\task\manager::adhoc_task_complete($task);
|
||||
} catch (\Throwable $e) {
|
||||
if ($DB && $DB->is_transaction_started()) {
|
||||
error_log('Database transaction aborted automatically in ' . get_class($task));
|
||||
$DB->force_transaction_rollback();
|
||||
}
|
||||
if (isset($predbqueries)) {
|
||||
mtrace("... used " . ($DB->perf_get_queries() - $predbqueries) . " dbqueries");
|
||||
mtrace("... used " . (microtime(1) - $pretime) . " seconds");
|
||||
}
|
||||
mtrace("Adhoc task failed: " . get_class($task) . "," . $e->getMessage());
|
||||
if ($CFG->debugdeveloper) {
|
||||
if (!empty($e->debuginfo)) {
|
||||
mtrace("Debug info:");
|
||||
mtrace($e->debuginfo);
|
||||
}
|
||||
mtrace("Backtrace:");
|
||||
mtrace(format_backtrace($e->getTrace(), true));
|
||||
}
|
||||
\core\task\manager::adhoc_task_failed($task);
|
||||
} finally {
|
||||
// Reset debug level if it changed.
|
||||
if ($CFG->debug !== $debuglevel) {
|
||||
set_debugging($debuglevel);
|
||||
}
|
||||
// Reset back to the standard admin user.
|
||||
cron_setup_user();
|
||||
cron_prepare_core_renderer(true);
|
||||
}
|
||||
get_mailer('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the process title
|
||||
*
|
||||
* This makes it very easy for a sysadmin to immediately see what task
|
||||
* a cron process is running at any given moment.
|
||||
*
|
||||
* @param string $title process status title
|
||||
*/
|
||||
function cron_set_process_title(string $title) {
|
||||
global $CFG;
|
||||
if (CLI_SCRIPT) {
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
$datetime = userdate(time(), '%b %d, %H:%M:%S');
|
||||
cli_set_process_title_suffix("$datetime $title");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output some standard information during cron runs. Specifically current time
|
||||
* and memory usage. This method also does gc_collect_cycles() (before displaying
|
||||
* memory usage) to try to help PHP manage memory better.
|
||||
*/
|
||||
function cron_trace_time_and_memory() {
|
||||
gc_collect_cycles();
|
||||
mtrace('... started ' . date('H:i:s') . '. Current memory use ' . display_size(memory_get_usage()) . '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the output renderer for the cron run.
|
||||
*
|
||||
* This involves creating a new $PAGE, and $OUTPUT fresh for each task and prevents any one task from influencing
|
||||
* any other.
|
||||
*
|
||||
* @param bool $restore Whether to restore the original PAGE and OUTPUT
|
||||
*/
|
||||
function cron_prepare_core_renderer($restore = false) {
|
||||
global $OUTPUT, $PAGE;
|
||||
|
||||
// Store the original PAGE and OUTPUT values so that they can be reset at a later point to the original.
|
||||
// This should not normally be required, but may be used in places such as the scheduled task tool's "Run now"
|
||||
// functionality.
|
||||
static $page = null;
|
||||
static $output = null;
|
||||
|
||||
if (null === $page) {
|
||||
$page = $PAGE;
|
||||
}
|
||||
|
||||
if (null === $output) {
|
||||
$output = $OUTPUT;
|
||||
}
|
||||
|
||||
if (!empty($restore)) {
|
||||
$PAGE = $page;
|
||||
$page = null;
|
||||
|
||||
$OUTPUT = $output;
|
||||
$output = null;
|
||||
} else {
|
||||
// Setup a new General renderer.
|
||||
// Cron tasks may produce output to be used in web, so we must use the appropriate renderer target.
|
||||
// This allows correct use of templates, etc.
|
||||
$PAGE = new \moodle_page();
|
||||
$OUTPUT = new \core_renderer($PAGE, RENDERER_TARGET_GENERAL);
|
||||
}
|
||||
}
|
||||
debugging(
|
||||
'Inclusion of lib/cronlib.php is no longer required. ' .
|
||||
'Please update your code to use the new \\core\\cron class.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
|
||||
+124
-1
@@ -2838,7 +2838,7 @@ function cron_execute_plugin_type($plugintype, $description = null) {
|
||||
|
||||
mtrace('Processing cron function for ' . $component . '...');
|
||||
debugging("Use of legacy cron is deprecated ($cronfunction). Please use scheduled tasks.", DEBUG_DEVELOPER);
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
$pre_dbqueries = $DB->perf_get_queries();
|
||||
$pre_time = microtime(true);
|
||||
|
||||
@@ -3671,3 +3671,126 @@ function print_error($errorcode, $module = 'error', $link = '', $a = null, $debu
|
||||
"Please throw a new moodle_exception instance instead.", DEBUG_DEVELOPER);
|
||||
throw new \moodle_exception($errorcode, $module, $link, $a, $debuginfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute cron tasks
|
||||
*
|
||||
* @param int|null $keepalive The keepalive time for this cron run.
|
||||
* @deprecated since 4.2 Use \core\cron::run_main_process() instead.
|
||||
*/
|
||||
function cron_run(?int $keepalive = null): void {
|
||||
debugging(
|
||||
'The cron_run() function is deprecated. Please use \core\cron::run_main_process() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::run_main_process($keepalive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all queued scheduled tasks, applying necessary concurrency limits and time limits.
|
||||
*
|
||||
* @param int $timenow The time this process started.
|
||||
* @deprecated since 4.2 Use \core\cron::run_scheduled_tasks() instead.
|
||||
*/
|
||||
function cron_run_scheduled_tasks(int $timenow) {
|
||||
debugging(
|
||||
'The cron_run_scheduled_tasks() function is deprecated. Please use \core\cron::run_scheduled_tasks() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::run_scheduled_tasks($timenow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all queued adhoc tasks, applying necessary concurrency limits and time limits.
|
||||
*
|
||||
* @param int $timenow The time this process started.
|
||||
* @param int $keepalive Keep this function alive for N seconds and poll for new adhoc tasks.
|
||||
* @param bool $checklimits Should we check limits?
|
||||
* @deprecated since 4.2 Use \core\cron::run_adhoc_tasks() instead.
|
||||
*/
|
||||
function cron_run_adhoc_tasks(int $timenow, $keepalive = 0, $checklimits = true) {
|
||||
debugging(
|
||||
'The cron_run_adhoc_tasks() function is deprecated. Please use \core\cron::run_adhoc_tasks() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::run_adhoc_tasks($timenow, $keepalive, $checklimits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared code that handles running of a single scheduled task within the cron.
|
||||
*
|
||||
* Not intended for calling directly outside of this library!
|
||||
*
|
||||
* @param \core\task\task_base $task
|
||||
* @deprecated since 4.2 Use \core\cron::run_inner_scheduled_task() instead.
|
||||
*/
|
||||
function cron_run_inner_scheduled_task(\core\task\task_base $task) {
|
||||
debugging(
|
||||
'The cron_run_inner_scheduled_task() function is deprecated. Please use \core\cron::run_inner_scheduled_task() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::run_inner_scheduled_task($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared code that handles running of a single adhoc task within the cron.
|
||||
*
|
||||
* @param \core\task\adhoc_task $task
|
||||
* @deprecated since 4.2 Use \core\cron::run_inner_adhoc_task() instead.
|
||||
*/
|
||||
function cron_run_inner_adhoc_task(\core\task\adhoc_task $task) {
|
||||
debugging(
|
||||
'The cron_run_inner_adhoc_task() function is deprecated. Please use \core\cron::run_inner_adhoc_task() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::run_inner_adhoc_task($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the process title
|
||||
*
|
||||
* This makes it very easy for a sysadmin to immediately see what task
|
||||
* a cron process is running at any given moment.
|
||||
*
|
||||
* @param string $title process status title
|
||||
* @deprecated since 4.2 Use \core\cron::set_process_title() instead.
|
||||
*/
|
||||
function cron_set_process_title(string $title) {
|
||||
debugging(
|
||||
'The cron_set_process_title() function is deprecated. Please use \core\cron::set_process_title() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::set_process_title($title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output some standard information during cron runs. Specifically current time
|
||||
* and memory usage. This method also does gc_collect_cycles() (before displaying
|
||||
* memory usage) to try to help PHP manage memory better.
|
||||
*
|
||||
* @deprecated since 4.2 Use \core\cron::trace_time_and_memory() instead.
|
||||
*/
|
||||
function cron_trace_time_and_memory() {
|
||||
debugging(
|
||||
'The cron_trace_time_and_memory() function is deprecated. Please use \core\cron::trace_time_and_memory() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::trace_time_and_memory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the output renderer for the cron run.
|
||||
*
|
||||
* This involves creating a new $PAGE, and $OUTPUT fresh for each task and prevents any one task from influencing
|
||||
* any other.
|
||||
*
|
||||
* @param bool $restore Whether to restore the original PAGE and OUTPUT
|
||||
* @deprecated since 4.2 Use \core\cron::prepare_core_renderer() instead.
|
||||
*/
|
||||
function cron_prepare_core_renderer($restore = false) {
|
||||
debugging(
|
||||
'The cron_prepare_core_renderer() function is deprecated. Please use \core\cron::prepare_core_renderer() instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
\core\cron::prepare_core_renderer($restore);
|
||||
}
|
||||
|
||||
@@ -2250,12 +2250,11 @@ class file_storage {
|
||||
*/
|
||||
public function cron() {
|
||||
global $CFG, $DB;
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
// find out all stale draft areas (older than 4 days) and purge them
|
||||
// those are identified by time stamp of the /. root dir
|
||||
mtrace('Deleting old draft files... ', '');
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
$old = time() - 60*60*24*4;
|
||||
$sql = "SELECT *
|
||||
FROM {files}
|
||||
@@ -2272,7 +2271,7 @@ class file_storage {
|
||||
// * preview files in the core preview filearea without the existing original file.
|
||||
// * document converted files in core documentconversion filearea without the existing original file.
|
||||
mtrace('Deleting orphaned preview, and document conversion files... ', '');
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
$sql = "SELECT p.*
|
||||
FROM {files} p
|
||||
LEFT JOIN {files} o ON (p.filename = o.contenthash)
|
||||
@@ -2299,7 +2298,7 @@ class file_storage {
|
||||
require_once($CFG->libdir.'/filelib.php');
|
||||
// Delete files that are associated with a context that no longer exists.
|
||||
mtrace('Cleaning up files from deleted contexts... ', '');
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
$sql = "SELECT DISTINCT f.contextid
|
||||
FROM {files} f
|
||||
LEFT OUTER JOIN {context} c ON f.contextid = c.id
|
||||
@@ -2315,7 +2314,7 @@ class file_storage {
|
||||
mtrace('done.');
|
||||
|
||||
mtrace('Call filesystem cron tasks.', '');
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
$this->filesystem->cron();
|
||||
mtrace('done.');
|
||||
}
|
||||
|
||||
@@ -689,8 +689,7 @@ abstract class advanced_testcase extends base_testcase {
|
||||
* @param int $matchuserid The userid to match.
|
||||
*/
|
||||
protected function runAdhocTasks($matchclass = '', $matchuserid = null) {
|
||||
global $CFG, $DB;
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
global $DB;
|
||||
|
||||
$params = [];
|
||||
if (!empty($matchclass)) {
|
||||
@@ -729,7 +728,7 @@ abstract class advanced_testcase extends base_testcase {
|
||||
$task->set_cron_lock($cronlock);
|
||||
}
|
||||
|
||||
cron_prepare_core_renderer();
|
||||
\core\cron::prepare_core_renderer();
|
||||
$this->setUser($user);
|
||||
|
||||
$task->execute();
|
||||
|
||||
+8
-49
@@ -176,61 +176,20 @@ function get_moodle_cookie() {
|
||||
* value 'reset' means reset internal static caches.
|
||||
* @param stdClass $course full course record, null means $SITE
|
||||
* @param bool $leavepagealone If specified, stops it messing with global page object
|
||||
* @deprecated since 4.2. Use \core\core::setup_user() instead.
|
||||
* @return void
|
||||
*/
|
||||
function cron_setup_user($user = null, $course = null, $leavepagealone = false) {
|
||||
global $CFG, $SITE, $PAGE;
|
||||
|
||||
if (!CLI_SCRIPT && !$leavepagealone) {
|
||||
throw new coding_exception('Function cron_setup_user() cannot be used in normal requests!');
|
||||
}
|
||||
|
||||
static $cronuser = NULL;
|
||||
static $cronsession = NULL;
|
||||
debugging(
|
||||
'The cron_setup_user() function is deprecated. ' .
|
||||
'Please use \core\cron::setup_user() and reset_user_cache() as appropriate instead.',
|
||||
DEBUG_DEVELOPER
|
||||
);
|
||||
|
||||
if ($user === 'reset') {
|
||||
$cronuser = null;
|
||||
$cronsession = null;
|
||||
\core\session\manager::init_empty_session();
|
||||
\core\cron::reset_user_cache();
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($cronuser)) {
|
||||
/// ignore admins timezone, language and locale - use site default instead!
|
||||
$cronuser = get_admin();
|
||||
$cronuser->timezone = $CFG->timezone;
|
||||
$cronuser->lang = '';
|
||||
$cronuser->theme = '';
|
||||
unset($cronuser->description);
|
||||
|
||||
$cronsession = new stdClass();
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
// Cached default cron user (==modified admin for now).
|
||||
\core\session\manager::init_empty_session();
|
||||
\core\session\manager::set_user($cronuser);
|
||||
$GLOBALS['SESSION'] = $cronsession;
|
||||
|
||||
} else {
|
||||
// Emulate real user session - needed for caps in cron.
|
||||
if ($GLOBALS['USER']->id != $user->id) {
|
||||
\core\session\manager::init_empty_session();
|
||||
\core\session\manager::set_user($user);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO MDL-19774 relying on global $PAGE in cron is a bad idea.
|
||||
// Temporary hack so that cron does not give fatal errors.
|
||||
if (!$leavepagealone) {
|
||||
$PAGE = new moodle_page();
|
||||
if ($course) {
|
||||
$PAGE->set_course($course);
|
||||
} else {
|
||||
$PAGE->set_course($SITE);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: it should be possible to improve perf by caching some limited number of users here ;-)
|
||||
|
||||
\core\cron::setup_user($user, $course, $leavepagealone);
|
||||
}
|
||||
|
||||
+4
-4
@@ -164,7 +164,7 @@ function stats_cron_daily($maxdays=1) {
|
||||
$defaultfproleid = (int)$CFG->defaultfrontpageroleid;
|
||||
|
||||
mtrace("Running daily statistics gathering, starting at $timestart:");
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
|
||||
$days = 0;
|
||||
$total = 0;
|
||||
@@ -664,7 +664,7 @@ function stats_cron_weekly() {
|
||||
$DB->delete_records_select('stats_user_weekly', "timeend > $timestart");
|
||||
|
||||
mtrace("Running weekly statistics gathering, starting at $timestart:");
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
|
||||
$weeks = 0;
|
||||
while ($now > $nextstartweek) {
|
||||
@@ -807,7 +807,7 @@ function stats_cron_monthly() {
|
||||
|
||||
|
||||
mtrace("Running monthly statistics gathering, starting at $timestart:");
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
|
||||
$months = 0;
|
||||
while ($now > $nextstartmonth) {
|
||||
@@ -1068,7 +1068,7 @@ function stats_get_next_month_start($time) {
|
||||
function stats_clean_old() {
|
||||
global $DB;
|
||||
mtrace("Running stats cleanup tasks...");
|
||||
cron_trace_time_and_memory();
|
||||
\core\cron::trace_time_and_memory();
|
||||
$deletebefore = stats_get_base_monthly();
|
||||
|
||||
// delete dailies older than 3 months (to be safe)
|
||||
|
||||
@@ -1108,9 +1108,6 @@ EOF;
|
||||
* @param string $taskname Name of task e.g. 'mod_whatever\task\do_something'
|
||||
*/
|
||||
public function i_run_the_scheduled_task($taskname) {
|
||||
global $CFG;
|
||||
require_once("{$CFG->libdir}/cronlib.php");
|
||||
|
||||
$task = \core\task\manager::get_scheduled_task($taskname);
|
||||
if (!$task) {
|
||||
throw new DriverException('The "' . $taskname . '" scheduled task does not exist');
|
||||
@@ -1118,7 +1115,7 @@ EOF;
|
||||
|
||||
// Do setup for cron task.
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Get lock.
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
@@ -1138,7 +1135,7 @@ EOF;
|
||||
|
||||
try {
|
||||
// Prepare the renderer.
|
||||
cron_prepare_core_renderer();
|
||||
\core\cron::prepare_core_renderer();
|
||||
|
||||
// Discard task output as not appropriate for Behat output!
|
||||
ob_start();
|
||||
@@ -1146,13 +1143,13 @@ EOF;
|
||||
ob_end_clean();
|
||||
|
||||
// Restore the previous renderer.
|
||||
cron_prepare_core_renderer(true);
|
||||
\core\cron::prepare_core_renderer(true);
|
||||
|
||||
// Mark task complete.
|
||||
\core\task\manager::scheduled_task_complete($task);
|
||||
} catch (Exception $e) {
|
||||
// Restore the previous renderer.
|
||||
cron_prepare_core_renderer(true);
|
||||
\core\cron::prepare_core_renderer(true);
|
||||
|
||||
// Mark task failed and throw exception.
|
||||
\core\task\manager::scheduled_task_failed($task);
|
||||
@@ -1175,11 +1172,10 @@ EOF;
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function i_run_all_adhoc_tasks() {
|
||||
global $CFG, $DB;
|
||||
require_once("{$CFG->libdir}/cronlib.php");
|
||||
global $DB;
|
||||
|
||||
// Do setup for cron task.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Discard task output as not appropriate for Behat output!
|
||||
ob_start();
|
||||
@@ -1193,7 +1189,7 @@ EOF;
|
||||
ob_clean();
|
||||
|
||||
// Run the task.
|
||||
cron_run_inner_adhoc_task($task);
|
||||
\core\cron::run_inner_adhoc_task($task);
|
||||
|
||||
// Check whether the task record still exists.
|
||||
// If a task was successful it will be removed.
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Tests for core\cron.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2023 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \core\cron
|
||||
*/
|
||||
class cron_test extends \advanced_testcase {
|
||||
/**
|
||||
* Reset relevant caches between tests.
|
||||
*/
|
||||
public function setUp(): void {
|
||||
cron::reset_user_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the setup_user function.
|
||||
*
|
||||
* @covers ::setup_user
|
||||
* @covers ::reset_user_cache
|
||||
*/
|
||||
public function test_setup_user(): void {
|
||||
// This function uses the $GLOBALS super global. Disable the VariableNameLowerCase sniff for this function.
|
||||
// phpcs:disable moodle.NamingConventions.ValidVariableName.VariableNameLowerCase
|
||||
global $PAGE, $USER, $SESSION, $SITE, $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$admin = get_admin();
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
cron::setup_user();
|
||||
$this->assertSame($admin->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertSame($CFG->timezone, $USER->timezone);
|
||||
$this->assertSame('', $USER->lang);
|
||||
$this->assertSame('', $USER->theme);
|
||||
$SESSION->test1 = true;
|
||||
$adminsession = $SESSION;
|
||||
$adminuser = $USER;
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user(null, $course);
|
||||
$this->assertSame($admin->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($course->id));
|
||||
$this->assertSame($adminsession, $SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user($user1);
|
||||
$this->assertSame($user1->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertObjectNotHasAttribute('test1', $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$usersession1 = $SESSION;
|
||||
$SESSION->test2 = true;
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user($user1);
|
||||
$this->assertSame($user1->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertSame($usersession1, $SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user($user2);
|
||||
$this->assertSame($user2->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($usersession1, $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$usersession2 = $SESSION;
|
||||
$usersession2->test3 = true;
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user($user2, $course);
|
||||
$this->assertSame($user2->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($course->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($usersession1, $SESSION);
|
||||
$this->assertSame($usersession2, $SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user($user1);
|
||||
$this->assertSame($user1->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($usersession1, $SESSION);
|
||||
$this->assertEmpty((array)$SESSION);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user();
|
||||
$this->assertSame($admin->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertSame($adminsession, $SESSION);
|
||||
$this->assertSame($adminuser, $USER);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::reset_user_cache();
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron::setup_user();
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($adminuser, $USER);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
// phpcs:enable
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,20 @@ namespace core;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class sessionlib_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* @covers ::cron_setup_user
|
||||
*/
|
||||
public function test_cron_setup_user() {
|
||||
// This function uses the $GLOBALS super global. Disable the VariableNameLowerCase sniff for this function.
|
||||
// phpcs:disable moodle.NamingConventions.ValidVariableName.VariableNameLowerCase
|
||||
|
||||
global $PAGE, $USER, $SESSION, $SITE, $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// NOTE: this function contains some static caches, let's reset first.
|
||||
cron_setup_user('reset');
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
|
||||
$admin = get_admin();
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
@@ -39,6 +47,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
cron_setup_user();
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($admin->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertSame($CFG->timezone, $USER->timezone);
|
||||
@@ -53,6 +62,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user(null, $course);
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($admin->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($course->id));
|
||||
$this->assertSame($adminsession, $SESSION);
|
||||
@@ -62,6 +72,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user($user1);
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($user1->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
@@ -75,6 +86,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user($user1);
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($user1->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
@@ -85,6 +97,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user($user2);
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($user2->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
@@ -98,6 +111,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user($user2, $course);
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($user2->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($course->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
@@ -109,6 +123,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user($user1);
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($user1->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
@@ -120,6 +135,7 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user();
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($admin->id, $USER->id);
|
||||
$this->assertSame($PAGE->context, \context_course::instance($SITE->id));
|
||||
$this->assertSame($adminsession, $SESSION);
|
||||
@@ -130,18 +146,22 @@ class sessionlib_test extends \advanced_testcase {
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user('reset');
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
cron_setup_user();
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
$this->assertNotSame($adminsession, $SESSION);
|
||||
$this->assertNotSame($adminuser, $USER);
|
||||
$this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
|
||||
$this->assertSame($GLOBALS['SESSION'], $SESSION);
|
||||
$this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
|
||||
$this->assertSame($GLOBALS['USER'], $USER);
|
||||
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
require_once($CFG->libdir . '/statslib.php');
|
||||
require_once($CFG->libdir . '/cronlib.php');
|
||||
require_once(__DIR__ . '/fixtures/stats_events.php');
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,22 +14,18 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
namespace core\task;
|
||||
|
||||
/**
|
||||
* Unit tests for the cron.
|
||||
* Unit tests for the file_temp_cleanup task.
|
||||
*
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright 2013 Tim Gusak <tim.gusak@remote-learner.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \core\task\file_temp_cleanup_task
|
||||
*/
|
||||
class cronlib_test extends \basic_testcase {
|
||||
class file_temp_cleanup_task_test extends \basic_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for cron_delete_from_temp.
|
||||
@@ -144,6 +140,7 @@ class cronlib_test extends \basic_testcase {
|
||||
* @dataProvider cron_delete_from_temp_provider
|
||||
* @param array $nodes List of files and directories
|
||||
* @param array $expected The expected results
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function test_cron_delete_from_temp($nodes, $expected) {
|
||||
global $CFG;
|
||||
@@ -91,6 +91,7 @@ information provided here is intended especially for developers.
|
||||
- groups_get_members()
|
||||
- groups_get_groups_members()
|
||||
groups_print_activity_menu() will now only return groups where particiation == true.
|
||||
* Cron utility functions have been moved from the global namespaces to the \core\cron class.
|
||||
|
||||
=== 4.1 ===
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class send_email_task extends scheduled_task {
|
||||
// Keep track of which emails failed to send.
|
||||
$users = $this->get_unique_users();
|
||||
foreach ($users as $user) {
|
||||
cron_setup_user($user);
|
||||
\core\cron::setup_user($user);
|
||||
|
||||
$hascontent = false;
|
||||
$renderable = new \message_email\output\email_digest($user);
|
||||
@@ -99,7 +99,7 @@ class send_email_task extends scheduled_task {
|
||||
}
|
||||
}
|
||||
}
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$users->close();
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ class feedback_test extends \advanced_testcase {
|
||||
public function test_conversion_task() {
|
||||
$this->require_ghostscript();
|
||||
$this->resetAfterTest();
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
|
||||
@@ -2744,7 +2744,7 @@ class assign {
|
||||
|
||||
// Override the language and timezone of the "current" user, so that
|
||||
// mail is customised for the receiver.
|
||||
cron_setup_user($user, $course);
|
||||
\core\cron::setup_user($user, $course);
|
||||
|
||||
// Context lookups are already cached.
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
@@ -2824,7 +2824,7 @@ class assign {
|
||||
}
|
||||
mtrace('Done processing ' . count($submissions) . ' assignment submissions');
|
||||
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Free up memory just to be sure.
|
||||
unset($courses);
|
||||
|
||||
@@ -1562,7 +1562,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// First run cron so there are no messages waiting to be sent (from other tests).
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
\assign::cron();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
@@ -1580,7 +1580,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->mark_submission($teacher, $assign, $student, 50.0);
|
||||
|
||||
$this->expectOutputRegex('/Done processing 1 assignment submissions/');
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$sink = $this->redirectMessages();
|
||||
\assign::cron();
|
||||
$messages = $sink->get_messages();
|
||||
@@ -1604,7 +1604,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// First run cron so there are no messages waiting to be sent (from other tests).
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
\assign::cron();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
@@ -1623,7 +1623,7 @@ class locallib_test extends \advanced_testcase {
|
||||
'sendstudentnotifications' => 0,
|
||||
]);
|
||||
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$sink = $this->redirectMessages();
|
||||
\assign::cron();
|
||||
$messages = $sink->get_messages();
|
||||
@@ -1635,7 +1635,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// First run cron so there are no messages waiting to be sent (from other tests).
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
\assign::cron();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
@@ -1653,14 +1653,14 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->mark_submission($teacher, $assign, $student, 50.0);
|
||||
|
||||
$this->expectOutputRegex('/Done processing 1 assignment submissions/');
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
\assign::cron();
|
||||
|
||||
// Regrade.
|
||||
$this->mark_submission($teacher, $assign, $student, 50.0);
|
||||
|
||||
$this->expectOutputRegex('/Done processing 1 assignment submissions/');
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$sink = $this->redirectMessages();
|
||||
\assign::cron();
|
||||
$messages = $sink->get_messages();
|
||||
@@ -1677,7 +1677,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// First run cron so there are no messages waiting to be sent (from other tests).
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
\assign::cron();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
@@ -1700,7 +1700,7 @@ class locallib_test extends \advanced_testcase {
|
||||
'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE,
|
||||
]);
|
||||
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$sink = $this->redirectMessages();
|
||||
\assign::cron();
|
||||
$messages = $sink->get_messages();
|
||||
@@ -1714,7 +1714,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$assign->testable_apply_grade_to_user($submission, $student->id, 0);
|
||||
|
||||
// Now run cron and see that one message was sent.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$sink = $this->redirectMessages();
|
||||
$this->expectOutputRegex('/Done processing 1 assignment submissions/');
|
||||
\assign::cron();
|
||||
@@ -1729,7 +1729,7 @@ class locallib_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// First run cron so there are no messages waiting to be sent (from other tests).
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
\assign::cron();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
@@ -1750,7 +1750,7 @@ class locallib_test extends \advanced_testcase {
|
||||
\phpunit_util::stop_message_redirection();
|
||||
|
||||
// Now run cron and see that one message was sent.
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$this->preventResetByRollback();
|
||||
$sink = $this->redirectEvents();
|
||||
$this->expectOutputRegex('/Done processing 1 assignment submissions/');
|
||||
|
||||
@@ -183,7 +183,7 @@ abstract class send_notification extends adhoc_task {
|
||||
foreach ($this->get_recipients() as $recipient) {
|
||||
try {
|
||||
\core_user::require_active_user($recipient, true, true);
|
||||
cron_setup_user($recipient);
|
||||
\core\cron::setup_user($recipient);
|
||||
} catch (moodle_exception $e) {
|
||||
// Skip sending.
|
||||
continue;
|
||||
@@ -192,7 +192,7 @@ abstract class send_notification extends adhoc_task {
|
||||
$this->send_notification_to_current_user();
|
||||
}
|
||||
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -329,7 +329,7 @@ class cron_task extends \core\task\scheduled_task {
|
||||
|
||||
$send = false;
|
||||
// Setup this user so that the capabilities are cached, and environment matches receiving user.
|
||||
cron_setup_user($user);
|
||||
\core\cron::setup_user($user);
|
||||
|
||||
list($individualpostdata, $digestpostdata) = $this->fetch_posts_for_user($user);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ trait mod_forum_tests_cron_trait {
|
||||
|
||||
// Note, we cannot use expectOutputRegex because it only allows for a single RegExp.
|
||||
ob_start();
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$cron = new \mod_forum\task\cron_task();
|
||||
$cron->execute();
|
||||
$output = ob_get_contents();
|
||||
|
||||
@@ -54,7 +54,7 @@ class scheduled_allocator_test extends \advanced_testcase {
|
||||
|
||||
$workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
|
||||
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Let the students add submissions.
|
||||
$this->workshop->switch_phase(\workshop::PHASE_SUBMISSION);
|
||||
@@ -94,7 +94,7 @@ class scheduled_allocator_test extends \advanced_testcase {
|
||||
|
||||
$workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
|
||||
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Let the students add submissions.
|
||||
$this->workshop->switch_phase(\workshop::PHASE_SUBMISSION);
|
||||
|
||||
@@ -32,7 +32,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/workshop/lib.php'); // Include the code to test.
|
||||
require_once($CFG->dirroot . '/mod/workshop/locallib.php'); // Include the code to test.
|
||||
require_once($CFG->dirroot . '/lib/cronlib.php'); // Include the code to test.
|
||||
require_once(__DIR__ . '/../fixtures/testable.php');
|
||||
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class cron_task_test extends \advanced_testcase {
|
||||
|
||||
// Execute the cron.
|
||||
ob_start();
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$cron = new \mod_workshop\task\cron_task();
|
||||
$cron->execute();
|
||||
$output = ob_get_contents();
|
||||
@@ -96,7 +96,7 @@ class cron_task_test extends \advanced_testcase {
|
||||
// Execute the cron.
|
||||
$eventsink = $this->redirectEvents();
|
||||
ob_start();
|
||||
cron_setup_user();
|
||||
\core\cron::setup_user();
|
||||
$cron = new \mod_workshop\task\cron_task();
|
||||
$cron->execute();
|
||||
ob_end_clean();
|
||||
|
||||
@@ -75,10 +75,10 @@ class send_schedule extends adhoc_task {
|
||||
|
||||
// Handle schedule configuration as to who the report should be viewed as.
|
||||
if ($scheduleuserviewas === schedule::REPORT_VIEWAS_CREATOR) {
|
||||
cron_setup_user(core_user::get_user($schedule->get('usercreated')));
|
||||
\core\cron::setup_user(core_user::get_user($schedule->get('usercreated')));
|
||||
$scheduleattachment = helper::get_schedule_report_file($schedule);
|
||||
} else if ($scheduleuserviewas !== schedule::REPORT_VIEWAS_RECIPIENT) {
|
||||
cron_setup_user(core_user::get_user($scheduleuserviewas));
|
||||
\core\cron::setup_user(core_user::get_user($scheduleuserviewas));
|
||||
$scheduleattachment = helper::get_schedule_report_file($schedule);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ class send_schedule extends adhoc_task {
|
||||
if ($scheduleattachment !== null) {
|
||||
helper::send_schedule_message($schedule, $user, $scheduleattachment);
|
||||
} else {
|
||||
cron_setup_user($user);
|
||||
\core\cron::setup_user($user);
|
||||
|
||||
if ($schedulereportempty === schedule::REPORT_EMPTY_DONT_SEND &&
|
||||
helper::get_schedule_report_count($schedule) === 0) {
|
||||
@@ -123,6 +123,6 @@ class send_schedule extends adhoc_task {
|
||||
$this->log_finish('Sending schedule complete');
|
||||
|
||||
// Restore cron user to original state.
|
||||
cron_setup_user($originaluser);
|
||||
\core\cron::setup_user($originaluser);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ abstract class engine {
|
||||
$now = manager::get_current_time();
|
||||
if ($now - $lastprogress >= manager::DISPLAY_INDEXING_PROGRESS_EVERY) {
|
||||
$lastprogress = $now;
|
||||
// The first date format is the same used in cron_trace_time_and_memory().
|
||||
// The first date format is the same used in \core\cron::trace_time_and_memory().
|
||||
$options['progress']->output(date('H:i:s', (int)$now) . ': Done to ' . userdate(
|
||||
$lastindexeddoc, get_string('strftimedatetimeshort', 'langconfig')), 1);
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2023031400.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2023031400.02; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.2dev+ (Build: 20230314)'; // Human-friendly version name
|
||||
|
||||
Reference in New Issue
Block a user