diff --git a/classes/form/outage/edit.php b/classes/form/outage/edit.php index 3393396..1b13fd8 100644 --- a/classes/form/outage/edit.php +++ b/classes/form/outage/edit.php @@ -90,6 +90,10 @@ class edit extends moodleform { $mform->disabledIf('accesskey', 'useaccesskey'); $mform->addHelpButton('accesskey', 'accesskey', 'auth_outage'); + $mform->addElement('text', 'metadata', get_string('metadata', 'auth_outage')); + $mform->setType('metadata', PARAM_TEXT); + $mform->addHelpButton('metadata', 'metadata', 'auth_outage'); + $this->add_action_buttons(); } @@ -144,6 +148,7 @@ class edit extends moodleform { 'title' => $data->title, 'description' => $data->description['text'], 'accesskey' => $data->useaccesskey ? $data->accesskey : null, + 'metadata' => $data->metadata ?? null, ]; return new outage($outagedata); } @@ -169,6 +174,7 @@ class edit extends moodleform { 'description' => ['text' => $outage->description, 'format' => '1'], 'accesskey' => $outage->accesskey, 'useaccesskey' => !empty($outage->accesskey), + 'metadata' => $outage->metadata, ]); // If the default_autostart is configured in config, then force autostart to be the default value. diff --git a/classes/local/controllers/infopage.php b/classes/local/controllers/infopage.php index a812b7c..acfe126 100644 --- a/classes/local/controllers/infopage.php +++ b/classes/local/controllers/infopage.php @@ -109,6 +109,13 @@ class infopage { // No hooks injecting into this page, do it manually. echo outagelib::get_inject_code(); + // Inject metadata into the header before output. + if (!empty($this->outage->metadata)) { + header('X-Outage-Metadata: ' . $this->outage->metadata); + header('X-Outage-StartTime: ' . $this->outage->starttime); + header('X-Outage-EndTime: ' . $this->outage->stoptime); + } + echo $OUTPUT->header(); $viewbag = [ 'admin' => is_siteadmin(), diff --git a/classes/local/controllers/maintenance_static_page.php b/classes/local/controllers/maintenance_static_page.php index 485d640..4a6896d 100644 --- a/classes/local/controllers/maintenance_static_page.php +++ b/classes/local/controllers/maintenance_static_page.php @@ -49,6 +49,12 @@ class maintenance_static_page { } else if (PHPUNIT_TEST || defined('BEHAT_SITE_RUNNING')) { $html = ''; } else { + // Inject metadata into the header before output. + if (!empty($outage->metadata)) { + header('X-Outage-Metadata: ' . $outage->metadata); + header('X-Outage-StartTime: ' . $outage->starttime); + header('X-Outage-EndTime: ' . $outage->stoptime); + } $data = maintenance_static_page_io::file_get_data( $CFG->wwwroot . '/auth/outage/info.php?auth_outage_hide_warning=1&static=1&id=' . $outage->id ); diff --git a/classes/local/outage.php b/classes/local/outage.php index 6a55aaf..b64a309 100644 --- a/classes/local/outage.php +++ b/classes/local/outage.php @@ -113,6 +113,11 @@ class outage { */ public $accesskey = null; + /** + * @var string|null metadata string, or null if not enabled. + */ + public $metadata = null; + /** * outage constructor. * @param stdClass|array|null $data The data for the outage. diff --git a/classes/local/outagelib.php b/classes/local/outagelib.php index 0316ef5..93722c2 100644 --- a/classes/local/outagelib.php +++ b/classes/local/outagelib.php @@ -283,7 +283,7 @@ class outagelib { * @return string * @throws invalid_parameter_exception */ - public static function create_climaintenancephp_code($starttime, $stoptime, $allowedips, $accesskey = null) { + public static function create_climaintenancephp_code($starttime, $stoptime, $allowedips, $accesskey = null, $metadata = null) { global $CFG; if (!is_int($starttime) || !is_int($stoptime)) { throw new invalid_parameter_exception('Make sure $startime and $stoptime are integers.'); @@ -337,6 +337,11 @@ if ((time() >= {{STARTTIME}}) && (time() < {{STOPTIME}})) { header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Accept-Ranges: none'); header('X-Moodle-Maintenance: manager'); + if (!empty({{METADATA}})) { + header('X-Outage-Metadata: ' . {{METADATA}}); + } + header('X-Outage-StartTime: ' . '{{STARTTIME}}'); + header('X-Outage-EndTime: ' . '{{STOPTIME}}'); } if (!$isphpunit && ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER))) { @@ -363,10 +368,10 @@ if ((time() >= {{STARTTIME}}) && (time() < {{STOPTIME}})) { } EOT; $search = ['{{STARTTIME}}', '{{STOPTIME}}', '{{USEALLOWEDIPS}}', '{{ALLOWEDIPS}}', '{{USEACCESSKEY}}', '{{ACCESSKEY}}', - '{{YOURIP}}', '{{COOKIESECURE}}', '{{COOKIEHTTPONLY}}']; + '{{YOURIP}}', '{{COOKIESECURE}}', '{{COOKIEHTTPONLY}}', '{{METADATA}}']; // Note that var_export is required because (string) false == '', not 'false'. $replace = [$starttime, $stoptime, var_export(!empty($allowedips), true), $allowedips, var_export(!empty($accesskey), true), - $accesskey, getremoteaddr('n/a'), var_export($cookiesecure, true), var_export($cookiehttponly, true)]; + $accesskey, getremoteaddr('n/a'), var_export($cookiesecure, true), var_export($cookiehttponly, true), var_export($metadata, true)]; return str_replace($search, $replace, $code); } @@ -389,6 +394,7 @@ EOT; $config = self::get_config(); $allowedips = trim($config->allowedips); $accesskey = $outage->accesskey ?? null; + $metadata = $outage->metadata ?? null; // If no outage, or allowed ips is null and access key is null (i.e. no blocking required). if (is_null($outage) || ($allowedips == '' && empty($accesskey))) { @@ -396,7 +402,7 @@ EOT; unlink($file); } } else { - $code = self::create_climaintenancephp_code($outage->starttime, $outage->stoptime, $allowedips, $accesskey); + $code = self::create_climaintenancephp_code($outage->starttime, $outage->stoptime, $allowedips, $accesskey, $metadata); $dir = dirname($file); if (!file_exists($dir) || !is_dir($dir)) { diff --git a/db/install.xml b/db/install.xml index 560e44f..8699ef9 100644 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ - @@ -18,6 +18,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index 8673f61..8d27d7a 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -61,5 +61,20 @@ function xmldb_auth_outage_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2024081900, 'auth', 'outage'); } + if ($oldversion < 2026011301) { + + // Define field metadata to be added to auth_outage. + $table = new xmldb_table('auth_outage'); + $field = new xmldb_field('metadata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'accesskey'); + + // Conditionally launch add field metadata. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Outage savepoint reached. + upgrade_plugin_savepoint(true, 2026011301, 'auth', 'outage'); + } + return true; } diff --git a/edit.php b/edit.php index 4976353..d3f34a3 100644 --- a/edit.php +++ b/edit.php @@ -27,7 +27,6 @@ use auth_outage\dml\outagedb; use auth_outage\form\outage\edit; use auth_outage\local\outage; use auth_outage\local\outagelib; -use auth_outage\output\renderer; require_once(__DIR__ . '/../../config.php'); require_once($CFG->libdir . '/adminlib.php'); @@ -90,6 +89,7 @@ if ($clone) { 'warntime' => $time - $config->default_warning_duration, 'title' => $config->default_title, 'description' => $config->default_description, + 'metadata' => $config->default_metadata, ]); $action = 'outagecreate'; } diff --git a/lang/en/auth_outage.php b/lang/en/auth_outage.php index b0f3d89..5d72a9f 100644 --- a/lang/en/auth_outage.php +++ b/lang/en/auth_outage.php @@ -88,6 +88,8 @@ $string['defaultdescriptiondescription'] = 'Default warning message for outages. $string['defaultdescriptionvalue'] = 'There is maintenance scheduled from {{start}} to {{stop}} and our system will not be available during that time.'; $string['defaultlayoutcss'] = 'Layout CSS'; $string['defaultlayoutcssdescription'] = 'This CSS code can be used to override the Outage Warning Bar CSS.'; +$string['defaultmetadata'] = 'Default metadata'; +$string['defaultmetadatadescription'] = 'The default metadata to include in the outage settings, to be used as a template for new outages. E.g. JIRA-{ticketno}.'; $string['defaultoutageautostart'] = 'Outage auto start'; $string['defaultoutageautostartdescription'] = 'If the outage should automatically trigger maintenance mode once it starts, locking down the whole site.'; $string['defaultoutageduration'] = 'Outage duration'; @@ -120,6 +122,8 @@ $string['messageoutagebackonline'] = 'We are back online!'; $string['messageoutagebackonlinedescription'] = 'You may resume browsing safely.'; $string['messageoutageongoing'] = 'Back online at {$a->stop}.'; $string['messageoutagewarning'] = 'Shutting down in {{countdown}}'; +$string['metadata'] = 'Outage metadata'; +$string['metadata_help'] = 'Data here will be output in the outage page as a meta tag in the header of the outage page.'; $string['na'] = 'n/a'; $string['notfound'] = 'No outages found.'; $string['outage:updatenotify'] = ''; diff --git a/settings.php b/settings.php index eeb10f0..dfd5221 100644 --- a/settings.php +++ b/settings.php @@ -83,6 +83,16 @@ if ($hassiteconfig) { $defaults['default_title'], PARAM_TEXT )); + $settings->add( + new admin_setting_configtext( + 'auth_outage/default_metadata', + get_string('defaultmetadata', 'auth_outage'), + get_string('defaultmetadatadescription', 'auth_outage'), + '', + PARAM_TEXT + ) + ); + $settings->add(new admin_setting_configtextarea( 'auth_outage/default_description', get_string('defaultdescription', 'auth_outage'), diff --git a/tests/local/outagelib_test.php b/tests/local/outagelib_test.php index 092725a..12595ef 100644 --- a/tests/local/outagelib_test.php +++ b/tests/local/outagelib_test.php @@ -352,6 +352,11 @@ e.e.e.e/20'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Accept-Ranges: none'); header('X-Moodle-Maintenance: manager'); + if (!empty('testmeta')) { + header('X-Outage-Metadata: ' . 'testmeta'); + } + header('X-Outage-StartTime: ' . '123'); + header('X-Outage-EndTime: ' . '456'); } if (!$isphpunit && ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER))) { @@ -377,7 +382,7 @@ e.e.e.e/20'); } } EOT; - $found = outagelib::create_climaintenancephp_code(123, 456, "hey'\"you\na.b.c.d\ne.e.e.e/20", '12345'); + $found = outagelib::create_climaintenancephp_code(123, 456, "hey'\"you\na.b.c.d\ne.e.e.e/20", '12345', 'testmeta'); self::assertSame($expected, $found); } @@ -431,6 +436,11 @@ if ((time() >= 123) && (time() < 456)) { header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Accept-Ranges: none'); header('X-Moodle-Maintenance: manager'); + if (!empty(NULL)) { + header('X-Outage-Metadata: ' . NULL); + } + header('X-Outage-StartTime: ' . '123'); + header('X-Outage-EndTime: ' . '456'); } if (!$isphpunit && ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER))) { diff --git a/version.php b/version.php index 1eb9caa..3f613a3 100644 --- a/version.php +++ b/version.php @@ -28,8 +28,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = "auth_outage"; -$plugin->version = 2026011300; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 2026011300; // Human-readable release information. +$plugin->version = 2026011301; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 2026011301; // Human-readable release information. $plugin->requires = 2025100600; // Moodle 5.1. $plugin->maturity = MATURITY_STABLE; // Suitable for PRODUCTION environments! $plugin->supported = [501, 501]; // A range of branch numbers of supported moodle versions.