diff --git a/admin/tool/task/classes/running_tasks_table.php b/admin/tool/task/classes/running_tasks_table.php new file mode 100644 index 0000000000000..d08f458f71dca --- /dev/null +++ b/admin/tool/task/classes/running_tasks_table.php @@ -0,0 +1,141 @@ +. + +/** + * Running tasks table. + * + * @package tool_task + * @copyright 2019 The Open University + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_task; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/tablelib.php'); + +/** + * Table to display list of running task. + * + * @copyright 2019 The Open University + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class running_tasks_table extends \table_sql { + + /** + * Constructor for the running tasks table. + */ + public function __construct() { + parent::__construct('runningtasks'); + + $columnheaders = [ + 'classname' => get_string('classname', 'tool_task'), + 'type' => get_string('tasktype', 'admin'), + 'time' => get_string('time'), + 'timestarted' => get_string('started', 'tool_task'), + 'hostname' => get_string('hostname', 'tool_task'), + 'pid' => get_string('pid', 'tool_task'), + ]; + $this->define_columns(array_keys($columnheaders)); + $this->define_headers(array_values($columnheaders)); + + // The name column is a header. + $this->define_header_column('classname'); + + // This table is not collapsible. + $this->collapsible(false); + + // Allow pagination. + $this->pageable(true); + } + + /** + * Query the db. Store results in the table object for use by build_table. + * + * @param int $pagesize size of page for paginated displayed table. + * @param bool $useinitialsbar do you want to use the initials bar. Bar + * will only be used if there is a fullname column defined for the table. + * @throws \dml_exception + */ + public function query_db($pagesize, $useinitialsbar = true) { + $sort = $this->get_sql_sort(); + $this->rawdata = \core\task\manager::get_running_tasks($sort); + } + + /** + * Format the classname cell. + * + * @param \stdClass $row + * @return string + */ + public function col_classname($row) : string { + $output = $row->classname; + if ($row->type == 'scheduled') { + if (class_exists($row->classname)) { + $task = new $row->classname; + if ($task instanceof \core\task\scheduled_task) { + $output .= \html_writer::tag('div', $task->get_name(), ['class' => 'task-class']); + } + } + } else if ($row->type == 'adhoc') { + $output .= \html_writer::tag('div', + get_string('adhoctaskid', 'tool_task', $row->id), ['class' => 'task-class']); + } + return $output; + } + + /** + * Format the type cell. + * + * @param \stdClass $row + * @return string + * @throws \coding_exception + */ + public function col_type($row) : string { + if ($row->type == 'scheduled') { + $output = \html_writer::span(get_string('scheduled', 'tool_task'), 'badge badge-primary'); + } else if ($row->type == 'adhoc') { + $output = \html_writer::span(get_string('adhoc', 'tool_task'), 'badge badge-warning'); + } else { + // This shouldn't ever happen. + $output = ''; + } + return $output; + } + + /** + * Format the time cell. + * + * @param \stdClass $row + * @return string + */ + public function col_time($row) : string { + return format_time($row->time); + } + + /** + * Format the timestarted cell. + * + * @param \stdClass $row + * @return string + */ + public function col_timestarted($row) : string { + return userdate($row->timestarted); + } +} diff --git a/admin/tool/task/lang/en/tool_task.php b/admin/tool/task/lang/en/tool_task.php index 797f2f1ce43df..be26065792852 100644 --- a/admin/tool/task/lang/en/tool_task.php +++ b/admin/tool/task/lang/en/tool_task.php @@ -22,6 +22,9 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['adhoc'] = 'Ad-hoc'; +$string['adhoctaskid'] = 'Ad-hoc task id: {$a}'; +$string['adhoctasks'] = 'Ad-hoc tasks'; $string['asap'] = 'ASAP'; $string['adhocempty'] = 'Ad hoc task queue is empty'; $string['adhocqueuesize'] = 'Ad hoc task queue has {$a} tasks'; @@ -32,6 +35,7 @@ $string['checkadhocqueue'] = 'Ad hoc task queue'; $string['checkcronrunning'] = 'Cron running'; $string['checkmaxfaildelay'] = 'Tasks max fail delay'; +$string['classname'] = 'Class name'; $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['corecomponent'] = 'Core'; @@ -47,18 +51,22 @@ $string['fromcomponent'] = 'From component: {$a}'; $string['hostname'] = 'Host name'; $string['lastruntime'] = 'Last run'; +$string['lastupdated'] = 'Last updated {$a}.'; $string['nextruntime'] = 'Next run'; $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['runningtasks'] = 'Tasks running now'; $string['runnow'] = 'Run now'; $string['runagain'] = 'Run again'; $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['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['started'] = 'Started'; $string['taskdisabled'] = 'Task disabled'; $string['taskfailures'] = '{$a} task(s) failing'; $string['tasklogs'] = 'Task logs'; diff --git a/admin/tool/task/runningtasks.php b/admin/tool/task/runningtasks.php new file mode 100644 index 0000000000000..d5c284274410e --- /dev/null +++ b/admin/tool/task/runningtasks.php @@ -0,0 +1,46 @@ +. + +/** + * Running task admin page. + * + * @package tool_task + * @copyright 2019 The Open University + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->libdir.'/tablelib.php'); + +$pageurl = new \moodle_url('/admin/tool/task/runningtasks.php'); +$heading = get_string('runningtasks', 'tool_task'); +$PAGE->set_url($pageurl); +$PAGE->set_context(context_system::instance()); +$PAGE->set_pagelayout('admin'); +$PAGE->set_title($heading); +$PAGE->set_heading($heading); + +admin_externalpage_setup('runningtasks'); + +echo $OUTPUT->header(); + +$table = new \tool_task\running_tasks_table(); +$table->baseurl = $pageurl; +$table->out(100, false); + +echo $OUTPUT->footer(); diff --git a/admin/tool/task/settings.php b/admin/tool/task/settings.php index ac9858ed61e21..3fd9669b81f7e 100644 --- a/admin/tool/task/settings.php +++ b/admin/tool/task/settings.php @@ -33,4 +33,13 @@ "$CFG->wwwroot/$CFG->admin/tool/task/scheduledtasks.php" ) ); + + $ADMIN->add( + 'taskconfig', + new admin_externalpage( + 'runningtasks', + new lang_string('runningtasks', 'tool_task'), + "$CFG->wwwroot/$CFG->admin/tool/task/runningtasks.php" + ) + ); } diff --git a/admin/tool/task/styles.css b/admin/tool/task/styles.css index 0e846ce3c5c47..f297fd6afaf11 100644 --- a/admin/tool/task/styles.css +++ b/admin/tool/task/styles.css @@ -1,4 +1,5 @@ -#page-admin-tool-task-scheduledtasks .task-class { +#page-admin-tool-task-scheduledtasks .task-class, +#page-admin-tool-task-runningtasks .task-class { display: block; padding: 0 0.5em; color: #888; diff --git a/admin/tool/task/tests/behat/running_tasks.feature b/admin/tool/task/tests/behat/running_tasks.feature new file mode 100644 index 0000000000000..e5db69ced50fe --- /dev/null +++ b/admin/tool/task/tests/behat/running_tasks.feature @@ -0,0 +1,40 @@ +@tool @tool_task +Feature: See running scheduled tasks + In order to configure scheduled tasks + As an admin + I need to see if tasks are running + + Background: + Given I log in as "admin" + + Scenario: If no task is running, I should see the corresponding message + Given I navigate to "Server > Tasks > Tasks running now" in site administration + Then I should see "Nothing to display" + + Scenario: If tasks are running, I should see task details + Given the following "tool_task > scheduled tasks" exist: + | classname | seconds | hostname | pid | + | \core\task\automated_backup_task | 121 | c69335460f7f | 1914 | + 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 > Tasks running now" in site administration + + # Check the scheduled task details. + Then I should see "Scheduled" in the "\core\task\automated_backup_task" "table_row" + And I should see "2 mins" in the "Automated backups" "table_row" + And I should see "c69335460f7f" in the "Automated backups" "table_row" + And I should see "1914" in the "Automated backups" "table_row" + + # Check the "asynchronous_backup_task" adhoc task details. + And I should see "Ad-hoc" in the "\core\task\asynchronous_backup_task" "table_row" + And I should see "2 hours" in the "core\task\asynchronous_backup_task" "table_row" + And I should see "c69335460f7f" in the "core\task\asynchronous_backup_task" "table_row" + And I should see "1915" in the "core\task\asynchronous_backup_task" "table_row" + + # Check the "asynchronous_restore_task" adhoc task details. + And I should see "Ad-hoc" in the "\core\task\asynchronous_restore_task" "table_row" + And I should see "2 days" in the "core\task\asynchronous_restore_task" "table_row" + And I should see "c69335460f7f" in the "core\task\asynchronous_restore_task" "table_row" + And I should see "1916" in the "core\task\asynchronous_restore_task" "table_row" diff --git a/admin/tool/task/tests/generator/behat_tool_task_generator.php b/admin/tool/task/tests/generator/behat_tool_task_generator.php new file mode 100644 index 0000000000000..307ad8103a6a9 --- /dev/null +++ b/admin/tool/task/tests/generator/behat_tool_task_generator.php @@ -0,0 +1,57 @@ +. + +/** + * Behat data generator for tool_task. + * + * @package tool_task + * @category test + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Behat data generator for tool_task. + * + * @package tool_task + * @category test + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_tool_task_generator extends behat_generator_base { + + /** + * Get a list of the entities that can be created. + + * @return array entity name => information about how to generate. + */ + protected function get_creatable_entities(): array { + return [ + 'scheduled tasks' => [ + 'singular' => 'scheduled task', + 'datagenerator' => 'scheduled_tasks', + 'required' => ['classname', 'seconds', 'hostname', 'pid'], + ], + 'adhoc tasks' => [ + 'singular' => 'adhoc task', + 'datagenerator' => 'adhoc_tasks', + 'required' => ['classname', 'seconds', 'hostname', 'pid'], + ], + ]; + } +} diff --git a/admin/tool/task/tests/generator/lib.php b/admin/tool/task/tests/generator/lib.php new file mode 100644 index 0000000000000..b9324c5db607a --- /dev/null +++ b/admin/tool/task/tests/generator/lib.php @@ -0,0 +1,69 @@ +. + +/** + * Tool task test data generator class + * + * @package tool_task + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Tool task test data generator class + * + * @package tool_task + * @copyright 2020 Mikhail Golenkov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_task_generator extends testing_module_generator { + + /** + * Mark a scheduled task as running. + * + * @param array $data Scheduled task properties + * @throws dml_exception + */ + public function create_scheduled_tasks($data) { + global $DB; + $conditions = ['classname' => $data['classname']]; + $record = $DB->get_record('task_scheduled', $conditions, '*', MUST_EXIST); + $record->timestarted = time() - $data['seconds']; + $record->hostname = $data['hostname']; + $record->pid = $data['pid']; + $DB->update_record('task_scheduled', $record); + } + + /** + * Mark an adhoc task as running. + * + * @param array $data Adhoc task properties + * @throws dml_exception + */ + public function create_adhoc_tasks($data) { + global $DB; + $adhoctask = (object)[ + 'classname' => $data['classname'], + 'nextruntime' => 0, + 'timestarted' => time() - $data['seconds'], + 'hostname' => $data['hostname'], + 'pid' => $data['pid'], + ]; + $DB->insert_record('task_adhoc', $adhoctask); + } +} diff --git a/admin/tool/task/version.php b/admin/tool/task/version.php index 1823273530900..151504cddcf70 100644 --- a/admin/tool/task/version.php +++ b/admin/tool/task/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2021052500; // The current plugin version (Date: YYYYMMDDXX) +$plugin->version = 2021052501; // The current plugin version (Date: YYYYMMDDXX) $plugin->requires = 2021052500; // Requires this Moodle version $plugin->component = 'tool_task'; // Full name of the plugin (used for diagnostics)