diff --git a/admin/tool/task/adhoctasks.php b/admin/tool/task/adhoctasks.php index 74a98e742d5..d967148fd81 100644 --- a/admin/tool/task/adhoctasks.php +++ b/admin/tool/task/adhoctasks.php @@ -28,6 +28,7 @@ require_once($CFG->libdir.'/adminlib.php'); admin_externalpage_setup('adhoctasks'); $failedonly = optional_param('failedonly', false, PARAM_BOOL); +$dueonly = optional_param('dueonly', false, PARAM_BOOL); $classname = optional_param('classname', null, PARAM_RAW); $renderer = $PAGE->get_renderer('tool_task'); @@ -37,7 +38,7 @@ if ($classname) { $PAGE->navbar->add(get_string('adhoctasks', 'tool_task'), $pageurl); $PAGE->navbar->add(s($classname), $PAGE->url); - $tasks = core\task\manager::get_adhoc_tasks($classname, $failedonly); + $tasks = core\task\manager::get_adhoc_tasks($classname, $failedonly, dueonly: $dueonly); echo $OUTPUT->header(); @@ -45,7 +46,7 @@ if ($classname) { echo $renderer->cron_disabled(); } - echo $renderer->adhoc_tasks_class_table($classname, $tasks, ['failedonly' => $failedonly]); + echo $renderer->adhoc_tasks_class_table($classname, $tasks, ['failedonly' => $failedonly , 'dueonly' => $dueonly ]); } else { $summary = core\task\manager::get_adhoc_tasks_summary(); diff --git a/admin/tool/task/delete_adhoctasks.php b/admin/tool/task/delete_adhoctasks.php new file mode 100644 index 00000000000..af838317ec2 --- /dev/null +++ b/admin/tool/task/delete_adhoctasks.php @@ -0,0 +1,46 @@ +. + +/** + * Script deletes an adhoc task. + * + * @package tool_task + * @copyright Catalyst + * @author Waleed ul hassan + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('NO_OUTPUT_BUFFERING', true); + +require('../../../config.php'); + +// Basic security checks. +require_admin(); +$context = context_system::instance(); + +// Get task and check the parameter is valid. +$taskid = required_param('taskid', PARAM_INT); +$task = \core\task\manager::get_adhoc_task($taskid); +if (!$task) { + throw new \moodle_exception('cannotfindinfo', 'error', $taskid); +} + +$returnurl = new moodle_url('/admin/tool/task/adhoctasks.php', + ['classname' => get_class($task)]); + +require_sesskey(); +\core\task\manager::delete_adhoc_task($taskid); +redirect($returnurl); diff --git a/admin/tool/task/lang/en/tool_task.php b/admin/tool/task/lang/en/tool_task.php index 0489f4ae2ed..808ddfde9b2 100644 --- a/admin/tool/task/lang/en/tool_task.php +++ b/admin/tool/task/lang/en/tool_task.php @@ -46,11 +46,13 @@ $string['checklongrunningtasks'] = 'Long running tasks'; $string['checklongrunningtaskcount'] = 'Long running tasks: {$a}'; $string['clearfaildelay_confirm'] = 'Are you sure you want to clear the fail delay for task \'{$a}\'? After clearing the delay, the task will run according to its normal schedule.'; $string['component'] = 'Component'; +$string['confirmdeletetaskwithid'] = 'Are you sure you want to delete the task with id: {$a->id}?'; $string['corecomponent'] = 'Core'; $string['crondisabled'] = 'Cron is disabled. No new tasks will be started. The system will not operate properly until it is enabled again.'; $string['cronok'] = 'Cron is running frequently'; $string['default'] = 'Default'; $string['defaultx'] = 'Default: {$a}'; +$string['deletetask'] = 'Delete task'; $string['disabled'] = 'Disabled'; $string['disabled_help'] = 'Disabled scheduled tasks are not executed from cron, however they can still be executed manually via the CLI tool.'; $string['edittaskschedule'] = 'Edit task schedule: {$a}'; @@ -85,6 +87,7 @@ $string['runadhoctasks'] = 'Run all \'{$a}\' tasks'; $string['runadhoctasksfailed'] = 'Run failed \'{$a}\' tasks'; $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['runclassnamedueonly'] = 'Run due only'; $string['runclassnamefailedonly'] = 'Run all failed'; $string['runpattern'] = 'Run pattern'; $string['scheduled'] = 'Scheduled'; @@ -140,3 +143,5 @@ $string['taskschedulemonth_help'] = 'Month field for task schedule. The field us * 1,5 Every January and May'; $string['privacy:metadata'] = 'The Scheduled task configuration plugin does not store any personal data.'; $string['viewlogs'] = 'View logs for {$a}'; +$string['actions'] = 'Actions'; +$string['deleteadhoctask'] = 'Delete ad hoc task {$a->id}'; diff --git a/admin/tool/task/renderer.php b/admin/tool/task/renderer.php index 02bb26c79a7..1a14556dd81 100644 --- a/admin/tool/task/renderer.php +++ b/admin/tool/task/renderer.php @@ -96,6 +96,16 @@ class tool_task_renderer extends plugin_renderer_base { ), 'task-runnow' ); + $duecontent .= html_writer::div( + html_writer::link( + new moodle_url( + $adhocrunurl, + ['classname' => $classname, 'dueonly' => 1] + ), + get_string('runclassnamedueonly', 'tool_task') + ), + 'task-runnow' + ); } // Mark cell if has failed tasks. @@ -233,6 +243,7 @@ class tool_task_renderer extends plugin_renderer_base { */ private function generate_adhoc_tasks_simple_table(array $tasks, bool $wantruntasks = false): html_table { $adhocrunurl = '/admin/tool/task/run_adhoctasks.php'; + $adhocdeleteurl = '/admin/tool/task/delete_adhoctasks.php'; $now = time(); $failedstr = get_string('failed', 'tool_task'); @@ -243,7 +254,9 @@ class tool_task_renderer extends plugin_renderer_base { get_string('taskid', 'tool_task'), get_string('nextruntime', 'tool_task'), get_string('payload', 'tool_task'), - $failedstr + $failedstr, + get_string('faildelay', 'tool_task'), + get_string('actions','tool_task'), ]; $table->attributes['class'] = 'generaltable'; @@ -263,9 +276,9 @@ class tool_task_renderer extends plugin_renderer_base { // Mark cell if task has failed. $faildelay = $task->get_fail_delay(); - $faildelaycell = new html_table_cell($faildelay ? $failedstr : ''); + $failedcell = new html_table_cell($faildelay ? $failedstr : ''); if ($faildelay) { - $faildelaycell->attributes['class'] = 'table-danger'; + $failedcell->attributes['class'] = 'table-danger'; } // Prepares the next run time cell contents. @@ -293,11 +306,38 @@ class tool_task_renderer extends plugin_renderer_base { } } + // Create fail delay cell. + $faildelaycellcontent = $faildelay; + $faildelaycell = new html_table_cell($faildelaycellcontent); + + if ($faildelay) { + $faildelaycell->attributes['class'] = 'table-danger'; + } + + // Add delete link with modal trigger. + $deletelink = html_writer::link( + new moodle_url($adhocdeleteurl, ['taskid' => $taskid, 'sesskey' => sesskey()]), + get_string('delete'), + [ + 'class' => 'btn btn-danger', + 'role' => 'button', + 'aria-label' => get_string('deleteadhoctask', 'tool_task', $taskid), + 'data-confirmation' => 'modal', + 'data-confirmation-type' => 'delete', + 'data-confirmation-title-str' => '["deleteadhoctask", "tool_task",{"id":'.$taskid.'}]', + 'data-confirmation-content-str' => '["confirmdeletetaskwithid", "tool_task", {"id":'.$taskid.'}]', + 'data-confirmation-yes-button-str' => '["delete", "core"]', + ] + ); + $deletecell = new html_table_cell($deletelink); + $data[] = new html_table_row([ $taskidcell, new html_table_cell($nextrun), new html_table_cell($task->get_custom_data_as_string()), + $failedcell, $faildelaycell, + $deletecell, ]); } $table->data = $data; diff --git a/admin/tool/task/run_adhoctasks.php b/admin/tool/task/run_adhoctasks.php index 7912f4b1beb..292c3427525 100644 --- a/admin/tool/task/run_adhoctasks.php +++ b/admin/tool/task/run_adhoctasks.php @@ -37,6 +37,7 @@ $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); +$dueonly = optional_param('dueonly', false, PARAM_BOOL); $taskid = optional_param('id', null, PARAM_INT); $confirmed = optional_param('confirm', 0, PARAM_INT); @@ -45,7 +46,7 @@ if (!\core\task\manager::is_runnable()) { throw new moodle_exception('cannotfindthepathtothecli', 'tool_task', $redirecturl->out()); } -$params = ['classname' => $classname, 'failedonly' => $failedonly, 'id' => $taskid]; +$params = ['classname' => $classname, 'failedonly' => $failedonly, 'dueonly' => $dueonly, 'id' => $taskid]; // Check input parameter id against all existing tasks. if ($taskid) { @@ -69,7 +70,7 @@ if ($taskid) { $now = time(); $tasks = array_filter( - core\task\manager::get_adhoc_tasks($classname, $failedonly, true), + core\task\manager::get_adhoc_tasks($classname, $failedonly, $dueonly, true), function ($t) use ($now) { return $t->get_fail_delay() || $t->get_next_run_time() <= $now; } @@ -130,14 +131,16 @@ if ($taskid) { \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); + $repeat = core\task\manager::get_adhoc_tasks($classname, $failedonly, $dueonly, 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', ['class' => 'task-output']); - \core\task\manager::run_all_adhoc_from_cli(true, $classname); - echo html_writer::end_tag('pre'); + if (!$dueonly) { + echo html_writer::tag('p', get_string('runningfailedtasks', 'tool_task'), ['class' => 'lead']); + echo html_writer::start_tag('pre', ['class' => 'task-output']); + \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']); diff --git a/admin/tool/task/tests/behat/delete_task.feature b/admin/tool/task/tests/behat/delete_task.feature new file mode 100644 index 00000000000..18762129a16 --- /dev/null +++ b/admin/tool/task/tests/behat/delete_task.feature @@ -0,0 +1,21 @@ +@tool @tool_task @javascript +Feature: Delete an adhoc task + In order to manage adhoc tasks + As an admin + I need to be able to delete adhoc tasks + + Scenario: Delete an existing adhoc task + Given I log in as "admin" + And the following "tool_task > adhoc tasks" exist: + | classname | seconds | hostname | pid | + | \core\task\asynchronous_backup_task | 7201 | c69335460f7f | 1915 | + | \core\task\asynchronous_restore_task | 172800 | c69335460f7f | 1916 | + And I navigate to "Server > Tasks > Ad hoc tasks" in site administration + Then "asynchronous_backup_task" "table_row" should exist + And "asynchronous_restore_task" "table_row" should exist + And I follow "asynchronous_backup_task" + When I follow "Delete" + And I click on "Delete" "button" in the ".modal-dialog" "css_element" + Then I navigate to "Server > Tasks > Ad hoc tasks" in site administration + And "asynchronous_backup_task" "table_row" should not exist + And "asynchronous_restore_task" "table_row" should exist diff --git a/lib/classes/task/manager.php b/lib/classes/task/manager.php index 7411235394f..b2679047eac 100644 --- a/lib/classes/task/manager.php +++ b/lib/classes/task/manager.php @@ -507,11 +507,17 @@ 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 + * @param bool $failedonly Return only failed tasks + * @param bool $skiprunning Do not return tasks that are in the running state + * @param bool $dueonly Return only tasks that are due to run (nextruntime < now) * @return array */ - public static function get_adhoc_tasks(string $classname, bool $failedonly = false, bool $skiprunning = false): array { + public static function get_adhoc_tasks( + string $classname, + bool $failedonly = false, + bool $skiprunning = false, + bool $dueonly = false + ): array { global $DB; $conds[] = 'classname = ?'; @@ -519,6 +525,8 @@ class manager { if ($failedonly) { $conds[] = 'faildelay > 0'; + } else if ($dueonly) { + $conds[] = 'faildelay = 0'; } if ($skiprunning) { $conds[] = 'timestarted IS NULL'; @@ -950,6 +958,17 @@ class manager { return null; } + /** + * This function will delete an adhoc task by id. The task will be removed + * from the database. + * + * @param int $taskid + */ + public static function delete_adhoc_task(int $taskid): void { + global $DB; + $DB->delete_records('task_adhoc', ['id' => $taskid]); + } + /** * This function will set locks on the task. *