From fcb2a681e74f62fac9556d454b4cdca56a736826 Mon Sep 17 00:00:00 2001 From: Peter Burnett Date: Wed, 31 Dec 2025 10:31:30 +1000 Subject: [PATCH] Issue 387: Add metadata header to outage pages --- classes/form/outage/edit.php | 6 ++++++ classes/local/controllers/infopage.php | 7 +++++++ .../local/controllers/maintenance_static_page.php | 6 ++++++ classes/local/outage.php | 5 +++++ classes/local/outagelib.php | 14 ++++++++++---- db/install.xml | 3 ++- db/upgrade.php | 15 +++++++++++++++ edit.php | 2 +- lang/en/auth_outage.php | 4 ++++ settings.php | 10 ++++++++++ tests/local/outagelib_test.php | 12 +++++++++++- version.php | 4 ++-- 12 files changed, 79 insertions(+), 9 deletions(-) diff --git a/classes/form/outage/edit.php b/classes/form/outage/edit.php index d830cf3..3ac2b75 100644 --- a/classes/form/outage/edit.php +++ b/classes/form/outage/edit.php @@ -83,6 +83,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(); } @@ -137,6 +141,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); } @@ -162,6 +167,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 fcdf051..ed0ab1c 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 b2a6858..d9f216d 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); $html = $data['contents']; 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 30c40aa..dc7dffa 100644 --- a/classes/local/outagelib.php +++ b/classes/local/outagelib.php @@ -284,7 +284,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.'); @@ -338,6 +338,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))) { @@ -364,10 +369,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); } @@ -390,6 +395,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))) { @@ -397,7 +403,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 4e46ec9..77c5bbf 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -62,5 +62,20 @@ function xmldb_auth_outage_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2024081900, 'auth', 'outage'); } + if ($oldversion < 2024081901) { + + // 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, 2024081901, 'auth', 'outage'); + } + return true; } diff --git a/edit.php b/edit.php index f4812e8..a618d92 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 8eb74f5..8f6379e 100644 --- a/lang/en/auth_outage.php +++ b/lang/en/auth_outage.php @@ -76,6 +76,8 @@ $string['configurationinaccessiblewwwroot'] = 'Could not access {$a->wwwroot} $string['datetimeformat'] = '%a %d %h %Y at %I:%M%P %Z'; $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'; @@ -115,6 +117,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:viewinfo'] = 'View outage info'; diff --git a/settings.php b/settings.php index 068986c..39cc71d 100644 --- a/settings.php +++ b/settings.php @@ -78,6 +78,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 903d607..3435401 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 14df994..cfa64c7 100644 --- a/version.php +++ b/version.php @@ -28,8 +28,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = "auth_outage"; -$plugin->version = 2024081900; // The current plugin version (Date: YYYYMMDDXX). -$plugin->release = 2024081900; // Human-readable release information. +$plugin->version = 2024081901; // The current plugin version (Date: YYYYMMDDXX). +$plugin->release = 2024081901; // Human-readable release information. $plugin->requires = 2017111309; // 2017111309 = T13, but this really requires 3.9 and higher. $plugin->maturity = MATURITY_STABLE; // Suitable for PRODUCTION environments! $plugin->supported = [39, 405]; // A range of branch numbers of supported moodle versions.