mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 08:55:15 +02:00
MDL-70975 task: adhoctasks.php - queued ad hoc tasks report
Similar to scheduled tasks report core\task\manager::adhoc_task_from_record() now raises moodle_exception
This commit is contained in:
parent
6d9aaa8412
commit
85323070e7
@ -60,7 +60,7 @@ $report = system_report_factory::create(task_logs::class, context_system::instan
|
||||
|
||||
if (!empty($filter)) {
|
||||
$report->set_filter_values([
|
||||
'task_log:name_values' => $filter,
|
||||
'task_log:name_values' => trim($filter, '\\'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
60
admin/tool/task/adhoctasks.php
Normal file
60
admin/tool/task/adhoctasks.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Ad hoc task list.
|
||||
*
|
||||
* @package tool_task
|
||||
* @copyright Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('adhoctasks');
|
||||
|
||||
$failedonly = optional_param('failedonly', false, PARAM_BOOL);
|
||||
$classname = optional_param('classname', null, PARAM_RAW);
|
||||
|
||||
$renderer = $PAGE->get_renderer('tool_task');
|
||||
|
||||
if ($classname) {
|
||||
$pageurl = new moodle_url('/admin/tool/task/adhoctasks.php');
|
||||
$PAGE->navbar->add(get_string('adhoctasks', 'tool_task'), $pageurl);
|
||||
$PAGE->navbar->add($classname, $PAGE->url);
|
||||
|
||||
$tasks = core\task\manager::get_adhoc_tasks($classname, $failedonly);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
if (!get_config('core', 'cron_enabled')) {
|
||||
echo $renderer->cron_disabled();
|
||||
}
|
||||
|
||||
echo $renderer->adhoc_tasks_class_table($classname, $tasks, ['failedonly' => $failedonly]);
|
||||
} else {
|
||||
$summary = core\task\manager::get_adhoc_tasks_summary();
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
if (!get_config('core', 'cron_enabled')) {
|
||||
echo $renderer->cron_disabled();
|
||||
}
|
||||
|
||||
echo $renderer->adhoc_tasks_summary_table($summary);
|
||||
}
|
||||
echo $OUTPUT->footer();
|
@ -24,11 +24,17 @@
|
||||
|
||||
$string['adhoc'] = 'Ad hoc';
|
||||
$string['adhoctaskid'] = 'Ad hoc task ID: {$a}';
|
||||
$string['adhoctaskrun'] = 'Ad hoc task run initiated';
|
||||
$string['adhoctasks'] = 'Ad hoc tasks';
|
||||
$string['adhoctasksdue'] = 'Ad hoc tasks due';
|
||||
$string['adhoctasksfailed'] = 'Ad hoc tasks failed';
|
||||
$string['adhoctasksfuture'] = 'Future ad hoc tasks';
|
||||
$string['adhoctasksrunning'] = 'Ad hoc tasks running';
|
||||
$string['asap'] = 'ASAP';
|
||||
$string['adhocempty'] = 'Ad hoc task queue is empty';
|
||||
$string['adhocqueuesize'] = 'Ad hoc task queue has {$a} tasks';
|
||||
$string['adhocqueueold'] = 'Oldest unprocessed task is {$a->age}, which is more than {$a->max}';
|
||||
$string['backtoadhoctasks'] = 'Back to ad hoc tasks';
|
||||
$string['backtoscheduledtasks'] = 'Back to scheduled tasks';
|
||||
$string['blocking'] = 'Blocking';
|
||||
$string['cannotfindthepathtothecli'] = 'Cannot find the path to the PHP CLI executable so task execution aborted. Set the \'Path to PHP CLI\' setting in Site administration / Server / System paths.';
|
||||
@ -51,31 +57,46 @@ $string['edittaskschedule'] = 'Edit task schedule: {$a}';
|
||||
$string['enablerunnow'] = 'Allow \'Run now\' for scheduled tasks';
|
||||
$string['enablerunnow_desc'] = 'Allows administrators to run a single scheduled task immediately, rather than waiting for it to run as scheduled. The feature requires \'Path to PHP CLI\' (pathtophp) to be set in System paths. The task runs on the web server, so you may wish to disable this feature to avoid potential performance issues.';
|
||||
$string['faildelay'] = 'Fail delay';
|
||||
$string['failed'] = 'Failed';
|
||||
$string['fromcomponent'] = 'From component: {$a}';
|
||||
$string['hostname'] = 'Host name';
|
||||
$string['lastcronstart'] = 'Time since last cron run: {$a}';
|
||||
$string['lastruntime'] = 'Last run';
|
||||
$string['lastupdated'] = 'Last updated {$a}.';
|
||||
$string['nextruntime'] = 'Next run';
|
||||
$string['noclassname'] = 'Class name not specified';
|
||||
$string['notasks'] = 'No tasks to run';
|
||||
$string['payload'] = 'Payload';
|
||||
$string['pid'] = 'PID';
|
||||
$string['plugindisabled'] = 'Plugin disabled';
|
||||
$string['pluginname'] = 'Scheduled task configuration';
|
||||
$string['resettasktodefaults'] = 'Reset task schedule to defaults';
|
||||
$string['resettasktodefaults_help'] = 'This will discard any local changes and revert the schedule for this task back to its original settings.';
|
||||
$string['run_adhoctasks'] = 'Run ad hoc tasks';
|
||||
$string['runningalltasks'] = 'Running all tasks';
|
||||
$string['runningfailedtasks'] = 'Running failed tasks';
|
||||
$string['runningtasks'] = 'Tasks running now';
|
||||
$string['runnow'] = 'Run now';
|
||||
$string['runagain'] = 'Run again';
|
||||
$string['runadhoc_confirm'] = 'Tasks will run on the web server and may take some time to complete.';
|
||||
$string['runadhoc'] = 'Run ad hoc tasks now?';
|
||||
$string['runnow_confirm'] = 'Are you sure you want to run this task \'{$a}\' now? The task will run on the web server and may take some time to complete.';
|
||||
$string['runclassname'] = 'Run all';
|
||||
$string['runclassnamefailedonly'] = 'Run all failed';
|
||||
$string['runpattern'] = 'Run pattern';
|
||||
$string['scheduled'] = 'Scheduled';
|
||||
$string['scheduledtasks'] = 'Scheduled tasks';
|
||||
$string['scheduledtaskchangesdisabled'] = 'Modifications to the list of scheduled tasks have been prevented in Moodle configuration';
|
||||
$string['slowtask'] = 'Task has run for longer than {$a}';
|
||||
$string['showall'] = 'Show all';
|
||||
$string['showfailedonly'] = 'Show failed only';
|
||||
$string['showsummary'] = 'Show ad hoc tasks summary';
|
||||
$string['started'] = 'Started';
|
||||
$string['taskage'] = 'Run time';
|
||||
$string['taskdetails'] = 'Tasks running for more than {$a->time} (max {$a->maxtime}): {$a->count}';
|
||||
$string['taskdisabled'] = 'Task disabled';
|
||||
$string['taskfailures'] = '{$a} task(s) failing';
|
||||
$string['taskid'] = 'Task ID';
|
||||
$string['tasklogs'] = 'Task logs';
|
||||
$string['tasknofailures'] = 'There are no tasks failing';
|
||||
$string['taskrunningtime'] = 'Task has run for {$a}';
|
||||
|
@ -38,3 +38,12 @@ function tool_task_status_checks() : array {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to handle mtrace by outputting the text to normal browser window.
|
||||
*
|
||||
* @param string $message Message to output
|
||||
* @param string $eol End of line character
|
||||
*/
|
||||
function tool_task_mtrace_wrapper(string $message, string $eol): void {
|
||||
echo s($message . $eol);
|
||||
}
|
||||
|
@ -35,6 +35,279 @@ use core\task\scheduled_task;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_task_renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* This function will render a table with the summary of all adhoc tasks.
|
||||
*
|
||||
* @param array $summary
|
||||
* @return string HTML to output.
|
||||
*/
|
||||
public function adhoc_tasks_summary_table(array $summary): string {
|
||||
$adhocurl = '/admin/tool/task/adhoctasks.php';
|
||||
$adhocrunurl = '/admin/tool/task/run_adhoctasks.php';
|
||||
|
||||
// Main tasks table.
|
||||
$table = new html_table();
|
||||
$table->caption = get_string('adhoctasks', 'tool_task');
|
||||
$table->head = [
|
||||
get_string('component', 'tool_task') . ' / ' . get_string('classname', 'tool_task'),
|
||||
get_string('adhoctasksrunning', 'tool_task'),
|
||||
get_string('adhoctasksdue', 'tool_task'),
|
||||
get_string('adhoctasksfuture', 'tool_task'),
|
||||
get_string('adhoctasksfailed', 'tool_task'),
|
||||
get_string('nextruntime', 'tool_task'),
|
||||
];
|
||||
|
||||
$table->attributes['class'] = 'admintable generaltable';
|
||||
$table->colclasses = [];
|
||||
|
||||
// For each task entry (row) show action buttons/logs link depending on the user permissions.
|
||||
$data = [];
|
||||
$canruntasks = \core\task\manager::is_runnable() && get_config('tool_task', 'enablerunnow');
|
||||
foreach ($summary as $component => $classes) {
|
||||
// Component cell.
|
||||
$componentcell = new html_table_cell($component);
|
||||
$componentcell->header = true;
|
||||
$componentcell->id = "tasks-$component";
|
||||
$componentcell->colspan = 6;
|
||||
|
||||
$data[] = new html_table_row([$componentcell]);
|
||||
|
||||
foreach ($classes as $classname => $stats) {
|
||||
// Task class cell.
|
||||
$classbits = explode('\\', $classname);
|
||||
$classcontent = html_writer::link(
|
||||
new moodle_url($adhocurl, ['classname' => $classname]),
|
||||
end($classbits)
|
||||
);
|
||||
$classcell = new html_table_cell($classcontent);
|
||||
$classcell->header = true;
|
||||
$classcell->class = "task-class-summary text-ltr";
|
||||
|
||||
$duecontent = $stats['due'];
|
||||
if ($canruntasks && ($stats['due'] > 0 || $stats['failed'] > 0)) {
|
||||
$duecontent .= html_writer::div(
|
||||
html_writer::link(
|
||||
new moodle_url(
|
||||
$adhocrunurl,
|
||||
['classname' => $classname]
|
||||
),
|
||||
get_string('runclassname', 'tool_task')
|
||||
),
|
||||
'task-runnow'
|
||||
);
|
||||
}
|
||||
|
||||
// Mark cell if has failed tasks.
|
||||
$failed = $stats['failed'];
|
||||
if ($canruntasks && $failed > 0) {
|
||||
$failed .= html_writer::div(
|
||||
html_writer::link(
|
||||
new moodle_url(
|
||||
$adhocrunurl,
|
||||
['classname' => $classname, 'failedonly' => 1]
|
||||
),
|
||||
get_string('runclassnamefailedonly', 'tool_task')
|
||||
),
|
||||
'task-runnow'
|
||||
);
|
||||
}
|
||||
$failedcell = new html_table_cell($failed);
|
||||
if ($failed > 0) {
|
||||
$failedcell->attributes['class'] = 'table-danger';
|
||||
}
|
||||
|
||||
// Prepares the next run time cell contents.
|
||||
$nextrun = '';
|
||||
if ($stats['due'] > 0) {
|
||||
$nextrun = get_string('asap', 'tool_task');
|
||||
} else if ($stats['nextruntime']) {
|
||||
$nextrun = userdate($stats['nextruntime']);
|
||||
}
|
||||
|
||||
$data[] = new html_table_row([
|
||||
$classcell,
|
||||
new html_table_cell($stats['running']),
|
||||
new html_table_cell($duecontent),
|
||||
new html_table_cell($stats['count'] - $stats['running'] - $stats['due']),
|
||||
$failedcell,
|
||||
new html_table_cell($nextrun),
|
||||
]);
|
||||
}
|
||||
}
|
||||
$table->data = $data;
|
||||
return html_writer::table($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will render a table with all the adhoc tasks for the class.
|
||||
*
|
||||
* @param string $classname
|
||||
* @param array $tasks - list of all adhoc tasks.
|
||||
* @param array|null $params
|
||||
* @return string HTML to output.
|
||||
*/
|
||||
public function adhoc_tasks_class_table(string $classname, array $tasks, ?array $params = []): string {
|
||||
$adhocurl = '/admin/tool/task/adhoctasks.php';
|
||||
$adhocrunurl = '/admin/tool/task/run_adhoctasks.php';
|
||||
$showloglink = \core\task\logmanager::has_log_report();
|
||||
$failedonly = !empty($params['failedonly']);
|
||||
$canruntasks = \core\task\manager::is_runnable() && get_config('tool_task', 'enablerunnow');
|
||||
|
||||
// Depending on the currently set parameters, set up toggle buttons.
|
||||
$failedorall = html_writer::link(
|
||||
new moodle_url(
|
||||
$adhocurl,
|
||||
array_merge($params, ['classname' => $classname, 'failedonly' => !$failedonly])
|
||||
),
|
||||
get_string($failedonly ? 'showall' : 'showfailedonly', 'tool_task')
|
||||
);
|
||||
|
||||
// Main tasks table.
|
||||
$table = $this->generate_adhoc_tasks_simple_table($tasks, $canruntasks);
|
||||
|
||||
$table->caption = "$classname "
|
||||
. get_string($failedonly ? 'adhoctasksfailed' : 'adhoctasks', 'tool_task');
|
||||
$table->head[3] .= " $failedorall"; // Spice up faildelay heading.
|
||||
|
||||
if ($showloglink) {
|
||||
// Insert logs as the second col.
|
||||
array_splice($table->head, 1, 0, [get_string('logs')]);
|
||||
array_walk($table->data, function ($row, $idx) use ($classname) {
|
||||
$loglink = '';
|
||||
$faildelaycell = $row->cells[3];
|
||||
if ($faildelaycell->attributes['class'] == 'table-danger') {
|
||||
// Failed task.
|
||||
$loglink = $this->output->action_icon(
|
||||
\core\task\logmanager::get_url_for_task_class($classname),
|
||||
new pix_icon('e/file-text', get_string('viewlogs', 'tool_task', $classname)
|
||||
));
|
||||
}
|
||||
|
||||
array_splice($row->cells, 1, 0, [new html_table_cell($loglink)]);
|
||||
});
|
||||
}
|
||||
|
||||
return html_writer::table($table)
|
||||
. html_writer::div(
|
||||
html_writer::link(
|
||||
new moodle_url(
|
||||
$adhocrunurl,
|
||||
array_merge($params, ['classname' => $classname])
|
||||
),
|
||||
get_string('runclassname', 'tool_task')
|
||||
),
|
||||
'task-runnow'
|
||||
)
|
||||
. html_writer::div(
|
||||
html_writer::link(
|
||||
new moodle_url(
|
||||
$adhocurl
|
||||
),
|
||||
get_string('showsummary', 'tool_task')
|
||||
),
|
||||
'task-show-summary'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will render a plain adhoc tasks table.
|
||||
*
|
||||
* @param array $tasks - list of adhoc tasks.
|
||||
* @return string HTML to output.
|
||||
*/
|
||||
public function adhoc_tasks_simple_table(array $tasks): string {
|
||||
$table = $this->generate_adhoc_tasks_simple_table($tasks);
|
||||
|
||||
return html_writer::table($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will render a plain adhoc tasks table.
|
||||
*
|
||||
* @param array $tasks - list of adhoc tasks.
|
||||
* @param bool $wantruntasks add 'Run now' link
|
||||
* @return html_table
|
||||
*/
|
||||
private function generate_adhoc_tasks_simple_table(array $tasks, bool $wantruntasks = false): html_table {
|
||||
$adhocrunurl = '/admin/tool/task/run_adhoctasks.php';
|
||||
$now = time();
|
||||
$failedstr = get_string('failed', 'tool_task');
|
||||
|
||||
// Main tasks table.
|
||||
$table = new html_table();
|
||||
$table->caption = get_string('adhoctasks', 'tool_task');
|
||||
$table->head = [
|
||||
get_string('taskid', 'tool_task'),
|
||||
get_string('nextruntime', 'tool_task'),
|
||||
get_string('payload', 'tool_task'),
|
||||
$failedstr
|
||||
];
|
||||
|
||||
$table->attributes['class'] = 'generaltable';
|
||||
$table->colclasses = [];
|
||||
|
||||
// For each task entry (row) show action buttons/logs link depending on the user permissions.
|
||||
$data = [];
|
||||
foreach ($tasks as $task) {
|
||||
$taskid = $task->get_id();
|
||||
$started = $task->get_timestarted();
|
||||
|
||||
// Task id cell.
|
||||
$taskidcellcontent = html_writer::span($taskid, 'task-id');
|
||||
$taskidcell = new html_table_cell($taskidcellcontent);
|
||||
$taskidcell->header = true;
|
||||
$taskidcell->id = "task-$taskid";
|
||||
|
||||
// Mark cell if task has failed.
|
||||
$faildelay = $task->get_fail_delay();
|
||||
$faildelaycell = new html_table_cell($faildelay ? $failedstr : '');
|
||||
if ($faildelay) {
|
||||
$faildelaycell->attributes['class'] = 'table-danger';
|
||||
}
|
||||
|
||||
// Prepares the next run time cell contents.
|
||||
$nextrun = get_string('started', 'tool_task');
|
||||
if (!$started) {
|
||||
$nextruntime = $task->get_next_run_time();
|
||||
$due = $nextruntime < $now;
|
||||
$nextrun = $due ? userdate($nextruntime) : get_string('asap', 'tool_task');
|
||||
|
||||
if ($wantruntasks && ($faildelay || $due)) {
|
||||
$nextrun .= ' '.html_writer::div(
|
||||
html_writer::link(
|
||||
new moodle_url(
|
||||
$adhocrunurl,
|
||||
['id' => $taskid]
|
||||
),
|
||||
get_string('runnow', 'tool_task')
|
||||
),
|
||||
'task-runnow'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$data[] = new html_table_row([
|
||||
$taskidcell,
|
||||
new html_table_cell($nextrun),
|
||||
new html_table_cell($task->get_custom_data_as_string()),
|
||||
$faildelaycell,
|
||||
]);
|
||||
}
|
||||
$table->data = $data;
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notification on ad hoc task run request.
|
||||
*
|
||||
* @return string HTML notification block for task initiated message
|
||||
*/
|
||||
public function adhoc_task_run(): string {
|
||||
return $this->output->notification(get_string('adhoctaskrun', 'tool_task'), 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will render one beautiful table with all the scheduled tasks.
|
||||
*
|
||||
@ -121,10 +394,14 @@ class tool_task_renderer extends plugin_renderer_base {
|
||||
|
||||
$faildelaycell = new html_table_cell($task->get_fail_delay());
|
||||
if ($task->get_fail_delay()) {
|
||||
$faildelaycell->text .= html_writer::div(html_writer::link(
|
||||
$faildelaycell->text .= html_writer::div(
|
||||
$this->output->single_button(
|
||||
new moodle_url('/admin/tool/task/clear_fail_delay.php',
|
||||
['task' => $classname, 'sesskey' => sesskey()]),
|
||||
get_string('clear')), 'task-clearfaildelay');
|
||||
['task' => $classname]),
|
||||
get_string('clear')
|
||||
),
|
||||
'task-runnow'
|
||||
);
|
||||
$faildelaycell->attributes['class'] = 'table-danger';
|
||||
}
|
||||
|
||||
|
157
admin/tool/task/run_adhoctasks.php
Normal file
157
admin/tool/task/run_adhoctasks.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Web run ad hoc task(s)
|
||||
*
|
||||
* This script runs a group or a single ad hoc task from the web UI.
|
||||
*
|
||||
* @package tool_task
|
||||
* @copyright Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('adhoctasks');
|
||||
|
||||
$runurl = '/admin/tool/task/run_adhoctasks.php';
|
||||
$tasksurl = '/admin/tool/task/adhoctasks.php';
|
||||
|
||||
// Allow execution of single task. This requires login and has different rules.
|
||||
$classname = optional_param('classname', null, PARAM_RAW);
|
||||
$failedonly = optional_param('failedonly', false, PARAM_BOOL);
|
||||
$taskid = optional_param('id', null, PARAM_INT);
|
||||
$confirmed = optional_param('confirm', 0, PARAM_INT);
|
||||
|
||||
if (!\core\task\manager::is_runnable()) {
|
||||
$redirecturl = new \moodle_url('/admin/settings.php', ['section' => 'systempaths']);
|
||||
throw new moodle_exception('cannotfindthepathtothecli', 'tool_task', $redirecturl->out());
|
||||
}
|
||||
|
||||
$params = ['classname' => $classname, 'failedonly' => $failedonly, 'id' => $taskid];
|
||||
|
||||
// Check input parameter id against all existing tasks.
|
||||
if ($taskid) {
|
||||
$record = $DB->get_record('task_adhoc', ['id' => $taskid]);
|
||||
if (!$record) {
|
||||
throw new \moodle_exception('invalidtaskid');
|
||||
}
|
||||
$classname = $record->classname;
|
||||
$heading = "Run $classname task Id $taskid";
|
||||
$tasks = [core\task\manager::adhoc_task_from_record($record)];
|
||||
} else {
|
||||
if (!$classname) {
|
||||
throw new \moodle_exception('noclassname', 'tool_task');
|
||||
}
|
||||
$heading = "Run $classname " . ($failedonly ? "failed" : "all")." tasks";
|
||||
$now = time();
|
||||
$tasks = array_filter(
|
||||
core\task\manager::get_adhoc_tasks($classname, $failedonly, true),
|
||||
function ($t) use ($now) {
|
||||
return $t->get_fail_delay() || $t->get_next_run_time() <= $now;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Start output.
|
||||
$context = context_system::instance();
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_heading($SITE->fullname);
|
||||
$PAGE->set_title($classname);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($heading);
|
||||
|
||||
if (!$tasks) {
|
||||
echo $OUTPUT->single_button($tasksurl,
|
||||
get_string('notasks', 'tool_task'),
|
||||
'get');
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
}
|
||||
|
||||
$renderer = $PAGE->get_renderer('tool_task');
|
||||
if (!get_config('core', 'cron_enabled')) {
|
||||
echo $renderer->cron_disabled();
|
||||
}
|
||||
echo $renderer->adhoc_tasks_simple_table($tasks);
|
||||
|
||||
// The initial request just shows the confirmation page; we don't do anything further unless
|
||||
// they confirm.
|
||||
if (!$confirmed) {
|
||||
echo $OUTPUT->confirm(get_string('runadhoc_confirm', 'tool_task'),
|
||||
new single_button(new moodle_url($runurl, array_merge($params, ['confirm' => 1])),
|
||||
get_string('runadhoc', 'tool_task')),
|
||||
new single_button(new moodle_url($tasksurl, $params),
|
||||
get_string('cancel'), false));
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
}
|
||||
|
||||
// Action requires session key.
|
||||
require_sesskey();
|
||||
|
||||
\core\session\manager::write_close();
|
||||
|
||||
// Prepare to handle output via mtrace.
|
||||
$CFG->mtrace_wrapper = 'tool_task_mtrace_wrapper';
|
||||
|
||||
// Run the specified tasks.
|
||||
if ($taskid) {
|
||||
$repeat = $DB->get_record('task_adhoc', ['id' => $taskid]);
|
||||
|
||||
echo html_writer::start_tag('pre');
|
||||
\core\task\manager::run_adhoc_from_cli($taskid);
|
||||
echo html_writer::end_tag('pre');
|
||||
} else {
|
||||
$repeat = core\task\manager::get_adhoc_tasks($classname, $failedonly, true);
|
||||
|
||||
// Run failed first (if any). We have to run them separately anyway,
|
||||
// because faildelay is observed if failed flag is not true.
|
||||
echo html_writer::tag('p', get_string('runningfailedtasks', 'tool_task'), ['class' => 'lead']);
|
||||
echo html_writer::start_tag('pre');
|
||||
\core\task\manager::run_all_adhoc_from_cli(true, $classname);
|
||||
echo html_writer::end_tag('pre');
|
||||
|
||||
if (!$failedonly) {
|
||||
echo html_writer::tag('p', get_string('runningalltasks', 'tool_task'), ['class' => 'lead']);
|
||||
echo html_writer::start_tag('pre');
|
||||
\core\task\manager::run_all_adhoc_from_cli(false, $classname);
|
||||
echo html_writer::end_tag('pre');
|
||||
}
|
||||
}
|
||||
|
||||
if ($repeat) {
|
||||
echo html_writer::div(
|
||||
$OUTPUT->single_button(
|
||||
new moodle_url($runurl, array_merge($params, ['confirm' => 1])),
|
||||
get_string('runagain', 'tool_task')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
echo html_writer::div(
|
||||
html_writer::link(
|
||||
new moodle_url($tasksurl, $taskid ? ['classname' => $classname] : []),
|
||||
get_string('backtoadhoctasks', 'tool_task')
|
||||
)
|
||||
);
|
||||
|
||||
echo $OUTPUT->footer();
|
@ -28,16 +28,6 @@ define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require('../../../config.php');
|
||||
|
||||
/**
|
||||
* Function used to handle mtrace by outputting the text to normal browser window.
|
||||
*
|
||||
* @param string $message Message to output
|
||||
* @param string $eol End of line character
|
||||
*/
|
||||
function tool_task_mtrace_wrapper($message, $eol) {
|
||||
echo s($message . $eol);
|
||||
}
|
||||
|
||||
// Allow execution of single task. This requires login and has different rules.
|
||||
$taskname = required_param('task', PARAM_RAW_TRIMMED);
|
||||
|
||||
|
@ -34,6 +34,15 @@ if ($hassiteconfig) {
|
||||
)
|
||||
);
|
||||
|
||||
$ADMIN->add(
|
||||
'taskconfig',
|
||||
new admin_externalpage(
|
||||
'adhoctasks',
|
||||
new lang_string('adhoctasks', 'tool_task'),
|
||||
"$CFG->wwwroot/$CFG->admin/tool/task/adhoctasks.php"
|
||||
)
|
||||
);
|
||||
|
||||
$ADMIN->add(
|
||||
'taskconfig',
|
||||
new admin_externalpage(
|
||||
|
@ -24,6 +24,9 @@
|
||||
*/
|
||||
namespace core\task;
|
||||
|
||||
use core\lock\lock;
|
||||
use core\lock\lock_factory;
|
||||
|
||||
define('CORE_TASK_TASKS_FILENAME', 'db/tasks.php');
|
||||
/**
|
||||
* Collection of task related methods.
|
||||
@ -317,12 +320,12 @@ class manager {
|
||||
*
|
||||
* @param \stdClass $record
|
||||
* @return \core\task\adhoc_task
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public static function adhoc_task_from_record($record) {
|
||||
$classname = self::get_canonical_class_name($record->classname);
|
||||
if (!class_exists($classname)) {
|
||||
debugging("Failed to load task: " . $classname, DEBUG_DEVELOPER);
|
||||
return false;
|
||||
throw new \moodle_exception('invalidtaskclassname', '', '', $record->classname);
|
||||
}
|
||||
$task = new $classname;
|
||||
if (isset($record->nextruntime)) {
|
||||
@ -474,18 +477,83 @@ class manager {
|
||||
* This function load the adhoc tasks for a given classname.
|
||||
*
|
||||
* @param string $classname
|
||||
* @param bool $failedonly
|
||||
* @param bool $skiprunning do not return tasks that are in the running state
|
||||
* @return array
|
||||
*/
|
||||
public static function get_adhoc_tasks($classname) {
|
||||
public static function get_adhoc_tasks(string $classname, bool $failedonly = false, bool $skiprunning = false): array {
|
||||
global $DB;
|
||||
|
||||
$classname = self::get_canonical_class_name($classname);
|
||||
// We are just reading - so no locks required.
|
||||
$records = $DB->get_records('task_adhoc', array('classname' => $classname));
|
||||
$conds[] = 'classname = ?';
|
||||
$params[] = self::get_canonical_class_name($classname);
|
||||
|
||||
if ($failedonly) {
|
||||
$conds[] = 'faildelay > 0';
|
||||
}
|
||||
if ($skiprunning) {
|
||||
$conds[] = 'timestarted IS NULL';
|
||||
}
|
||||
|
||||
// We are just reading - so no locks required.
|
||||
$sql = 'SELECT * FROM {task_adhoc}';
|
||||
if ($conds) {
|
||||
$sql .= ' WHERE '.implode(' AND ', $conds);
|
||||
}
|
||||
$rs = $DB->get_records_sql($sql, $params);
|
||||
return array_map(function($record) {
|
||||
return self::adhoc_task_from_record($record);
|
||||
}, $records);
|
||||
}, $rs);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns adhoc tasks summary per component classname
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_adhoc_tasks_summary(): array {
|
||||
global $DB;
|
||||
|
||||
$now = time();
|
||||
$records = $DB->get_records('task_adhoc');
|
||||
$summary = [];
|
||||
foreach ($records as $r) {
|
||||
if (!isset($summary[$r->component])) {
|
||||
$summary[$r->component] = [];
|
||||
}
|
||||
|
||||
if (isset($summary[$r->component][$r->classname])) {
|
||||
$classsummary = $summary[$r->component][$r->classname];
|
||||
} else {
|
||||
$classsummary = [
|
||||
'nextruntime' => null,
|
||||
'count' => 0,
|
||||
'failed' => 0,
|
||||
'running' => 0,
|
||||
'due' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$classsummary['count']++;
|
||||
$nextruntime = (int)$r->nextruntime;
|
||||
if (!$classsummary['nextruntime'] || $nextruntime < $classsummary['nextruntime']) {
|
||||
$classsummary['nextruntime'] = $nextruntime;
|
||||
}
|
||||
|
||||
if ((int)$r->timestarted > 0) {
|
||||
$classsummary['running']++;
|
||||
} else {
|
||||
if ((int)$r->faildelay > 0) {
|
||||
$classsummary['failed']++;
|
||||
}
|
||||
|
||||
if ($nextruntime <= $now) {
|
||||
$classsummary['due']++;
|
||||
}
|
||||
}
|
||||
|
||||
$summary[$r->component][$r->classname] = $classsummary;
|
||||
}
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -550,9 +618,10 @@ class manager {
|
||||
$records = $DB->get_records_sql('SELECT * from {task_adhoc} WHERE faildelay > ?', [$delay]);
|
||||
|
||||
foreach ($records as $record) {
|
||||
$task = self::adhoc_task_from_record($record);
|
||||
if ($task) {
|
||||
$tasks[] = $task;
|
||||
try {
|
||||
$tasks[] = self::adhoc_task_from_record($record);
|
||||
} catch (\moodle_exception $e) {
|
||||
debugging("Failed to load task: $record->classname", DEBUG_DEVELOPER, $e->getTrace());
|
||||
}
|
||||
}
|
||||
return $tasks;
|
||||
@ -781,9 +850,11 @@ class manager {
|
||||
continue;
|
||||
}
|
||||
|
||||
$task = self::adhoc_task_from_record($record);
|
||||
// Safety check in case the task in the DB does not match a real class (maybe something was uninstalled).
|
||||
if (!$task) {
|
||||
try {
|
||||
$task = self::adhoc_task_from_record($record);
|
||||
} catch (\moodle_exception $e) {
|
||||
debugging("Failed to load task: $record->classname", DEBUG_DEVELOPER);
|
||||
$lock->release();
|
||||
unset(self::$miniqueue[$taskid]);
|
||||
continue;
|
||||
@ -875,7 +946,7 @@ class manager {
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will get a (failed) adhoc task by id. The task will be handed out
|
||||
* This function will get an adhoc task by id. The task will be handed out
|
||||
* with an open lock - possibly on the entire cron process. Make sure you call either
|
||||
* {@see ::adhoc_task_failed} or {@see ::adhoc_task_complete} to release the lock and reschedule the task.
|
||||
*
|
||||
@ -886,7 +957,7 @@ class manager {
|
||||
public static function get_adhoc_task(int $taskid): ?adhoc_task {
|
||||
global $DB;
|
||||
|
||||
$record = $DB->get_record('task_adhoc', array('id' => $taskid));
|
||||
$record = $DB->get_record('task_adhoc', ['id' => $taskid]);
|
||||
if (!$record) {
|
||||
throw new \moodle_exception('invalidtaskid');
|
||||
}
|
||||
@ -894,11 +965,12 @@ class manager {
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
|
||||
if ($lock = $cronlockfactory->get_lock('adhoc_' . $record->id, 0)) {
|
||||
$task = self::adhoc_task_from_record($record);
|
||||
// Safety check in case the task in the DB does not match a real class (maybe something was uninstalled).
|
||||
if (!$task) {
|
||||
try {
|
||||
$task = self::adhoc_task_from_record($record);
|
||||
} catch (\moodle_exception $e) {
|
||||
$lock->release();
|
||||
throw new \moodle_exception('invalidtaskclassname');
|
||||
throw $e;
|
||||
}
|
||||
|
||||
self::set_locks($task, $lock, $cronlockfactory);
|
||||
@ -911,12 +983,12 @@ class manager {
|
||||
/**
|
||||
* This function will set locks on the task.
|
||||
*
|
||||
* @param \core\task\adhoc_task $task
|
||||
* @param \core\lock\lock $lock task lock
|
||||
* @param \core\lock\lock_factory $cronlockfactory
|
||||
* @param adhoc_task $task
|
||||
* @param lock $lock task lock
|
||||
* @param lock_factory $cronlockfactory
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
private static function set_locks($task, $lock, $cronlockfactory): void {
|
||||
private static function set_locks(adhoc_task $task, lock $lock, lock_factory $cronlockfactory): void {
|
||||
// The global cron lock is under the most contention so request it
|
||||
// as late as possible and release it as soon as possible.
|
||||
if (!$cronlock = $cronlockfactory->get_lock('core_cron', 10)) {
|
||||
@ -1418,11 +1490,11 @@ class manager {
|
||||
/**
|
||||
* Executes a cron from web invocation using PHP CLI.
|
||||
*
|
||||
* @param \core\task\task_base $task Task that be executed via CLI.
|
||||
* @param scheduled_task $task Task that be executed via CLI.
|
||||
* @return bool
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public static function run_from_cli(\core\task\task_base $task):bool {
|
||||
public static function run_from_cli(scheduled_task $task): bool {
|
||||
global $CFG;
|
||||
|
||||
if (!self::is_runnable()) {
|
||||
@ -1450,6 +1522,73 @@ class manager {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an ad hoc task from web invocation using PHP CLI.
|
||||
*
|
||||
* @param int $taskid Task to execute via CLI.
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public static function run_adhoc_from_cli(int $taskid) {
|
||||
// Shell-escaped task name.
|
||||
$taskarg = escapeshellarg("--id={$taskid}");
|
||||
|
||||
self::run_adhoc_from_cli_base($taskarg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes ad hoc tasks from web invocation using PHP CLI.
|
||||
*
|
||||
* @param bool|null $failedonly
|
||||
* @param string|null $classname Task class to execute via CLI.
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public static function run_all_adhoc_from_cli(?bool $failedonly = false, ?string $classname = null) {
|
||||
$taskargs = [];
|
||||
if ($failedonly) {
|
||||
$taskargs[] = '--failed';
|
||||
}
|
||||
if ($classname) {
|
||||
// Shell-escaped task select.
|
||||
$taskargs[] = escapeshellarg("--classname={$classname}");
|
||||
}
|
||||
|
||||
self::run_adhoc_from_cli_base($taskargs ? implode(' ', $taskargs) : '--execute');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an ad hoc task from web invocation using PHP CLI.
|
||||
*
|
||||
* @param string $taskarg Task to execute via CLI.
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
private static function run_adhoc_from_cli_base(string $taskarg): void {
|
||||
global $CFG;
|
||||
|
||||
if (!self::is_runnable()) {
|
||||
$redirecturl = new \moodle_url('/admin/settings.php', ['section' => 'systempaths']);
|
||||
throw new \moodle_exception('cannotfindthepathtothecli', 'tool_task', $redirecturl->out());
|
||||
}
|
||||
|
||||
// Shell-escaped path to the PHP binary.
|
||||
$phpbinary = escapeshellarg(self::find_php_cli_path());
|
||||
|
||||
// Shell-escaped path CLI script.
|
||||
$pathcomponents = [$CFG->dirroot, $CFG->admin, 'cli', 'adhoc_task.php'];
|
||||
$scriptpath = escapeshellarg(implode(DIRECTORY_SEPARATOR, $pathcomponents));
|
||||
|
||||
// Build the CLI command.
|
||||
$command = "{$phpbinary} {$scriptpath} {$taskarg} --force";
|
||||
|
||||
// We cannot run it in phpunit.
|
||||
if (PHPUNIT_TEST) {
|
||||
echo $command;
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute it.
|
||||
passthru($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given scheduled task record, this method will check to see if any overrides have
|
||||
* been applied in config and return a copy of the record with any overridden values.
|
||||
|
@ -91,7 +91,7 @@ class adhoc_task_test extends \advanced_testcase {
|
||||
$classname = get_class($task);
|
||||
|
||||
// The task will not be returned.
|
||||
$this->assertNull(manager::get_next_adhoc_task($now, true, "${classname}x"));
|
||||
$this->assertNull(manager::get_next_adhoc_task($now, true, "{$classname}notexists"));
|
||||
|
||||
// Get it from the scheduler.
|
||||
$task = manager::get_next_adhoc_task($now, true, $classname);
|
||||
@ -557,4 +557,52 @@ class adhoc_task_test extends \advanced_testcase {
|
||||
$this->assertEquals('Task 1', $task->get_custom_data_as_string());
|
||||
manager::adhoc_task_complete($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adhoc task run from CLI.
|
||||
* @covers ::run_adhoc_from_cli
|
||||
*/
|
||||
public function test_run_adhoc_from_cli() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$taskid = 1;
|
||||
|
||||
if (!manager::is_runnable()) {
|
||||
$this->markTestSkipped("Cannot run tasks");
|
||||
}
|
||||
|
||||
ob_start();
|
||||
manager::run_adhoc_from_cli($taskid);
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$this->assertMatchesRegularExpression(
|
||||
sprintf('!admin/cli/adhoc_task.php\W+--id=%d\W+--force!', $taskid),
|
||||
$output
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adhoc class run from CLI.
|
||||
* @covers ::run_all_adhoc_from_cli
|
||||
*/
|
||||
public function test_run_all_adhoc_from_cli() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$classname = 'fake';
|
||||
|
||||
if (!manager::is_runnable()) {
|
||||
$this->markTestSkipped("Cannot run tasks");
|
||||
}
|
||||
|
||||
ob_start();
|
||||
manager::run_all_adhoc_from_cli(false, $classname);
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$this->assertMatchesRegularExpression(
|
||||
sprintf('!admin/cli/adhoc_task.php\W+--classname=%s\W+--force!', $classname),
|
||||
$output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,13 @@ information provided here is intended especially for developers.
|
||||
needs to be rendered as is and not whitened out, the `.nofilter` CSS class needs to be applied to the icon.
|
||||
* Functions get_next_adhoc_task() and cron::run_adhoc_tasks() have additional parameter $classname to filter by the specified class.
|
||||
* Function cron::run_adhoc_tasks() has additional parameter $number to limit the number of the tasks to run.
|
||||
* Function get_adhoc_tasks() has additional parameter $failedonly to return only failed tasks.
|
||||
* admin/cli/adhoc_task.php has additional parameters:
|
||||
- id - to run individual tasks by id
|
||||
- classname - to run tasks by classname
|
||||
- taskslimit - to limit the number of tasks in one run
|
||||
- failed - to limit the run to only the tasks that failed in their previous run
|
||||
Can be mixed, apart from 'id' of course.
|
||||
|
||||
=== 4.1 ===
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user