diff --git a/.travis.yml b/.travis.yml index 6ff61a9..49eb20c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,15 +2,16 @@ language: php sudo: true +services: + - mysql + addons: firefox: "47.0.1" - postgresql: "9.4" + postgresql: "9.6" apt: packages: - openjdk-8-jre -dist: trusty - cache: directories: - $HOME/.composer/cache @@ -20,39 +21,36 @@ php: - 7.2 env: - - DB=pgsql MOODLE_BRANCH=master - - DB=mysqli MOODLE_BRANCH=master - - DB=pgsql MOODLE_BRANCH=MOODLE_38_STABLE - - DB=mysqli MOODLE_BRANCH=MOODLE_38_STABLE - - DB=pgsql MOODLE_BRANCH=MOODLE_37_STABLE - - DB=mysqli MOODLE_BRANCH=MOODLE_37_STABLE - - DB=pgsql MOODLE_BRANCH=MOODLE_36_STABLE - - DB=mysqli MOODLE_BRANCH=MOODLE_36_STABLE + - DB=pgsql MOODLE_BRANCH=MOODLE_36_STABLE NODEJS=8 + - DB=mysqli MOODLE_BRANCH=MOODLE_36_STABLE NODEJS=8 + - DB=pgsql MOODLE_BRANCH=MOODLE_37_STABLE + - DB=pgsql MOODLE_BRANCH=MOODLE_38_STABLE + - DB=pgsql MOODLE_BRANCH=MOODLE_39_STABLE + - DB=mysqli MOODLE_BRANCH=MOODLE_39_STABLE matrix: include: - - php: 7.1 - env: DB=pgsql MOODLE_BRANCH=MOODLE_35_STABLE - - php: 7.1 + - php: 5.6 + env: DB=pgsql MOODLE_BRANCH=MOODLE_33_STABLE NODEJS=8 + - php: 7.0 env: DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE - - php: 7.0 - env: DB=pgsql MOODLE_BRANCH=MOODLE_34_STABLE - - php: 7.0 - env: DB=mysqli MOODLE_BRANCH=MOODLE_34_STABLE - - php: 5.6 - env: DB=pgsql MOODLE_BRANCH=MOODLE_33_STABLE - - php: 5.6 - env: DB=mysqli MOODLE_BRANCH=MOODLE_33_STABLE + - php: 7.1 + env: DB=mysqli MOODLE_BRANCH=MOODLE_33_STABLE NODEJS=8 + - php: 7.4 + env: DB=mysqli MOODLE_BRANCH=master + - php: 7.4 + env: DB=pgsql MOODLE_BRANCH=master before_install: - export MOODLE_VERSION=$(echo "$MOODLE_BRANCH" | cut -d'_' -f 2) - - if [ ${TRAVIS_PHP_VERSION:0:3} == "7.2" ] && [ $DB == "pgsql" ] && [[ "$MOODLE_VERSION" -gt 38 || "$MOODLE_VERSION" == "master" ]] ; then - sudo /etc/init.d/postgresql stop; - sudo /etc/init.d/postgresql start 9.5; - fi - phpenv config-rm xdebug.ini - - nvm install 8.9 - - nvm use 8.9 + - if [ "$NODEJS" = 8 ]; then + nvm install 8.9; + nvm use 8.9; + else + nvm install 14.0.0; + nvm use 14.0.0; + fi - cd ../.. - composer selfupdate - composer create-project -n --no-dev --prefer-dist blackboard-open-source/moodle-plugin-ci ci ^2 @@ -74,4 +72,4 @@ script: # Behat tests are failing due to issue: # https://github.com/blackboard-open-source/moodle-plugin-ci/issues/70 # Commenting it out until the issue is fixed. -# - moodle-plugin-ci behat \ No newline at end of file +# - moodle-plugin-ci behat diff --git a/README.md b/README.md index 590781e..a50c4d0 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,6 @@ * [Why is it an auth plugin?](#why-it-is-an-auth-plugin) * [Feedback and issues](#feedback-and-issues) -Version Support -------------- -The master branch supports Moodle 3.3 and higher. -The MOODLE_32_STABLE branch supports Moodle 2.7 -> Moodle 3.2 - What is this? ------------- @@ -45,6 +40,14 @@ If you have an older version of Moodle you can still make it work but you will need to manually add one extra plugin, please check: * https://github.com/catalyst/moodle-local_outage +Branches +-------- +| Moodle version | Branch | PHP | +| ----------------- | ----------- | ---- | +| Moodle 2.7 to 3.2 | MOODLE_32_STABLE | 5.5+ | +| Totara up to 10 | TOTARA_10 | 5.5+ | +| Moodle 3.3+ | master | 7.0+ | +| Totara 11+ | master | 7.0+ | Screenshots ----------- diff --git a/bootstrap.php b/bootstrap.php index 84f5b6b..b06e11a 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -69,7 +69,11 @@ if (is_callable('auth_outage_bootstrap_callback')) { // 3) Check for allowed scripts or IPs during outages. if (!empty($_SERVER['REQUEST_URI'])) { $rooturl = parse_url($CFG->wwwroot); - $url = $rooturl['path'].'/auth/outage/info.php'; + $path = ''; + if (array_key_exists('path', $rooturl) && !empty($rooturl['path'])) { + $path = $rooturl['url']; + } + $url = $path.'/auth/outage/info.php'; $outageinfo = strpos($_SERVER['REQUEST_URI'], $url) === 0 ? true : false; } $allowed = !file_exists($CFG->dataroot.'/climaintenance.php') // Not in maintenance mode. diff --git a/classes/form/outage/edit.php b/classes/form/outage/edit.php index b37df11..d27d77d 100644 --- a/classes/form/outage/edit.php +++ b/classes/form/outage/edit.php @@ -164,7 +164,8 @@ class edit extends moodleform { if (!empty($outage->id) && $outage->autostart && $outage->starttime < time() && $outage->stoptime > time()) { $warning = $mform->getElement('warningreenablemaintenancemode'); - $warning->setValue($OUTPUT->notification(get_string('warningreenablemaintenancemode', 'auth_outage'), 'notifywarning')); + $warning->setValue($OUTPUT->notification(get_string('warningreenablemaintenancemode', 'auth_outage'), + 'notifywarning')); } } else { throw new coding_exception('$outage must be an outage object.', $outage); diff --git a/classes/local/controllers/infopage.php b/classes/local/controllers/infopage.php index 4c691d4..a1be30e 100644 --- a/classes/local/controllers/infopage.php +++ b/classes/local/controllers/infopage.php @@ -53,6 +53,11 @@ class infopage { * @param array $params Parameters to use or null to get from Moodle API (request). */ public function __construct(array $params = null) { + global $CFG; + // Enable SVG support here to make sure all SVG files + // used in the current theme are served properly. + $CFG->svgicons = true; + if (is_null($params)) { $params = [ 'id' => optional_param('id', null, PARAM_INT), diff --git a/classes/local/controllers/maintenance_static_page_generator.php b/classes/local/controllers/maintenance_static_page_generator.php index d58e87b..986750a 100644 --- a/classes/local/controllers/maintenance_static_page_generator.php +++ b/classes/local/controllers/maintenance_static_page_generator.php @@ -128,7 +128,7 @@ class maintenance_static_page_generator { continue; } $saved = $this->io->save_url_file($href); - if (is_null($saved['url'])) { + if (empty($saved['url'])) { $url = $href; // Skipped, use original URL. } else { $this->update_link_stylesheet_parse($saved['file'], dirname($href)); diff --git a/classes/local/controllers/maintenance_static_page_io.php b/classes/local/controllers/maintenance_static_page_io.php index a5dbb40..eeafb53 100644 --- a/classes/local/controllers/maintenance_static_page_io.php +++ b/classes/local/controllers/maintenance_static_page_io.php @@ -213,7 +213,7 @@ class maintenance_static_page_io { /** * Saves the content of the URL into a file, returning the local filename. * @param string $url Input URL. - * @return string|null Output filename or null if skipped. + * @return array|null Output an array with the filename and url or null if skipped. */ public function save_url_file($url) { global $CFG; diff --git a/classes/local/outagelib.php b/classes/local/outagelib.php index 8a8b496..b450bd1 100644 --- a/classes/local/outagelib.php +++ b/classes/local/outagelib.php @@ -29,6 +29,7 @@ use auth_outage\dml\outagedb; use auth_outage\local\controllers\maintenance_static_page; use auth_outage\output\renderer; use coding_exception; +use curl; use Exception; use file_exception; use invalid_parameter_exception; @@ -58,16 +59,14 @@ class outagelib { private static $injectcalled = false; public static function fetch_page($file) { - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $file); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); // It is localhost, time to connect is enough. - curl_setopt($curl, CURLOPT_TIMEOUT, 60); - $contents = curl_exec($curl); - $mime = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); - curl_close($curl); + $curl = new curl(); + $contents = $curl->get($file); + $info = $curl->get_info(); + if (!empty($info['content_type'])) { + $mime = $info['content_type']; + } else { + $mime = ''; + } return compact('contents', 'mime'); } @@ -79,6 +78,26 @@ class outagelib { self::inject(); } + /** + * Given a time, usually now, when is the next outage window? + */ + public static function get_next_window($time = null) { + + $config = self::get_config(); + + if (!$time) { + $time = time(); + } + + $default = $config->default_time; + if ($default) { + // First try natural language parsing. + $time = strtotime($default, $time); + } + return $time; + } + + /** * Will check for ongoing or warning outages and will attach the message bar as required. */ @@ -152,6 +171,7 @@ class outagelib { return [ 'allowedips' => '', 'css' => '', + 'default_time' => '', 'default_autostart' => '0', 'default_duration' => (string)(60 * 60), 'default_warning_duration' => (string)(60 * 60), @@ -269,6 +289,9 @@ class outagelib { if ((time() >= {{STARTTIME}}) && (time() < {{STOPTIME}})) { define('MOODLE_INTERNAL', true); require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) { + require_once($CFG->dirroot.'/lib/classes/ip_utils.php'); + } if (!remoteip_in_list('{{ALLOWEDIPS}}')) { header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); header('Status: 503 Moodle under maintenance'); diff --git a/edit.php b/edit.php index 5cd25f8..e2b0e18 100644 --- a/edit.php +++ b/edit.php @@ -47,6 +47,7 @@ if ($mform->is_cancelled()) { $clone = optional_param('clone', 0, PARAM_INT); $edit = optional_param('edit', 0, PARAM_INT); +$time = optional_param('starttime', 0, PARAM_INT); if ($clone && $edit) { throw new invalid_parameter_exception('Cannot provide both clone and edit ids.'); } @@ -60,7 +61,10 @@ if ($clone) { $action = 'outageedit'; } else { $config = outagelib::get_config(); - $time = time(); + if (empty($time)) { + $time = outagelib::get_next_window(); + } + $outage = new outage([ 'autostart' => $config->default_autostart, 'starttime' => $time, diff --git a/lang/en/auth_outage.php b/lang/en/auth_outage.php index c1dbcf4..badfb5f 100644 --- a/lang/en/auth_outage.php +++ b/lang/en/auth_outage.php @@ -80,6 +80,8 @@ $string['defaultoutageduration'] = 'Outage duration'; $string['defaultoutagedurationdescription'] = 'Default duration (in minutes) of an outage.'; $string['defaultwarningduration'] = 'Warning duration'; $string['defaultwarningdurationdescription'] = 'Default warning time (in minutes) for outages.'; +$string['defaulttime'] = 'Default time'; +$string['defaulttimedescription'] = 'The default time for the next outage, expressed in natural language eg "next Thursday 7pm". See PHP relative dates'; $string['defaulttitle'] = 'Title'; $string['defaulttitledescription'] = 'Default title for outages. Use {{start}} and {{stop}} placeholders as required.'; $string['defaulttitlevalue'] = 'System down from {{start}} for {{duration}}'; @@ -167,6 +169,7 @@ $string['outagefinish'] = 'Finish outage'; $string['outagefinishwarning'] = 'You are about to mark this outage as finished. The system will be immediately back online.'; $string['outageslistfuture'] = 'Planned outages'; $string['outageslistpast'] = 'Outage history'; +$string['outage:updatenotify'] = ''; $string['pluginname'] = 'Outage manager'; $string['removeselectors'] = 'Remove selectors'; $string['removeselectorsdescription'] = 'CSS selectors to remove when rendering a static themed maintenance page. One selector per line.'; diff --git a/settings.php b/settings.php index 9c3be2c..c86670e 100644 --- a/settings.php +++ b/settings.php @@ -59,6 +59,13 @@ if ($hassiteconfig && is_enabled_auth('outage')) { $defaults['default_duration'], 60 )); + $settings->add(new admin_setting_configtext( + 'auth_outage/default_time', + get_string('defaulttime', 'auth_outage'), + get_string('defaulttimedescription', 'auth_outage'), + '', + PARAM_TEXT + )); $settings->add(new admin_setting_configtext( 'auth_outage/default_title', get_string('defaulttitle', 'auth_outage'), diff --git a/tests/phpunit/base_testcase.php b/tests/phpunit/base_testcase.php index 0e71ed1..307a0c4 100644 --- a/tests/phpunit/base_testcase.php +++ b/tests/phpunit/base_testcase.php @@ -31,6 +31,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use auth_outage\dml\outagedb; + defined('MOODLE_INTERNAL') || die(); /** @@ -70,4 +72,12 @@ abstract class auth_outage_base_testcase extends advanced_testcase { parent::setUp(); $this->resetAfterTest(true); } + + public function tearDown() { + global $DB; + + foreach (outagedb::get_all() as $i => $outage) { + $DB->delete_records('auth_outage', ['id' => $outage->id]); + } + } } diff --git a/tests/phpunit/local/controllers/infopage_test.php b/tests/phpunit/local/controllers/infopage_test.php index 1041175..9c3bc5a 100644 --- a/tests/phpunit/local/controllers/infopage_test.php +++ b/tests/phpunit/local/controllers/infopage_test.php @@ -106,4 +106,14 @@ class infopagecontroller_test extends auth_outage_base_testcase { $output = $info->get_output(); self::assertContains('auth_outage_info', $output); } + + /** + * Tests the constructor enables SVG support. + */ + public function test_svgicons_is_true() { + global $CFG; + $CFG->svgicons = false; + new infopage(); + self::assertTrue($CFG->svgicons); + } } diff --git a/tests/phpunit/local/outagelib_test.php b/tests/phpunit/local/outagelib_test.php index 40ec445..6fd2875 100644 --- a/tests/phpunit/local/outagelib_test.php +++ b/tests/phpunit/local/outagelib_test.php @@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->libdir.'/adminlib.php'); +require_once(__DIR__.'/../base_testcase.php'); /** * outagelib_test test class. @@ -41,7 +42,7 @@ require_once($CFG->libdir.'/adminlib.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @SuppressWarnings(public) Allow as many methods as needed. */ -class outagelib_test extends advanced_testcase { +class outagelib_test extends auth_outage_base_testcase { /** * Check if maintenance message is disabled as needed. */ @@ -289,6 +290,9 @@ class outagelib_test extends advanced_testcase { if ((time() >= 123) && (time() < 456)) { define('MOODLE_INTERNAL', true); require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) { + require_once($CFG->dirroot.'/lib/classes/ip_utils.php'); + } if (!remoteip_in_list('hey\'\"you a.b.c.d e.e.e.e/20')) { @@ -330,6 +334,9 @@ EOT; if ((time() >= 123) && (time() < 456)) { define('MOODLE_INTERNAL', true); require_once($CFG->dirroot.'/lib/moodlelib.php'); + if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) { + require_once($CFG->dirroot.'/lib/classes/ip_utils.php'); + } if (!remoteip_in_list('127.0.0.1')) { header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance'); header('Status: 503 Moodle under maintenance'); diff --git a/version.php b/version.php index 2a572fd..cd60ab6 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = "auth_outage"; -$plugin->version = 2020021500; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 2020021500; // Human-readable release information. +$plugin->version = 2020032500; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 2020032500; // Human-readable release information. $plugin->requires = 2017051500; // Requires 3.3 and higher. $plugin->maturity = MATURITY_STABLE; // Suitable for PRODUCTION environments! diff --git a/views/manage.php b/views/manage.php index 52c856c..f2fee40 100644 --- a/views/manage.php +++ b/views/manage.php @@ -27,6 +27,7 @@ use auth_outage\output\manage\history_table; use auth_outage\output\manage\planned_table; use auth_outage\output\renderer; use auth_outage\dml\outagedb; +use auth_outage\local\outagelib; defined('MOODLE_INTERNAL') || die(); @@ -49,10 +50,23 @@ echo $viewbag['warning']; $table->finish_output(); ?> - - - single_button($urlnew, get_string('outagecreate', 'auth_outage')); ?> - + default_time; + $max = $default ? 3 : 1; + $next = time(); + for ($c = 0; $c < $max; $c++) { + echo '
';
+ $next = outagelib::get_next_window($next);
+ $urlnew->param('starttime', $next);
+ echo $OUTPUT->single_button($urlnew, get_string('outagecreate', 'auth_outage'));
+ if ($default) {
+ echo ' ' . userdate( $next, get_string('datetimeformat', 'auth_outage'));
+ }
+ }
+ endif; ?>