From 40e0851db594108b60bf4794faae307e55c6f71b Mon Sep 17 00:00:00 2001 From: Benjamin Walker Date: Fri, 8 Mar 2024 17:15:32 +1000 Subject: [PATCH] Add ability to override check status #169 Co-authored-by: Owen Herbert --- classes/checker.php | 35 +++++- classes/form/override_form.php | 76 ++++++++++++ classes/object/override.php | 207 +++++++++++++++++++++++++++++++++ classes/privacy/provider.php | 148 +++++++++++++++++++++-- classes/table/status_table.php | 135 +++++++++++++++++++++ db/install.xml | 26 +++++ db/upgrade.php | 35 ++++++ lang/en/tool_heartbeat.php | 18 ++- override.php | 111 ++++++++++++++++++ settings.php | 18 ++- status.php | 40 +++++++ version.php | 4 +- 12 files changed, 832 insertions(+), 21 deletions(-) create mode 100644 classes/form/override_form.php create mode 100644 classes/object/override.php create mode 100644 classes/table/status_table.php create mode 100644 db/install.xml create mode 100644 override.php create mode 100644 status.php diff --git a/classes/checker.php b/classes/checker.php index 6bc2f72..f1637a6 100644 --- a/classes/checker.php +++ b/classes/checker.php @@ -57,9 +57,7 @@ public static function get_check_messages(): array { } // Remove any supressed checks from the list. - $checks = array_filter($checks, function($check) { - return !in_array(get_class($check), self::supressed_checks()); - }); + $checks = self::remove_supressed_checks($checks); // Execute each check and store their messages. $messages = []; @@ -132,7 +130,7 @@ private static function exception_to_message(string $prefix, Throwable $e): resu private static function process_check_and_get_result(check $check): resultmessage { $res = new resultmessage(); - $checkresult = $check->get_result(); + $checkresult = self::get_overridden_result($check); // Map check result to nagios level. $map = [ @@ -297,5 +295,32 @@ private static function supressed_checks(): array { \tool_task\check\adhocqueue::class, ]; } -} + /** + * Removes supressed checks from an array + * @param array $checks + * @return array of checks without supressed checks + */ + public static function remove_supressed_checks(array $checks): array { + // Remove any supressed checks from the list. + return array_filter($checks, function($check) { + return !in_array(get_class($check), self::supressed_checks()); + }); + } + + /** + * Gets a check result while applying specified overrides. + * @param check $check + * @return result with overrides + */ + public static function get_overridden_result(check $check): result { + $ref = $check->get_ref(); + $result = $check->get_result(); + + $override = \tool_heartbeat\object\override::get_active_override($ref); + if (isset($override)) { + return new result($override->get('override'), $result->get_summary(), $result->get_details()); + } + return $result; + } +} diff --git a/classes/form/override_form.php b/classes/form/override_form.php new file mode 100644 index 0000000..e9dbf1e --- /dev/null +++ b/classes/form/override_form.php @@ -0,0 +1,76 @@ +. +/** + * Override form + * + * @package tool_heartbeat + * @copyright 2023 Owen Herbert + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + */ + +namespace tool_heartbeat\form; + +use core\check\result; +use core\form\persistent; + +/** + * Override form + * + * @package tool_heartbeat + * @copyright 2023 Owen Herbert + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class override_form extends persistent { + + /** @var string Persistent class name. */ + protected static $persistentclass = 'tool_heartbeat\\object\\override'; + + /** + * Define the form. + */ + public function definition() { + $mform = $this->_form; + + // Ref. + $mform->addElement('static', 'ref', get_string('check')); + $mform->setConstant('ref', $this->_customdata['ref']); + + // Override. + $mform->addElement('select', 'override', get_string('override', 'tool_heartbeat'), [ + result::NA => get_string('statusna'), + result::OK => get_string('statusok'), + result::INFO => get_string('statusinfo'), + result::UNKNOWN => get_string('statusunknown'), + result::WARNING => get_string('statuswarning'), + result::CRITICAL => get_string('statuscritical'), + result::ERROR => get_string('statuserror'), + ]); + + // Note. + $mform->addElement('textarea', 'note', get_string('notes', 'core_notes'), ['rows' => 3]); + $mform->addRule('note', get_string('noterequired', 'tool_heartbeat'), 'required', null, 'client'); + + // URL. + $mform->addElement('text', 'url', get_string('url'), ['size' => 80]); + $mform->setType('url', PARAM_URL); + + // Override until. + $mform->addElement('date_selector', 'expires_at', get_string('expiresat', 'tool_heartbeat')); + + $this->add_action_buttons(); + } +} diff --git a/classes/object/override.php b/classes/object/override.php new file mode 100644 index 0000000..10ce115 --- /dev/null +++ b/classes/object/override.php @@ -0,0 +1,207 @@ +. +/** + * + * Heartbeat override + * + * @package tool_heartbeat + * @copyright 2023 Owen Herbert + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + */ + +namespace tool_heartbeat\object; + +use core\persistent; + +/** + * Represents a heartbeat override. + */ +class override extends Persistent { + + /** + * Table name for the persistent. + */ + const TABLE = 'tool_heartbeat_overrides'; + + /** + * Create an instance of this class with the default expires at. + * + * @param int $id If set, this is the id of an existing record, used to load the data. + * @param \stdClass $record If set will be passed to {@link self::from_record()}. + */ + + public function __construct(int $id = 0, \stdClass $record = null) { + $this->set_default_expiry(); + parent::__construct($id, $record); + } + + /** + * Return the definition of the properties of this model. + * + * @return array + */ + protected static function define_properties(): array { + return [ + 'ref' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_TEXT, + ], + 'override' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_TEXT, + ], + 'userid' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_INT, + 'default' => 0, + ], + 'note' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_TEXT, + ], + 'url' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_TEXT, + ], + 'expires_at' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_INT, + ], + 'resolved_at' => [ + 'null' => NULL_NOT_ALLOWED, + 'type' => PARAM_INT, + 'default' => 0, + ], + ]; + } + + /** + * Sets the expiry time to the default value + * + * @return void + */ + private function set_default_expiry() { + $expiredate = date('Y-m-d', time() + (int) get_config('tool_heartbeat', 'mutedefault')); + $this->set('expires_at', strtotime($expiredate)); + } + + /** + * Resolves and unmutes a check. + * + * @param int $time optional param to set a sepcific resolved at time + * @return void + */ + public function resolve($time = 0) { + $time = $time ?: time(); + $this->set('resolved_at', $time); + $this->save(); + } + + /** + * Gets an active override based on a ref. + * + * @param string $ref ref + * @return override|null + */ + public static function get_active_override($ref): ?override { + // This call will usually be called for every ref, so load all active overrides. + $overrides = self::get_active_overrides(); + return $overrides[$ref] ?? null; + } + + /** + * Returns a list of active overrides with ref as the key. + * + * @return array array of overrides, with check ref as the key + */ + protected static function get_active_overrides(): array { + static $overrides; + + if (isset($overrides)) { + return $overrides; + } + + // Keep resolved status up to date. + self::mark_expired_as_resolved(); + + // Get overrides that and unresolved and expire in the future. + $overrides = []; + $conditions = "expires_at >= ? AND resolved_at = 0"; + $results = self::get_records_select($conditions, [time()]); + + // Update key to be the ref, which is unique when unresolved. + foreach ($results as $result) { + $ref = $result->get('ref'); + $overrides[$ref] = $result; + } + + return $overrides; + } + + /** + * Returns an instance of a previous override with reset values. + * + * @param string $ref + * @return override|null + */ + public static function get_restored_override(string $ref): ?override { + // Make sure there's no active override. + $active = self::get_active_override($ref); + if (!empty($active)) { + return null; + } + + // Get previous override that has expired or been resolved within the last year. + $conditions = "ref = ? AND (expires_at < ? OR resolved_at != 0) AND expires_at > ?"; + $params = [$ref, time(), strtotime('-1 year')]; + $previous = self::get_records_select($conditions, $params, 'expires_at DESC, resolved_at DESC', '*', 0, 1); + if (empty($previous)) { + return null; + } + + // Copy record and clear previous time and id data so a new record is created. + $override = reset($previous); + $override->set('id', 0); + $override->set('resolved_at', 0); + $override->set_default_expiry(); + return $override; + } + + /** + * Marks all expired overrides as resolved + * + * @return void + */ + protected static function mark_expired_as_resolved() { + $conditions = "expires_at < ? AND resolved_at = 0"; + $overrides = self::get_records_select($conditions, [time()]); + foreach ($overrides as $override) { + $override->resolve(); + } + } + + /** + * Returns the human readable time until mute ends + * + * @return string + */ + public function get_time_until_mute_ends(): string { + $enddate = userdate($this->get('expires_at'), get_string('strftimedate', 'langconfig')); + $remainingtime = format_time($this->get('expires_at') - time()); + return "$enddate ($remainingtime)"; + } +} diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 2704cc3..dd44631 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -23,8 +23,13 @@ namespace tool_heartbeat\privacy; - -use core_privacy\local\request\context; +use core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\userlist; +use core_privacy\local\request\writer; +use tool_heartbeat\object\override; /** * Privacy provider. @@ -33,20 +38,141 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements - \core_privacy\local\metadata\null_provider { + \core_privacy\local\metadata\provider, + \core_privacy\local\request\plugin\provider, + \core_privacy\local\request\core_userlist_provider { + + /** + * Returns meta data about this system. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $collection): collection { + $collection->add_database_table( + override::TABLE, + [ + 'note' => 'privacy:metadata:tool_heartbeat_overrides:note', + 'url' => 'privacy:metadata:tool_heartbeat_overrides:url', + 'userid' => 'privacy:metadata:tool_heartbeat_overrides:userid', + 'usermodified' => 'privacy:metadata:tool_heartbeat_overrides:usermodified', + 'timecreated' => 'privacy:metadata:tool_heartbeat_overrides:timecreated', + 'timemodified' => 'privacy:metadata:tool_heartbeat_overrides:timemodified', + ], + 'privacy:metadata:tool_heartbeat_overrides' + ); + return $collection; + } + + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid): contextlist { + $contextlist = new contextlist(); + $contextlist->add_system_context(); + return $contextlist; + } + + /** + * Export all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + global $DB; - use \core_privacy\local\legacy_polyfill; + $userid = $contextlist->get_user()->id; + foreach ($contextlist as $context) { + if ($context->contextlevel == CONTEXT_SYSTEM) { + $list = []; + // Overrides with a matching userid. + $rows = $DB->get_records(override::TABLE, ['userid' => $userid]); + foreach ($rows as $row) { + $list[] = [ + 'userid' => $userid, + 'note' => $row->note, + 'url' => $row->url, + 'timecreated' => $row->timecreated, + ]; + } + // Overrides with a matching usermodified. + $rows = $DB->get_records(override::TABLE, ['usermodified' => $userid]); + foreach ($rows as $row) { + $list[] = [ + 'usermodified' => $userid, + 'note' => $row->note, + 'url' => $row->url, + 'timemodified' => $row->timemodified, + ]; + } + writer::with_context($context)->export_data( + [get_string('privacy:metadata:tool_heartbeat_overrides', 'tool_heartbeat')], + (object) $list + ); + } + } + } + + /** + * Delete all data for all users in the specified context. + * + * @param context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + global $DB; + if ($context->contextlevel == CONTEXT_SYSTEM) { + $DB->delete_records(override::TABLE, []); + } + } /** - * Get the language string identifier with the component's language - * file to explain why this plugin stores no data. + * Delete all user data for the specified user, in the specified contexts. * - * This function is compatible with old php version. (Diff is the underscore '_' in the beginning) - * But the get_reason is still available because of the trait legacy_polyfill. + * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. + */ + public static function delete_data_for_user(approved_contextlist $contextlist) { + global $DB; + $userid = $contextlist->get_user()->id; + foreach ($contextlist as $context) { + if ($context->contextlevel == CONTEXT_SYSTEM) { + $DB->delete_records(override::TABLE, ['userid' => $userid]); + $DB->delete_records(override::TABLE, ['usermodified' => $userid]); + } + } + } + + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + $context = $userlist->get_context(); + if ($context->contextlevel == CONTEXT_SYSTEM) { + $sql = "SELECT * FROM {tool_heartbeat_overrides}"; + $userlist->add_from_sql('userid', $sql, []); + $sql = "SELECT * FROM {tool_heartbeat_overrides}"; + $userlist->add_from_sql('usermodified', $sql, []); + } + } + + /** + * Delete multiple users within a single context. * - * @return string + * @param approved_userlist $userlist The approved context and user information to delete information for. */ - public static function get_reason(): string { - return 'privacy:no_data_reason'; + public static function delete_data_for_users(approved_userlist $userlist) { + global $DB; + $context = $userlist->get_context(); + if ($context->contextlevel == CONTEXT_SYSTEM) { + $users = $userlist->get_users(); + foreach ($users as $user) { + $DB->delete_records(override::TABLE, ['userid' => $user->id]); + $DB->delete_records(override::TABLE, ['usermodified' => $user->id]); + } + } } } diff --git a/classes/table/status_table.php b/classes/table/status_table.php new file mode 100644 index 0000000..f03bdf8 --- /dev/null +++ b/classes/table/status_table.php @@ -0,0 +1,135 @@ +. + +/** + * A table of check results + * + * @package tool_heartbeat + * @copyright 2023 Owen Herbert + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_heartbeat\table; + +use core\check\table; +use core\check\result; +use html_writer; +use renderer; +use tool_heartbeat\checker; + +/** + * A table of check results + * + * @copyright 2020 Brendan Heywood + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class status_table extends table { + + /** + * Render a table of checks + * + * @param renderer $output to use + * @return string html output + */ + public function render($output) { + + $html = ''; + + $table = new \html_table(); + $table->data = []; + $table->head = [ + get_string('status'), + get_string('check'), + get_string('summary'), + get_string('mute', 'tool_heartbeat'), + ]; + $table->colclasses = [ + 'rightalign status', + 'leftalign check', + 'leftalign summary', + 'leftalign mute', + ]; + $table->id = $this->type . 'reporttable'; + $table->attributes = ['class' => 'admintable ' . $this->type . 'report generaltable']; + + $checks = checker::remove_supressed_checks($this->checks); + foreach ($checks as $check) { + $ref = $check->get_ref(); + $result = checker::get_overridden_result($check); + $reportlink = new \moodle_url('/report/status/index.php', ['detail' => $ref]); + + $row = []; + $row[] = $output->check_result($result); + $row[] = $output->action_link($reportlink, $check->get_name()); + + $row[] = $result->get_summary() + . '
' + . html_writer::start_tag('small') + . $output->action_link($reportlink, get_string('moreinfo')) + . html_writer::end_tag('small'); + + $row[] = $this->get_override_html($output, $ref, $result); + $table->data[] = $row; + } + $html .= html_writer::table($table); + + return $html; + } + + /** + * Returns the html output for the override column. + * + * @param renderer $output + * @param string $ref + * @param result $result + * @return string html output + */ + private function get_override_html($output, string $ref, result $result): string { + $override = \tool_heartbeat\object\override::get_active_override($ref); + $overridelink = new \moodle_url('/admin/tool/heartbeat/override.php', ['ref' => $ref]); + $rowdata = ''; + + // If we have an existing override, display a link to edit and delete. + if (isset($override)) { + $dellink = new \moodle_url('/admin/tool/heartbeat/override.php', [ + 'ref' => $ref, + 'unmute' => true, + ]); + + $notes = $override->get('note'); + $url = $override->get('url'); + + $rowdata .= get_string('expiresat', 'tool_heartbeat') . ': ' . $override->get_time_until_mute_ends(); + $rowdata .= format_text($notes); + $rowdata .= !empty($url) ? html_writer::link($url, $url) . '
' : ''; + $rowdata .= html_writer::start_tag('small'); + $rowdata .= $output->action_link($overridelink, get_string('edit')); + $rowdata .= ' | '; + $rowdata .= $output->action_link($dellink, get_string('unmute', 'tool_heartbeat')); + $rowdata .= html_writer::end_tag('small'); + return $rowdata; + } + + // If the status of a check isn't normal, display a link to create an override. + $overridablestatus = [result::CRITICAL, result::WARNING, result::ERROR, result::UNKNOWN]; + if (in_array($result->get_status(), $overridablestatus)) { + $rowdata .= $output->action_link($overridelink, get_string('addmute', 'tool_heartbeat')); + return $rowdata; + } + + return $rowdata; + } +} diff --git a/db/install.xml b/db/install.xml new file mode 100644 index 0000000..b7165e2 --- /dev/null +++ b/db/install.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
\ No newline at end of file diff --git a/db/upgrade.php b/db/upgrade.php index 1533c01..ff83916 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -27,6 +27,10 @@ * @param int $oldversion */ function xmldb_tool_heartbeat_upgrade($oldversion) { + global $DB; + + $dbman = $DB->get_manager(); + if ($oldversion < 2023102400) { // If there are issues with split caches they need to be exposed // after some time for them to diverge. @@ -37,5 +41,36 @@ function xmldb_tool_heartbeat_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2023102400, 'tool', 'heartbeat'); } + if ($oldversion < 2024030800) { + // Define table tool_heartbeat_overrides to be created. + $table = new xmldb_table('tool_heartbeat_overrides'); + + // Adding fields to table tool_heartbeat_overrides. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('ref', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('override', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('note', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); + $table->add_field('url', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('expires_at', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('resolved_at', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table tool_heartbeat_overrides. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('reference', XMLDB_KEY_UNIQUE, ['ref', 'resolved_at']); + $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); + $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); + + // Conditionally launch create table for tool_heartbeat_overrides. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + // Update the plugin version. + upgrade_plugin_savepoint(true, 2024030800, 'tool', 'heartbeat'); + } + return true; } diff --git a/lang/en/tool_heartbeat.php b/lang/en/tool_heartbeat.php index b23d1e6..4c98053 100644 --- a/lang/en/tool_heartbeat.php +++ b/lang/en/tool_heartbeat.php @@ -89,6 +89,16 @@ $string['checkfailingtaskchecktask'] = 'Failing task: {$a}'; $string['checkfailingtaskok'] = '{$a} tasks running OK.'; $string['checkdirsizes'] = 'CFG->dataroot size'; +$string['mute'] = 'Mute'; +$string['unmute'] = 'Unmute'; +$string['expiresat'] = 'Muted until'; +$string['override'] = 'Display status'; +$string['addmute'] = 'Add mute'; +$string['editmute'] = 'Edit mute'; +$string['overriderestore'] = 'Fields have been pre-filled with information from a previous override.'; +$string['noterequired'] = 'Please add some notes'; +$string['settings:mutedefault'] = 'Default mute duration'; +$string['settings:mutedefault:desc'] = 'Adjust the default duration of a check mute.'; $string['settings:cachecheckheading'] = 'Cache consistency check'; $string['settings:shouldlogcacheping:heading'] = 'Log cache ping'; @@ -99,4 +109,10 @@ /* * Privacy provider (GDPR) */ -$string["privacy:no_data_reason"] = "The Heartbeat plugin does not store any personal data."; +$string['privacy:metadata:tool_heartbeat_overrides'] = 'Heartbeat Overrides'; +$string['privacy:metadata:tool_heartbeat_overrides:note'] = 'Override note added by user'; +$string['privacy:metadata:tool_heartbeat_overrides:url'] = 'Override URL added by user'; +$string['privacy:metadata:tool_heartbeat_overrides:userid'] = 'User who created the override'; +$string['privacy:metadata:tool_heartbeat_overrides:usermodified'] = 'User who modified the override'; +$string['privacy:metadata:tool_heartbeat_overrides:timecreated'] = 'Timestamp when the override was created'; +$string['privacy:metadata:tool_heartbeat_overrides:timemodified'] = 'Timestamp when the override was modified'; diff --git a/override.php b/override.php new file mode 100644 index 0000000..f1fe40b --- /dev/null +++ b/override.php @@ -0,0 +1,111 @@ +. + +/** + * System Status report + * + * @package tool_heartbeat + * @copyright 2023 Owen Herbert (owenherbert@catalyst-au.net) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_heartbeat\form\override_form; +use tool_heartbeat\object\override; + +define('NO_OUTPUT_BUFFERING', true); + +require('../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +admin_externalpage_setup('tool_heartbeat_status'); + +// Check if an ID is provided. +$ref = required_param('ref', PARAM_TEXT); +$unmute = optional_param('unmute', null, PARAM_BOOL); + +// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important. +$PAGE->set_context(context_system::instance()); +$PAGE->set_url(new moodle_url('/admin/tool/heartbeat/override.php', ['ref' => $ref])); + +// The URL used for redirection. +$statuspage = new moodle_url('/admin/tool/heartbeat/status.php'); + +// Attempt to instantiate a persistent object. +$override = override::get_active_override($ref); + +// Handle unmute. +if (!empty($unmute) && !empty($override) && $unmute) { + try { + $override->resolve(); + \core\notification::success(get_string('changessaved')); + } catch (Exception $e) { + \core\notification::error($e->getMessage()); + } + redirect($statuspage); +} + +// Try restoring override. +$restored = false; +if (empty($override)) { + $override = override::get_restored_override($ref); + $restored = !empty($override); +} + +// Create the form instance. We need to use the current URL and the custom data. +$customdata = [ + 'persistent' => $override, + 'ref' => $ref, +]; + +// Create override form. +$form = new override_form($PAGE->url->out(false), $customdata); + +// Get the data. This ensures that the form was validated. +if ($form->is_cancelled()) { + redirect($statuspage); +} else if ($data = $form->get_data()) { + + try { + if (empty($data->id)) { + // If there is no ID we need to create a new persistent. + $data->userid = $USER->id; + $override = new override(0, $data); + $override->create(); + } else { + // If we have an ID we need to update the persistent. + $override->from_record($data); + $override->update(); + } + \core\notification::success(get_string('changessaved')); + } catch (Exception $e) { + \core\notification::error($e->getMessage()); + } + + // Redirect to heartbeat status page when done. + redirect($statuspage); +} + +if ($restored) { + \core\notification::INFO(get_string('overriderestore', 'tool_heartbeat')); +} + +// Display the mandatory header and footer. +// And display the form, and its validation errors if there are any. +echo $OUTPUT->header(); +$headingstring = (empty($override) || $restored) ? 'addmute' : 'editmute'; +echo $OUTPUT->heading(get_string($headingstring, 'tool_heartbeat')); +$form->display(); +echo $OUTPUT->footer(); diff --git a/settings.php b/settings.php index 7892ae1..259619f 100644 --- a/settings.php +++ b/settings.php @@ -26,9 +26,19 @@ if ($hassiteconfig) { - $settings = new admin_settingpage('tool_heartbeat', get_string('pluginname', 'tool_heartbeat')); + $category = new admin_category('heartbeatfolder', get_string('pluginname', 'tool_heartbeat')); + $ADMIN->add('tools', $category); + + $statuspage = new admin_externalpage( + 'tool_heartbeat_status', + get_string('pluginname', 'report_status'), + new moodle_url('/admin/tool/heartbeat/status.php') + ); + $ADMIN->add('heartbeatfolder', $statuspage); + + $settings = new admin_settingpage('tool_heartbeat', get_string('settings')); + $ADMIN->add('heartbeatfolder', $settings); - $ADMIN->add('tools', $settings); if (!during_initial_install()) { $options = array( @@ -91,6 +101,10 @@ get_string('errorascritical', 'tool_heartbeat'), get_string('errorascritical_desc', 'tool_heartbeat', $time->format('e P')), 'warning', $opts)); + $settings->add(new admin_setting_configduration('tool_heartbeat/mutedefault', + get_string('settings:mutedefault', 'tool_heartbeat'), + get_string('settings:mutedefault:desc', 'tool_heartbeat'), 12 * WEEKSECS, WEEKSECS)); + $example = '\logstore_standard\task\cleanup_task, 5, 5, 5'; $settings->add(new admin_setting_configtextarea('tool_heartbeat/tasklatencymonitoring', get_string('tasklatencymonitoring', 'tool_heartbeat'), diff --git a/status.php b/status.php new file mode 100644 index 0000000..a4b899c --- /dev/null +++ b/status.php @@ -0,0 +1,40 @@ +. + +/** + * System Status report + * + * @package tool_heartbeat + * @copyright 2023 Owen Herbert (owenherbert@catalyst-au.net) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_heartbeat\table\status_table; + +define('NO_OUTPUT_BUFFERING', true); + +require('../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +admin_externalpage_setup('tool_heartbeat_status', '', null, '', ['pagelayout' => 'report']); + +$url = '/admin/tool/heartbeat/status.php'; +$table = new status_table('status', $url); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('pluginname', 'report_status')); +echo $table->render($OUTPUT); +echo $OUTPUT->footer(); diff --git a/version.php b/version.php index 6b9c901..51cbcd5 100644 --- a/version.php +++ b/version.php @@ -24,8 +24,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024012200; -$plugin->release = 2024012200; // Match release exactly to version. +$plugin->version = 2024030800; +$plugin->release = 2024030800; // Match release exactly to version. $plugin->requires = 2020061500; // Support for 3.9 and above, due to the Check API. $plugin->supported = [39, 401]; $plugin->component = 'tool_heartbeat';