Merge branch 'MDL-61063-master' of https://github.com/sammarshallou/moodle

This commit is contained in:
Eloy Lafuente (stronk7) 2018-01-15 22:02:08 +01:00
commit d7ba45290b
8 changed files with 238 additions and 44 deletions

View File

@ -0,0 +1,69 @@
<?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/>.
/**
* Script clears the fail delay for a task and reschedules its next execution.
*
* @package tool_task
* @copyright 2017 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_OUTPUT_BUFFERING', true);
require('../../../config.php');
require_once($CFG->libdir.'/cronlib.php');
// Basic security checks.
require_login();
$context = context_system::instance();
require_capability('moodle/site:config', $context);
// Get task and check the parameter is valid.
$taskname = required_param('task', PARAM_RAW_TRIMMED);
$task = \core\task\manager::get_scheduled_task($taskname);
if (!$task) {
print_error('cannotfindinfo', 'error', $taskname);
}
// If actually doing the clear, then carry out the task and redirect to the scheduled task page.
if (optional_param('confirm', 0, PARAM_INT)) {
require_sesskey();
\core\task\manager::clear_fail_delay($task);
redirect(new moodle_url('/admin/tool/task/scheduledtasks.php'));
}
// Start output.
$PAGE->set_url(new moodle_url('/admin/tool/task/schedule_task.php'));
$PAGE->set_context($context);
$PAGE->navbar->add(get_string('scheduledtasks', 'tool_task'), new moodle_url('/admin/tool/task/scheduledtasks.php'));
$PAGE->navbar->add(s($task->get_name()));
$PAGE->navbar->add(get_string('clear'));
echo $OUTPUT->header();
// The initial request just shows the confirmation page; we don't do anything further unless
// they confirm.
echo $OUTPUT->confirm(get_string('clearfaildelay_confirm', 'tool_task', $task->get_name()),
new single_button(new moodle_url('/admin/tool/task/clear_fail_delay.php',
array('task' => $taskname, 'confirm' => 1, 'sesskey' => sesskey())),
get_string('clear')),
new single_button(new moodle_url('/admin/tool/task/scheduledtasks.php'),
get_string('cancel'), false));
echo $OUTPUT->footer();

View File

@ -25,6 +25,7 @@
$string['asap'] = 'ASAP';
$string['backtoscheduledtasks'] = 'Back to scheduled tasks';
$string['blocking'] = 'Blocking';
$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';
$string['default'] = 'Default';

View File

@ -112,6 +112,14 @@ class tool_task_renderer extends plugin_renderer_base {
get_string('runnow', 'tool_task')), 'task-runnow');
}
$clearfail = '';
if ($task->get_fail_delay()) {
$clearfail = html_writer::div(html_writer::link(
new moodle_url('/admin/tool/task/clear_fail_delay.php',
array('task' => get_class($task), 'sesskey' => sesskey())),
get_string('clear')), 'task-clearfaildelay');
}
$row = new html_table_row(array(
$namecell,
$componentcell,
@ -123,7 +131,7 @@ class tool_task_renderer extends plugin_renderer_base {
new html_table_cell($task->get_day()),
new html_table_cell($task->get_day_of_week()),
new html_table_cell($task->get_month()),
new html_table_cell($task->get_fail_delay()),
new html_table_cell($task->get_fail_delay() . $clearfail),
new html_table_cell($customised)));
// Cron-style values must always be LTR.

View File

@ -10,6 +10,7 @@
direction: ltr;
}
#page-admin-tool-task-scheduledtasks .task-runnow {
#page-admin-tool-task-scheduledtasks .task-runnow,
#page-admin-tool-task-scheduledtasks .task-clearfaildelay {
font-size: 0.75em;
}

View File

@ -0,0 +1,53 @@
<?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/>.
/**
* Behat step definitions for scheduled task administration.
*
* @package tool_task
* @copyright 2017 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
/**
* Behat step definitions for scheduled task administration.
*
* @package tool_task
* @copyright 2017 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_tool_task extends behat_base {
/**
* Set a fake fail delay for a scheduled task.
*
* @Given /^the scheduled task "(?P<task_name>[^"]+)" has a fail delay of "(?P<seconds_number>\d+)" seconds$/
* @param string $task Task classname
* @param int $seconds Fail delay time in seconds
*/
public function scheduled_task_has_fail_delay_seconds($task, $seconds) {
global $DB;
$id = $DB->get_field('task_scheduled', 'id', ['classname' => $task], IGNORE_MISSING);
if (!$id) {
throw new Exception('Unknown scheduled task: ' . $task);
}
$DB->set_field('task_scheduled', 'faildelay', $seconds, ['id' => $id]);
}
}

View File

@ -0,0 +1,25 @@
@tool @tool_task
Feature: Clear scheduled task fail delay
In order to stop failures from delaying a scheduled task run
As an admin
I need to be able to clear the fail delay on a task
Background:
Given the scheduled task "\core\task\send_new_user_passwords_task" has a fail delay of "60" seconds
And I log in as "admin"
And I navigate to "Scheduled tasks" node in "Site administration > Server"
Scenario: Clear fail delay
When I click on "Clear" "text" in the "Send new user passwords" "table_row"
And I should see "Are you sure you want to clear the fail delay"
And I press "Clear"
Then I should not see "60" in the "Send new user passwords" "table_row"
And I should not see "Clear" in the "Send new user passwords" "table_row"
Scenario: Cancel clearing the fail delay
When I click on "Clear" "text" in the "Send new user passwords" "table_row"
And I press "Cancel"
Then I should see "60" in the "Send new user passwords" "table_row"
And I should see "Clear" in the "Send new user passwords" "table_row"

View File

@ -89,10 +89,7 @@ class manager {
$validtasks = array();
foreach ($tasks as $taskid => $task) {
$classname = get_class($task);
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($task);
$validtasks[] = $classname;
@ -188,10 +185,7 @@ class manager {
public static function configure_scheduled_task(scheduled_task $task) {
global $DB;
$classname = get_class($task);
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($task);
$original = $DB->get_record('task_scheduled', array('classname'=>$classname), 'id', MUST_EXIST);
@ -211,10 +205,7 @@ class manager {
*/
public static function record_from_scheduled_task($task) {
$record = new \stdClass();
$record->classname = get_class($task);
if (strpos($record->classname, '\\') !== 0) {
$record->classname = '\\' . $record->classname;
}
$record->classname = self::get_canonical_class_name($task);
$record->component = $task->get_component();
$record->blocking = $task->is_blocking();
$record->customised = $task->is_customised();
@ -239,10 +230,7 @@ class manager {
*/
public static function record_from_adhoc_task($task) {
$record = new \stdClass();
$record->classname = get_class($task);
if (strpos($record->classname, '\\') !== 0) {
$record->classname = '\\' . $record->classname;
}
$record->classname = self::get_canonical_class_name($task);
$record->id = $task->get_id();
$record->component = $task->get_component();
$record->blocking = $task->is_blocking();
@ -261,10 +249,7 @@ class manager {
* @return \core\task\adhoc_task
*/
public static function adhoc_task_from_record($record) {
$classname = $record->classname;
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($record->classname);
if (!class_exists($classname)) {
debugging("Failed to load task: " . $classname, DEBUG_DEVELOPER);
return false;
@ -301,10 +286,7 @@ class manager {
* @return \core\task\scheduled_task
*/
public static function scheduled_task_from_record($record) {
$classname = $record->classname;
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($record->classname);
if (!class_exists($classname)) {
debugging("Failed to load task: " . $classname, DEBUG_DEVELOPER);
return false;
@ -381,9 +363,7 @@ class manager {
public static function get_scheduled_task($classname) {
global $DB;
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($classname);
// We are just reading - so no locks required.
$record = $DB->get_record('task_scheduled', array('classname'=>$classname), '*', IGNORE_MISSING);
if (!$record) {
@ -401,9 +381,7 @@ class manager {
public static function get_adhoc_tasks($classname) {
global $DB;
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($classname);
// We are just reading - so no locks required.
$records = $DB->get_records('task_adhoc', array('classname' => $classname));
@ -601,10 +579,7 @@ class manager {
$delay = 86400;
}
$classname = get_class($task);
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($task);
$task->set_next_run_time(time() + $delay);
$task->set_fail_delay($delay);
@ -657,10 +632,7 @@ class manager {
$delay = 86400;
}
$classname = get_class($task);
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($task);
$record = $DB->get_record('task_scheduled', array('classname' => $classname));
$record->nextruntime = time() + $delay;
@ -673,6 +645,23 @@ class manager {
$task->get_lock()->release();
}
/**
* Clears the fail delay for the given task and updates its next run time based on the schedule.
*
* @param scheduled_task $task Task to reset
* @throws \dml_exception If there is a database error
*/
public static function clear_fail_delay(scheduled_task $task) {
global $DB;
$record = new \stdClass();
$record->id = $DB->get_field('task_scheduled', 'id',
['classname' => self::get_canonical_class_name($task)]);
$record->nextruntime = $task->get_next_scheduled_time();
$record->faildelay = 0;
$DB->update_record('task_scheduled', $record);
}
/**
* This function indicates that a scheduled task was completed successfully and should be rescheduled.
*
@ -681,10 +670,7 @@ class manager {
public static function scheduled_task_complete(scheduled_task $task) {
global $DB;
$classname = get_class($task);
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
$classname = self::get_canonical_class_name($task);
$record = $DB->get_record('task_scheduled', array('classname' => $classname));
if ($record) {
$record->lastruntime = time();
@ -731,4 +717,21 @@ class manager {
$record = $DB->get_record('config', array('name'=>'scheduledtaskreset'));
return $record && (intval($record->value) > $starttime);
}
/**
* Gets class name for use in database table. Always begins with a \.
*
* @param string|task_base $taskorstring Task object or a string
*/
protected static function get_canonical_class_name($taskorstring) {
if (is_string($taskorstring)) {
$classname = $taskorstring;
} else {
$classname = get_class($taskorstring);
}
if (strpos($classname, '\\') !== 0) {
$classname = '\\' . $classname;
}
return $classname;
}
}

View File

@ -467,4 +467,38 @@ class core_scheduled_task_testcase extends advanced_testcase {
// There should only be two items in the array, '.' and '..'.
$this->assertEquals(2, count($filesarray));
}
/**
* Test that the function to clear the fail delay from a task works correctly.
*/
public function test_clear_fail_delay() {
$this->resetAfterTest();
// Get an example task to use for testing. Task is set to run every minute by default.
$taskname = '\core\task\send_new_user_passwords_task';
// Pretend task started running and then failed 3 times.
$before = time();
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
for ($i = 0; $i < 3; $i ++) {
$task = \core\task\manager::get_scheduled_task($taskname);
$lock = $cronlockfactory->get_lock('\\' . get_class($task), 10);
$task->set_lock($lock);
\core\task\manager::scheduled_task_failed($task);
}
// Confirm task is now delayed by several minutes.
$task = \core\task\manager::get_scheduled_task($taskname);
$this->assertEquals(240, $task->get_fail_delay());
$this->assertGreaterThan($before + 230, $task->get_next_run_time());
// Clear the fail delay and re-get the task.
\core\task\manager::clear_fail_delay($task);
$task = \core\task\manager::get_scheduled_task($taskname);
// There should be no delay and it should run within the next minute.
$this->assertEquals(0, $task->get_fail_delay());
$this->assertLessThan($before + 70, $task->get_next_run_time());
}
}