diff --git a/README.md b/README.md index 4687aa0..9463dae 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,17 @@ https://github.com/catalyst/moodle-auth_outage/issues 3. Go to `Dashboard ► Site administration ► Plugins ► Authentication ► Manage authentication`, enable the `Outage manager` plugin and place it on the top. +4. If you need to use the IP Blocking, please add the following lines into your `config.php` +after your `$CFG->dataroot` is set: + +``` +if (file_exists("$CFG->dataroot/climaintenance.php")) { + $CFG->dirroot = __DIR__; + require("$CFG->dataroot/climaintenance.php"); +} else { + $CFG->auth_outage_check = 1; +} +``` How to use ---------- diff --git a/classes/dml/outagedb.php b/classes/dml/outagedb.php index f785558..cad6f73 100644 --- a/classes/dml/outagedb.php +++ b/classes/dml/outagedb.php @@ -135,7 +135,7 @@ class outagedb { } // Trigger outages modified events. - outagelib::outages_modified(); + outagelib::prepare_next_outage(); // All done, return the id. return $outage->id; @@ -165,7 +165,7 @@ class outagedb { calendar::delete($id); // Trigger events. - outagelib::outages_modified(); + outagelib::prepare_next_outage(); } /** @@ -358,4 +358,35 @@ class outagedb { // Allowing multiple records still raises an internal error. return (count($data) == 0) ? null : new outage(array_shift($data)); } + + /** + * Gets an ongoing outage (between start and stop time but not finished). + * @param int|null $time Timestamp considered to check for outages, null for current date/time. + * @return outage|null The outage or null if no active outages were found. + * @throws coding_exception + */ + public static function get_ongoing($time = null) { + global $DB; + + if ($time === null) { + $time = time(); + } + if (!is_int($time) || ($time <= 0)) { + throw new coding_exception('$time must be null or a positive int.', $time); + } + + $data = $DB->get_records_select( + 'auth_outage', + 'starttime <= :datetime1 AND :datetime2 <= stoptime AND finished IS NULL', + ['datetime1' => $time, 'datetime2' => $time, 'datetime3' => $time], + 'starttime ASC, stoptime DESC, title ASC', + '*', + 0, + 1 + ); + + // Not using $DB->get_record_select instead because there is no 'limit' parameter. + // Allowing multiple records still raises an internal error. + return (count($data) == 0) ? null : new outage(array_shift($data)); + } } diff --git a/classes/local/controllers/infopage.php b/classes/local/controllers/infopage.php index d7c65cc..5b30c98 100644 --- a/classes/local/controllers/infopage.php +++ b/classes/local/controllers/infopage.php @@ -124,26 +124,25 @@ class infopage { /** * Updates the static info page by (re)creating or deleting it as needed. + * @param outage|null $outage Outage or null if no scheduled outage. * @param string|null $file File to update. Null to use default. * @throws coding_exception * @throws file_exception */ - public static function update_static_page($file = null) { + public static function update_static_page($outage, $file = null) { if (is_null($file)) { $file = self::get_defaulttemplatefile(); } if (!is_string($file)) { throw new coding_exception('$file is not a string.', $file); } + if (!is_null($outage) && !($outage instanceof outage)) { + throw new coding_exception('$outage must be null or an outage object.'); + } - $outage = outagedb::get_next_starting(); if (is_null($outage)) { if (file_exists($file)) { - if (is_file($file) && is_writable($file)) { - unlink($file); - } else { - throw new file_exception('Cannot remove: '.$file); - } + unlink($file); } } else { self::save_static_page($outage, $file); diff --git a/classes/local/outagelib.php b/classes/local/outagelib.php index 1aebd11..26d513e 100644 --- a/classes/local/outagelib.php +++ b/classes/local/outagelib.php @@ -30,6 +30,8 @@ use auth_outage\local\controllers\infopage; use auth_outage\output\renderer; use coding_exception; use Exception; +use file_exception; +use invalid_parameter_exception; use stdClass; defined('MOODLE_INTERNAL') || die(); @@ -130,29 +132,36 @@ class outagelib { */ public static function get_config_defaults() { return [ + 'allowedips' => '', + 'css' => '', 'default_autostart' => '0', 'default_duration' => (string)(60 * 60), 'default_warning_duration' => (string)(60 * 60), 'default_title' => get_string('defaulttitlevalue', 'auth_outage'), 'default_description' => get_string('defaultdescriptionvalue', 'auth_outage'), - 'css' => '', ]; } /** * Executed when outages are modified (created, updated or deleted). */ - public static function outages_modified() { - infopage::update_static_page(); - self::update_maintenance_later(); + public static function prepare_next_outage() { + // If there is an ongoing outage, prepare it instead. + $outage = outagedb::get_ongoing(); + if (is_null($outage)) { + $outage = outagedb::get_next_starting(); + } + infopage::update_static_page($outage); + self::update_climaintenance_code($outage); + self::update_maintenance_later($outage); } /** * Calls Moodle API - set_maintenance_later() to set when the next outage starts. + * @param outage|null $outage Outage or null if no scheduled outage. */ - private static function update_maintenance_later() { - $next = outagedb::get_next_autostarting(); - if (is_null($next)) { + private static function update_maintenance_later($outage) { + if (is_null($outage) || !$outage->autostart) { unset_config('maintenance_later'); } else { $message = get_config('moodle', 'maintenance_message'); @@ -162,7 +171,7 @@ class outagelib { // We cannot do much if forced config, but the logs will show the error. unset_config('maintenance_message'); } - set_config('maintenance_later', $next->starttime); + set_config('maintenance_later', $outage->starttime); } } @@ -187,4 +196,90 @@ class outagelib { // Nothing preventing the injection. return true; } + + /** + * Generates the code to put in sitedata/climaintenance.php when needed. + * @param int $starttime Outage start time. + * @param int $stoptime Outage stop time. + * @param string $allowedips List of IPs allowed. + * @return string + * @throws invalid_parameter_exception + */ + public static function create_climaintenancephp_code($starttime, $stoptime, $allowedips) { + if (!is_int($starttime) || !is_int($stoptime)) { + throw new invalid_parameter_exception('Make sure $startime and $stoptime are integers.'); + } + if (!is_string($allowedips) || (trim($allowedips) == '')) { + throw new invalid_parameter_exception('$allowedips must be a valid string.'); + } + // I know Moodle validation would clean up this field, but just in case, let's ensure no + // single-quotes (and double for the sake of it) are present otherwise it would break the code. + $allowedips = str_replace('\'"', '', $allowedips); + + $code = <<<'EOT' += {{STARTTIME}}) { + if (!defined('CLI_SCRIPT') || !CLI_SCRIPT) { + define('MOODLE_INTERNAL', true); + require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (!remoteip_in_list('{{ALLOWEDIPS}}')) { + header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); + header('Status: 503 Moodle under maintenance'); + header('Retry-After: 300'); + header('Content-type: text/html; charset=utf-8'); + header('X-UA-Compatible: IE=edge'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); + header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Accept-Ranges: none'); + echo ''; + if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { + require($CFG->dataroot.'/climaintenance.template.html'); + exit(0); + } + // The file above should always exist, but just in case... + die('We are currently under maintentance, please try again later.'); + } + } +} +$CFG->auth_outage_check = 1; +EOT; + $search = ['{{STARTTIME}}', '{{ALLOWEDIPS}}', '{{YOURIP}}']; + $replace = [$starttime, $allowedips, getremoteaddr('n/a')]; + return str_replace($search, $replace, $code); + } + + /** + * Updates the static info page by (re)creating or deleting it as needed. + * @param outage|null $outage Outage or null if no scheduled outage. + * @throws coding_exception + * @throws file_exception + */ + public static function update_climaintenance_code($outage) { + global $CFG; + $file = $CFG->dataroot.'/climaintenance.php'; + + if (!is_null($outage) && !($outage instanceof outage)) { + throw new coding_exception('$outage must be null or an outage object.'); + } + + $config = self::get_config(); + $allowedips = trim($config->allowedips); + + if (is_null($outage) || ($allowedips == '')) { + if (file_exists($file)) { + unlink($file); + } + } else { + $code = self::create_climaintenancephp_code($outage->starttime, $outage->stoptime, $allowedips); + + $dir = dirname($file); + if (!file_exists($dir) || !is_dir($dir)) { + throw new file_exception('Directory must exists: '.$dir); + } + file_put_contents($file, $code); + } + } } diff --git a/classes/task/update_static_page.php b/classes/task/update_static_page.php index 561cb6d..bf67067 100644 --- a/classes/task/update_static_page.php +++ b/classes/task/update_static_page.php @@ -26,6 +26,7 @@ namespace auth_outage\task; use auth_outage\local\controllers\infopage; +use auth_outage\local\outagelib; use core\task\scheduled_task; defined('MOODLE_INTERNAL') || die(); @@ -51,6 +52,6 @@ class update_static_page extends scheduled_task { * Executes the event. */ public function execute() { - infopage::update_static_page(); + outagelib::prepare_next_outage(); } } diff --git a/lang/en/auth_outage.php b/lang/en/auth_outage.php index 6800303..0fd726d 100644 --- a/lang/en/auth_outage.php +++ b/lang/en/auth_outage.php @@ -84,6 +84,10 @@ $string['infountil'] = 'Until:'; $string['infostart'] = 'start'; $string['infostartofwarning'] = 'start of warning'; $string['infopagestaticgenerated'] = 'This warning was generated on {$a->time}.'; +$string['allowedipsempty'] = 'When the allowed IPs list is empty we will not block anyone. You can add your own IP address ({$a->ip}) and block all other IPs.'; +$string['allowedipshasmyip'] = 'Your IP ({$a->ip}) is in the list and you will not be blocked out during an Outage.'; +$string['allowedipshasntmyip'] = 'Your IP ({$a->ip}) is not in the list and you will be blocked out during an outage.'; +$string['allowedipsnoconfig'] = 'Your config.php does not have the extra setup to allow blocking via IP.
Please refer to our README.md file for more information.'; $string['menusettings'] = 'Settings'; $string['menumanage'] = 'Manage'; $string['messageoutagebackonline'] = 'We are back online!'; @@ -108,6 +112,10 @@ $string['outagefinishwarning'] = 'You are about to mark this outage as finished. $string['outageslistfuture'] = 'Planned outages'; $string['outageslistpast'] = 'Outage history'; $string['pluginname'] = 'Outage manager'; +$string['settingssectiondefaults'] = 'Default Outage Parameters'; +$string['settingssectiondefaultsdescription'] = 'Configure the default values used when creating new outages.'; +$string['settingssectionplugin'] = 'Plugin Configuration'; +$string['settingssectionplugindescription'] = 'General outage management plugin settings.'; $string['starttime'] = 'Start date and time'; $string['starttime_help'] = 'At which date and time the outage starts, preventing general access to the system.'; $string['tableheaderduration'] = 'Duration'; diff --git a/settings.php b/settings.php index c84d636..7ae864c 100644 --- a/settings.php +++ b/settings.php @@ -28,8 +28,12 @@ defined('MOODLE_INTERNAL') || die; if ($hassiteconfig && is_enabled_auth('outage')) { $defaults = outagelib::get_config_defaults(); - // Configure default settings page. $settings->visiblename = get_string('menusettings', 'auth_outage'); + + $settings->add(new admin_setting_heading( + 'defaults', + get_string('settingssectiondefaults', 'auth_outage'), + get_string('settingssectiondefaultsdescription', 'auth_outage'))); $settings->add(new admin_setting_configcheckbox( 'auth_outage/default_autostart', get_string('defaultoutageautostart', 'auth_outage'), @@ -64,6 +68,12 @@ if ($hassiteconfig && is_enabled_auth('outage')) { $defaults['default_description'], PARAM_RAW )); + + $settings->add(new admin_setting_heading( + 'plugin', + get_string('settingssectionplugin', 'auth_outage'), + get_string('settingssectionplugindescription', 'auth_outage'))); + $settings->add(new admin_setting_configtextarea( 'auth_outage/css', get_string('defaultlayoutcss', 'auth_outage'), @@ -71,6 +81,36 @@ if ($hassiteconfig && is_enabled_auth('outage')) { $defaults['css'], PARAM_RAW )); + + // Create 'Allowed IPs' settings. + $allowedips = outagelib::get_config()->allowedips; + $description = ''; + + if (!isset($CFG->auth_outage_check) || !$CFG->auth_outage_check) { + $description .= $OUTPUT->notification(get_string('allowedipsnoconfig', 'auth_outage'), 'notifyfailure'); + } + + if (trim($allowedips) == '') { + $message = 'allowedipsempty'; + $type = 'notifymessage'; + } else if (remoteip_in_list($allowedips)) { + $message = 'allowedipshasmyip'; + $type = 'notifysuccess'; + } else { + $message = 'allowedipshasntmyip'; + $type = 'notifyfailure'; + }; + $description .= $OUTPUT->notification(get_string($message, 'auth_outage', ['ip' => getremoteaddr()]), $type); + + $description .= '

'.get_string('ipblockersyntax', 'admin').'

'; + + $settings->add(new admin_setting_configiplist( + 'auth_outage/allowedips', + get_string('allowediplist', 'admin'), + $description, + $defaults['allowedips'] + )); + // Create category for Outage. $ADMIN->add('authsettings', new admin_category('auth_outage', get_string('pluginname', 'auth_outage'))); // Add settings page toconfigure defaults. diff --git a/tests/behat/behat_auth_outage.php b/tests/behat/behat_auth_outage.php index acd23bf..ad2c4f4 100644 --- a/tests/behat/behat_auth_outage.php +++ b/tests/behat/behat_auth_outage.php @@ -154,6 +154,21 @@ class behat_auth_outage extends behat_base { } } + /** + * @Then /^I should see an empty settings text area "([^"]*)"$/ + * @param string $name + */ + public function i_should_see_an_empty_settings_text_area($name) { + $this->assertSession()->fieldValueEquals('s_auth_outage_'.$name, ''); + } + + /** + * @When /^I go to the "Outage Settings" page$/ + */ + public function i_go_to_the_outage_settings_page() { + $this->getSession()->visit($this->locate_path('/admin/settings.php?section=authsettingoutage')); + } + /** * Counts how many times an specific action is visible. * @param string $action Action to check. diff --git a/tests/behat/ipblock.feature b/tests/behat/ipblock.feature new file mode 100644 index 0000000..77e8566 --- /dev/null +++ b/tests/behat/ipblock.feature @@ -0,0 +1,21 @@ +@dev @auth @auth_outage @javascript +Feature: IP Blocker + In order to allow admins to access the system during an outage + As an admin + I need to be able to login into Moodle + + Terminology: + - An ongoing outage does not block Moodle execution, although it can trigger maintenance mode. + - Maintenance mode completely blocks Moodle and can only be deactivated using the CLI. + + + Background: + Given the authentication plugin "outage" is enabled + + + Scenario: Default IP Whitelist Settings + Given I am an administrator + And I am on homepage + When I navigate to "Settings" node in "Site administration > Plugins > Authentication > Outage manager" + Then I should see "Allowed IP list" + And I should see an empty settings text area "allowedips" diff --git a/tests/behat/warningbar.feature b/tests/behat/warningbar.feature index 8fb5a8e..6be9c40 100644 --- a/tests/behat/warningbar.feature +++ b/tests/behat/warningbar.feature @@ -1,4 +1,4 @@ -@dev @auth @auth_outage @javascript +@auth @auth_outage @javascript Feature: Warning bar In order alert users about an outage As any user diff --git a/tests/phpunit/local/controllers/infopage_test.php b/tests/phpunit/local/controllers/infopage_test.php index d42a215..09d0b18 100644 --- a/tests/phpunit/local/controllers/infopage_test.php +++ b/tests/phpunit/local/controllers/infopage_test.php @@ -257,7 +257,7 @@ class infopagecontroller_test extends auth_outage_base_testcase { * Tests updating the static page when there is no outage. */ public function test_updatestaticpage_nooutage() { - infopage::update_static_page(); + infopage::update_static_page(null); } /** @@ -267,7 +267,7 @@ class infopagecontroller_test extends auth_outage_base_testcase { $file = infopage::get_defaulttemplatefile(); touch($file); self::assertFileExists($file); - infopage::update_static_page(); + infopage::update_static_page(null); self::assertFileNotExists($file); } @@ -276,7 +276,15 @@ class infopagecontroller_test extends auth_outage_base_testcase { */ public function test_updatestaticpage_invalidfile() { $this->set_expected_exception('coding_exception'); - infopage::update_static_page(123); + infopage::update_static_page(null, 123); + } + + /** + * Tests updating the static page with an invalid outage. + */ + public function test_updatestaticpage_invalidoutage() { + $this->set_expected_exception('coding_exception'); + infopage::update_static_page("foobar"); } /** @@ -339,6 +347,7 @@ class infopagecontroller_test extends auth_outage_base_testcase { * Checks if we can create and execute a task to update outage pages. */ public function test_tasks() { + $this->resetAfterTest(true); $task = new update_static_page(); self::assertNotEmpty($task->get_name()); $task->execute(); diff --git a/tests/phpunit/local/outagelib_test.php b/tests/phpunit/local/outagelib_test.php index 3e3998f..2b7a069 100644 --- a/tests/phpunit/local/outagelib_test.php +++ b/tests/phpunit/local/outagelib_test.php @@ -24,6 +24,7 @@ */ use auth_outage\dml\outagedb; +use auth_outage\local\controllers\infopage; use auth_outage\local\outage; use auth_outage\local\outagelib; @@ -70,7 +71,7 @@ class outagelib_test extends advanced_testcase { $this->resetAfterTest(true); set_config('maintenance_later', time() + (60 * 60 * 24 * 7)); // In 1 week. self::assertNotEmpty(get_config('moodle', 'maintenance_later')); - outagelib::outages_modified(); + outagelib::prepare_next_outage(); self::assertEmpty(get_config('moodle', 'maintenance_later')); } @@ -194,6 +195,7 @@ class outagelib_test extends advanced_testcase { 'default_duration', 'default_title', 'default_warning_duration', + 'allowedips', ]; // Set config with values. foreach ($keys as $k) { @@ -206,12 +208,33 @@ class outagelib_test extends advanced_testcase { } } + /** + * Check that config has key. + */ + public function test_config_keys() { + $this->resetAfterTest(true); + $keys = [ + 'allowedips', + 'css', + 'default_autostart', + 'default_description', + 'default_duration', + 'default_title', + 'default_warning_duration', + ]; + $defaults = outagelib::get_config_defaults(); + foreach ($keys as $k) { + self::assertArrayHasKey($k, $defaults); + } + } + /** * Check if get config works getting defaults when needed. */ public function test_get_config_invalid() { $this->resetAfterTest(true); // Set config with invalid values. + set_config('allowedips', " \n", 'auth_outage'); set_config('css', " \n", 'auth_outage'); set_config('default_autostart', " \n", 'auth_outage'); set_config('default_description', " \n", 'auth_outage'); @@ -221,7 +244,7 @@ class outagelib_test extends advanced_testcase { // Get defaults. $defaults = outagelib::get_config_defaults(); $config = outagelib::get_config(); - // Ensure it is using all defailts. + // Ensure it is using all defaults. foreach ($defaults as $k => $v) { self::assertSame($v, $config->$k); } @@ -255,5 +278,165 @@ class outagelib_test extends advanced_testcase { self::assertEmpty($CFG->additionalhtmltopofbody); } -} + public function test_createmaintenancephpcode() { + $expected = <<<'EOT' += 123) { + if (!defined('CLI_SCRIPT') || !CLI_SCRIPT) { + define('MOODLE_INTERNAL', true); + require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (!remoteip_in_list('heyyou +a.b.c.d +e.e.e.e/20')) { + header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); + header('Status: 503 Moodle under maintenance'); + header('Retry-After: 300'); + header('Content-type: text/html; charset=utf-8'); + header('X-UA-Compatible: IE=edge'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); + header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Accept-Ranges: none'); + echo ''; + if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { + require($CFG->dataroot.'/climaintenance.template.html'); + exit(0); + } + // The file above should always exist, but just in case... + die('We are currently under maintentance, please try again later.'); + } + } +} +$CFG->auth_outage_check = 1; +EOT; + $found = outagelib::create_climaintenancephp_code(123, 456, "hey'\"you\na.b.c.d\ne.e.e.e/20"); + self::assertSame($expected, $found); + } + + public function test_createmaintenancephpcode_withoutage() { + global $CFG; + $this->resetAfterTest(true); + + $expected = <<<'EOT' += 123) { + if (!defined('CLI_SCRIPT') || !CLI_SCRIPT) { + define('MOODLE_INTERNAL', true); + require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (!remoteip_in_list('127.0.0.1')) { + header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); + header('Status: 503 Moodle under maintenance'); + header('Retry-After: 300'); + header('Content-type: text/html; charset=utf-8'); + header('X-UA-Compatible: IE=edge'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); + header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Accept-Ranges: none'); + echo ''; + if (file_exists($CFG->dataroot.'/climaintenance.template.html')) { + require($CFG->dataroot.'/climaintenance.template.html'); + exit(0); + } + // The file above should always exist, but just in case... + die('We are currently under maintentance, please try again later.'); + } + } +} +$CFG->auth_outage_check = 1; +EOT; + $outage = new outage([ + 'starttime' => 123, + 'stoptime' => 456, + ]); + $file = $CFG->dataroot.'/climaintenance.php'; + set_config('allowedips', '127.0.0.1', 'auth_outage'); + + outagelib::update_climaintenance_code($outage); + self::assertFileExists($file); + $found = file_get_contents($file); + self::assertSame($found, $expected); + } + + public function test_createmaintenancephpcode_withoutips() { + global $CFG; + $this->resetAfterTest(true); + + $outage = new outage([ + 'starttime' => 123, + 'stoptime' => 456, + ]); + $file = $CFG->dataroot.'/climaintenance.php'; + set_config('allowedips', '', 'auth_outage'); + + touch($file); + outagelib::update_climaintenance_code($outage); + self::assertFileNotExists($file); + } + + public function test_createmaintenancephpcode_withoutoutage() { + global $CFG; + $file = $CFG->dataroot.'/climaintenance.php'; + + touch($file); + outagelib::update_climaintenance_code(null); + self::assertFileNotExists($file); + } + + /** + * Related to Issue #70: Creating ongoing outage does not trigger maintenance file creation. + */ + public function test_preparenextoutage_notautostart() { + global $CFG; + + $this->resetAfterTest(true); + self::setAdminUser(); + $now = time(); + $outage = new outage([ + 'autostart' => false, + 'warntime' => $now - 200, + 'starttime' => $now - 100, + 'stoptime' => $now + 200, + 'title' => 'Title', + 'description' => 'Description', + ]); + set_config('allowedips', '127.0.0.1', 'auth_outage'); + outagedb::save($outage); + + // The method outagelib::prepare_next_outage() should have been called by save(). + foreach ([infopage::get_defaulttemplatefile(), $CFG->dataroot.'/climaintenance.php'] as $file) { + self::assertFileExists($file); + unlink($file); + } + } + + /** + * Related to Issue #72: IP Block still triggers cli maintenance mode even without autostart. + */ + public function test_preparenextoutage_noautostarttrigger() { + global $CFG; + + $this->resetAfterTest(true); + self::setAdminUser(); + $now = time(); + $outage = new outage([ + 'autostart' => false, + 'warntime' => $now - 200, + 'starttime' => $now - 100, + 'stoptime' => $now + 200, + 'title' => 'Title', + 'description' => 'Description', + ]); + outagedb::save($outage); + + // The method outagelib::prepare_next_outage() should have been called by save(). + self::assertFalse(get_config('moodle', 'maintenance_later')); + // This file should not exist even if the statement above fails as Moodle does not create it immediately but test anyway. + self::assertFileNotExists($CFG->dataroot.'/climaintenance.html'); + } +} diff --git a/version.php b/version.php index 02babdd..eead3a7 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = "auth_outage"; -$plugin->version = 2016110200; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = '1.0.5'; // Human-readable release information. +$plugin->version = 2016110700; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = '1.0.6'; // Human-readable release information. $plugin->requires = 2014051200; // Requires Moodle 2.7 or later. Moodle 3.0 or later recommended. $plugin->maturity = MATURITY_STABLE; // Suitable for PRODUCTION environments!