feat: Reorganize settings page with tabbed interface (v0.1.1)
All checks were successful
Create Release Package / build-release (push) Successful in 57s

- Add tabbed navigation (License, Metrics, Help)
- Move Prometheus configuration to dedicated Help tab
- Separate static and runtime metrics with descriptions
- Add admin CSS for tab styling
- Add endpoint info, curl examples, and metrics reference in Help tab

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 14:30:11 +01:00
parent 6256ba777c
commit bc108f6bd5
4 changed files with 308 additions and 86 deletions

View File

@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.1] - 2026-02-02
### Changed
- Reorganized settings page with tabbed interface (License, Metrics, Help tabs)
- Moved Prometheus configuration help to dedicated Help tab
- Separated static and runtime metrics in settings with descriptions
- Added admin CSS for improved tab styling
### Added
- New Help tab with endpoint information, curl examples, and metrics reference table
- Custom code examples section in Help tab
## [0.1.0] - 2026-02-02 ## [0.1.0] - 2026-02-02
### Added ### Added

47
assets/css/admin.css Normal file
View File

@@ -0,0 +1,47 @@
/**
* WP Prometheus Admin Styles
*
* @package WP_Prometheus
*/
/* Tab content styling */
.wp-prometheus-tab-content {
margin-top: 20px;
}
/* License status box */
.wp-prometheus-license-status {
margin: 15px 0;
}
/* Help tab code blocks */
.wp-prometheus-tab-content pre {
background: #f1f1f1;
padding: 15px;
overflow-x: auto;
margin: 15px 0;
border-radius: 3px;
border: 1px solid #ddd;
}
/* Help tab tables */
.wp-prometheus-tab-content .widefat {
margin: 15px 0;
}
.wp-prometheus-tab-content .widefat code {
background: none;
padding: 0;
}
/* Metrics fieldset */
.wp-prometheus-tab-content fieldset p strong {
display: block;
margin-bottom: 8px;
font-size: 14px;
}
/* Form table adjustments for tabs */
.wp-prometheus-tab-content .form-table {
margin-top: 10px;
}

View File

@@ -21,15 +21,38 @@ if ( ! defined( 'ABSPATH' ) ) {
*/ */
class Settings { class Settings {
/**
* Available tabs.
*
* @var array
*/
private array $tabs = array();
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() { public function __construct() {
$this->tabs = array(
'license' => __( 'License', 'wp-prometheus' ),
'metrics' => __( 'Metrics', 'wp-prometheus' ),
'help' => __( 'Help', 'wp-prometheus' ),
);
add_action( 'admin_menu', array( $this, 'add_settings_page' ) ); add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
} }
/**
* Get current tab.
*
* @return string
*/
private function get_current_tab(): string {
$tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'license';
return array_key_exists( $tab, $this->tabs ) ? $tab : 'license';
}
/** /**
* Add settings page to admin menu. * Add settings page to admin menu.
* *
@@ -51,56 +74,48 @@ class Settings {
* @return void * @return void
*/ */
public function register_settings(): void { public function register_settings(): void {
// License settings section. // Register settings for metrics tab.
add_settings_section( register_setting( 'wp_prometheus_metrics_settings', 'wp_prometheus_auth_token', array(
'wp_prometheus_license_section', 'type' => 'string',
__( 'License Settings', 'wp-prometheus' ), 'sanitize_callback' => 'sanitize_text_field',
array( $this, 'render_license_section' ), ) );
'wp-prometheus'
); register_setting( 'wp_prometheus_metrics_settings', 'wp_prometheus_enabled_metrics', array(
'type' => 'array',
'sanitize_callback' => array( $this, 'sanitize_metrics' ),
) );
// Auth token section. // Auth token section.
add_settings_section( add_settings_section(
'wp_prometheus_auth_section', 'wp_prometheus_auth_section',
__( 'Authentication', 'wp-prometheus' ), __( 'Authentication', 'wp-prometheus' ),
array( $this, 'render_auth_section' ), array( $this, 'render_auth_section' ),
'wp-prometheus' 'wp-prometheus-metrics'
); );
// Metrics section. // Metrics section.
add_settings_section( add_settings_section(
'wp_prometheus_metrics_section', 'wp_prometheus_metrics_section',
__( 'Default Metrics', 'wp-prometheus' ), __( 'Enabled Metrics', 'wp-prometheus' ),
array( $this, 'render_metrics_section' ), array( $this, 'render_metrics_section' ),
'wp-prometheus' 'wp-prometheus-metrics'
); );
// Register settings.
register_setting( 'wp_prometheus_settings', 'wp_prometheus_auth_token', array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
) );
register_setting( 'wp_prometheus_settings', 'wp_prometheus_enabled_metrics', array(
'type' => 'array',
'sanitize_callback' => array( $this, 'sanitize_metrics' ),
) );
// Auth token field. // Auth token field.
add_settings_field( add_settings_field(
'wp_prometheus_auth_token', 'wp_prometheus_auth_token',
__( 'Auth Token', 'wp-prometheus' ), __( 'Auth Token', 'wp-prometheus' ),
array( $this, 'render_auth_token_field' ), array( $this, 'render_auth_token_field' ),
'wp-prometheus', 'wp-prometheus-metrics',
'wp_prometheus_auth_section' 'wp_prometheus_auth_section'
); );
// Enabled metrics field. // Enabled metrics field.
add_settings_field( add_settings_field(
'wp_prometheus_enabled_metrics', 'wp_prometheus_enabled_metrics',
__( 'Enabled Metrics', 'wp-prometheus' ), __( 'Select Metrics', 'wp-prometheus' ),
array( $this, 'render_enabled_metrics_field' ), array( $this, 'render_enabled_metrics_field' ),
'wp-prometheus', 'wp-prometheus-metrics',
'wp_prometheus_metrics_section' 'wp_prometheus_metrics_section'
); );
} }
@@ -116,6 +131,13 @@ class Settings {
return; return;
} }
wp_enqueue_style(
'wp-prometheus-admin',
WP_PROMETHEUS_URL . 'assets/css/admin.css',
array(),
WP_PROMETHEUS_VERSION
);
wp_enqueue_script( wp_enqueue_script(
'wp-prometheus-admin', 'wp-prometheus-admin',
WP_PROMETHEUS_URL . 'assets/js/admin.js', WP_PROMETHEUS_URL . 'assets/js/admin.js',
@@ -140,8 +162,10 @@ class Settings {
return; return;
} }
$current_tab = $this->get_current_tab();
// Handle license settings save. // Handle license settings save.
if ( isset( $_POST['wp_prometheus_license_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_prometheus_license_nonce'] ), 'wp_prometheus_save_license' ) ) { if ( 'license' === $current_tab && isset( $_POST['wp_prometheus_license_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_prometheus_license_nonce'] ), 'wp_prometheus_save_license' ) ) {
LicenseManager::save_settings( array( LicenseManager::save_settings( array(
'license_key' => isset( $_POST['license_key'] ) ? sanitize_text_field( wp_unslash( $_POST['license_key'] ) ) : '', 'license_key' => isset( $_POST['license_key'] ) ? sanitize_text_field( wp_unslash( $_POST['license_key'] ) ) : '',
'server_url' => isset( $_POST['license_server_url'] ) ? esc_url_raw( wp_unslash( $_POST['license_server_url'] ) ) : '', 'server_url' => isset( $_POST['license_server_url'] ) ? esc_url_raw( wp_unslash( $_POST['license_server_url'] ) ) : '',
@@ -154,27 +178,64 @@ class Settings {
<div class="wrap"> <div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1> <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php $this->render_license_form(); ?> <?php $this->render_tabs( $current_tab ); ?>
<form method="post" action="options.php"> <div class="wp-prometheus-tab-content">
<?php <?php
settings_fields( 'wp_prometheus_settings' ); switch ( $current_tab ) {
do_settings_sections( 'wp-prometheus' ); case 'license':
submit_button(); $this->render_license_tab();
break;
case 'metrics':
$this->render_metrics_tab();
break;
case 'help':
$this->render_help_tab();
break;
}
?> ?>
</form> </div>
<?php $this->render_endpoint_info(); ?>
</div> </div>
<?php <?php
} }
/** /**
* Render license settings form. * Render tabs navigation.
*
* @param string $current_tab Current active tab.
* @return void
*/
private function render_tabs( string $current_tab ): void {
?>
<nav class="nav-tab-wrapper wp-clearfix">
<?php
foreach ( $this->tabs as $tab_id => $tab_name ) {
$tab_url = add_query_arg(
array(
'page' => 'wp-prometheus',
'tab' => $tab_id,
),
admin_url( 'options-general.php' )
);
$active_class = ( $current_tab === $tab_id ) ? ' nav-tab-active' : '';
printf(
'<a href="%s" class="nav-tab%s">%s</a>',
esc_url( $tab_url ),
esc_attr( $active_class ),
esc_html( $tab_name )
);
}
?>
</nav>
<?php
}
/**
* Render license tab content.
* *
* @return void * @return void
*/ */
private function render_license_form(): void { private function render_license_tab(): void {
$license_key = LicenseManager::get_license_key(); $license_key = LicenseManager::get_license_key();
$server_url = LicenseManager::get_server_url(); $server_url = LicenseManager::get_server_url();
$license_status = LicenseManager::get_cached_status(); $license_status = LicenseManager::get_cached_status();
@@ -204,7 +265,7 @@ class Settings {
$status_class = $status_classes[ $license_status ] ?? 'notice-info'; $status_class = $status_classes[ $license_status ] ?? 'notice-info';
$status_message = $status_messages[ $license_status ] ?? __( 'Unknown status.', 'wp-prometheus' ); $status_message = $status_messages[ $license_status ] ?? __( 'Unknown status.', 'wp-prometheus' );
?> ?>
<div class="wp-prometheus-license-status notice <?php echo esc_attr( $status_class ); ?>" style="padding: 12px;"> <div class="wp-prometheus-license-status notice <?php echo esc_attr( $status_class ); ?>" style="padding: 12px; margin: 15px 0;">
<strong><?php echo esc_html( $status_message ); ?></strong> <strong><?php echo esc_html( $status_message ); ?></strong>
<?php if ( 'valid' === $license_status && ! empty( $license_data['expires_at'] ) ) : ?> <?php if ( 'valid' === $license_status && ! empty( $license_data['expires_at'] ) ) : ?>
<br><span class="description"> <br><span class="description">
@@ -233,7 +294,7 @@ class Settings {
<form method="post" action="" id="wp-prometheus-license-form"> <form method="post" action="" id="wp-prometheus-license-form">
<?php wp_nonce_field( 'wp_prometheus_save_license', 'wp_prometheus_license_nonce' ); ?> <?php wp_nonce_field( 'wp_prometheus_save_license', 'wp_prometheus_license_nonce' ); ?>
<table class="form-table"> <table class="form-table" role="presentation">
<tr> <tr>
<th scope="row"> <th scope="row">
<label for="license_server_url"><?php esc_html_e( 'License Server URL', 'wp-prometheus' ); ?></label> <label for="license_server_url"><?php esc_html_e( 'License Server URL', 'wp-prometheus' ); ?></label>
@@ -279,17 +340,127 @@ class Settings {
</form> </form>
<div id="wp-prometheus-license-message" style="display: none; margin-top: 10px;"></div> <div id="wp-prometheus-license-message" style="display: none; margin-top: 10px;"></div>
<hr>
<?php <?php
} }
/** /**
* Render license section description. * Render metrics tab content.
* *
* @return void * @return void
*/ */
public function render_license_section(): void { private function render_metrics_tab(): void {
// License section rendered separately in render_license_form(). ?>
<form method="post" action="options.php">
<?php
settings_fields( 'wp_prometheus_metrics_settings' );
do_settings_sections( 'wp-prometheus-metrics' );
submit_button();
?>
</form>
<?php
}
/**
* Render help tab content.
*
* @return void
*/
private function render_help_tab(): void {
$token = get_option( 'wp_prometheus_auth_token', '' );
$endpoint_url = home_url( '/metrics/' );
?>
<h2><?php esc_html_e( 'Prometheus Configuration', 'wp-prometheus' ); ?></h2>
<p><?php esc_html_e( 'Add the following to your prometheus.yml:', 'wp-prometheus' ); ?></p>
<pre style="background: #f1f1f1; padding: 15px; overflow-x: auto; margin: 15px 0;">scrape_configs:
- job_name: 'wordpress'
static_configs:
- targets: ['<?php echo esc_html( wp_parse_url( home_url(), PHP_URL_HOST ) ); ?>']
metrics_path: '/metrics/'
scheme: '<?php echo esc_html( wp_parse_url( home_url(), PHP_URL_SCHEME ) ); ?>'
authorization:
type: Bearer
credentials: '<?php echo esc_html( $token ); ?>'</pre>
<h3><?php esc_html_e( 'Endpoint Information', 'wp-prometheus' ); ?></h3>
<table class="form-table" role="presentation">
<tr>
<th scope="row"><?php esc_html_e( 'Metrics URL', 'wp-prometheus' ); ?></th>
<td><code><?php echo esc_url( $endpoint_url ); ?></code></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Auth Token', 'wp-prometheus' ); ?></th>
<td><code><?php echo esc_html( $token ); ?></code></td>
</tr>
</table>
<h3><?php esc_html_e( 'Testing the Endpoint', 'wp-prometheus' ); ?></h3>
<p><?php esc_html_e( 'You can test the endpoint using curl:', 'wp-prometheus' ); ?></p>
<pre style="background: #f1f1f1; padding: 15px; overflow-x: auto; margin: 15px 0;">curl -H "Authorization: Bearer <?php echo esc_html( $token ); ?>" <?php echo esc_url( $endpoint_url ); ?></pre>
<h3><?php esc_html_e( 'Available Metrics', 'wp-prometheus' ); ?></h3>
<table class="widefat striped" style="margin: 15px 0;">
<thead>
<tr>
<th><?php esc_html_e( 'Metric', 'wp-prometheus' ); ?></th>
<th><?php esc_html_e( 'Type', 'wp-prometheus' ); ?></th>
<th><?php esc_html_e( 'Description', 'wp-prometheus' ); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>wordpress_info</code></td>
<td><?php esc_html_e( 'Gauge', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'WordPress installation info', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_users_total</code></td>
<td><?php esc_html_e( 'Gauge', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'Total users by role', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_posts_total</code></td>
<td><?php esc_html_e( 'Gauge', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'Total posts by type and status', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_comments_total</code></td>
<td><?php esc_html_e( 'Gauge', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'Total comments by status', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_plugins_total</code></td>
<td><?php esc_html_e( 'Gauge', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'Total plugins by status', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_http_requests_total</code></td>
<td><?php esc_html_e( 'Counter', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'HTTP requests by method, status, endpoint', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_http_request_duration_seconds</code></td>
<td><?php esc_html_e( 'Histogram', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'HTTP request duration distribution', 'wp-prometheus' ); ?></td>
</tr>
<tr>
<td><code>wordpress_db_queries_total</code></td>
<td><?php esc_html_e( 'Counter', 'wp-prometheus' ); ?></td>
<td><?php esc_html_e( 'Database queries by endpoint', 'wp-prometheus' ); ?></td>
</tr>
</tbody>
</table>
<h3><?php esc_html_e( 'Custom Metrics', 'wp-prometheus' ); ?></h3>
<p><?php esc_html_e( 'You can add custom metrics using the wp_prometheus_collect_metrics action:', 'wp-prometheus' ); ?></p>
<pre style="background: #f1f1f1; padding: 15px; overflow-x: auto; margin: 15px 0;">add_action( 'wp_prometheus_collect_metrics', function( $collector ) {
$gauge = $collector->register_gauge(
'my_custom_metric',
'Description of my metric',
array( 'label1', 'label2' )
);
$gauge->set( 42, array( 'value1', 'value2' ) );
} );</pre>
<?php
} }
/** /**
@@ -307,7 +478,7 @@ class Settings {
* @return void * @return void
*/ */
public function render_metrics_section(): void { public function render_metrics_section(): void {
echo '<p>' . esc_html__( 'Select which default metrics to expose.', 'wp-prometheus' ) . '</p>'; echo '<p>' . esc_html__( 'Select which metrics to expose on the /metrics endpoint.', 'wp-prometheus' ) . '</p>';
} }
/** /**
@@ -336,18 +507,26 @@ class Settings {
*/ */
public function render_enabled_metrics_field(): void { public function render_enabled_metrics_field(): void {
$enabled = get_option( 'wp_prometheus_enabled_metrics', array() ); $enabled = get_option( 'wp_prometheus_enabled_metrics', array() );
$metrics = array(
'wordpress_info' => __( 'WordPress Info (version, PHP version, multisite)', 'wp-prometheus' ), $static_metrics = array(
'wordpress_users_total' => __( 'Total Users by Role', 'wp-prometheus' ), 'wordpress_info' => __( 'WordPress Info (version, PHP version, multisite)', 'wp-prometheus' ),
'wordpress_posts_total' => __( 'Total Posts by Type and Status', 'wp-prometheus' ), 'wordpress_users_total' => __( 'Total Users by Role', 'wp-prometheus' ),
'wordpress_comments_total' => __( 'Total Comments by Status', 'wp-prometheus' ), 'wordpress_posts_total' => __( 'Total Posts by Type and Status', 'wp-prometheus' ),
'wordpress_plugins_total' => __( 'Total Plugins (active/inactive)', 'wp-prometheus' ), 'wordpress_comments_total' => __( 'Total Comments by Status', 'wp-prometheus' ),
'wordpress_http_requests_total' => __( 'HTTP Requests Total (by method, status, endpoint)', 'wp-prometheus' ), 'wordpress_plugins_total' => __( 'Total Plugins (active/inactive)', 'wp-prometheus' ),
'wordpress_http_request_duration_seconds' => __( 'HTTP Request Duration (histogram)', 'wp-prometheus' ),
'wordpress_db_queries_total' => __( 'Database Queries Total (by endpoint)', 'wp-prometheus' ),
); );
foreach ( $metrics as $key => $label ) { $runtime_metrics = array(
'wordpress_http_requests_total' => __( 'HTTP Requests Total (by method, status, endpoint)', 'wp-prometheus' ),
'wordpress_http_request_duration_seconds' => __( 'HTTP Request Duration (histogram)', 'wp-prometheus' ),
'wordpress_db_queries_total' => __( 'Database Queries Total (by endpoint)', 'wp-prometheus' ),
);
echo '<fieldset>';
echo '<legend class="screen-reader-text"><span>' . esc_html__( 'Static Metrics', 'wp-prometheus' ) . '</span></legend>';
echo '<p><strong>' . esc_html__( 'Static Metrics', 'wp-prometheus' ) . '</strong></p>';
foreach ( $static_metrics as $key => $label ) {
?> ?>
<label style="display: block; margin-bottom: 5px;"> <label style="display: block; margin-bottom: 5px;">
<input type="checkbox" name="wp_prometheus_enabled_metrics[]" <input type="checkbox" name="wp_prometheus_enabled_metrics[]"
@@ -357,40 +536,22 @@ class Settings {
</label> </label>
<?php <?php
} }
}
/** echo '<br><p><strong>' . esc_html__( 'Runtime Metrics', 'wp-prometheus' ) . '</strong></p>';
* Render endpoint info. echo '<p class="description" style="margin-bottom: 10px;">' . esc_html__( 'Runtime metrics track data across requests. Enable only what you need to minimize performance impact.', 'wp-prometheus' ) . '</p>';
*
* @return void foreach ( $runtime_metrics as $key => $label ) {
*/
private function render_endpoint_info(): void {
$token = get_option( 'wp_prometheus_auth_token', '' );
$endpoint_url = home_url( '/metrics/' );
?>
<hr>
<h2><?php esc_html_e( 'Prometheus Configuration', 'wp-prometheus' ); ?></h2>
<p><?php esc_html_e( 'Add the following to your prometheus.yml:', 'wp-prometheus' ); ?></p>
<pre style="background: #f1f1f1; padding: 15px; overflow-x: auto;">
scrape_configs:
- job_name: 'wordpress'
static_configs:
- targets: ['<?php echo esc_html( wp_parse_url( home_url(), PHP_URL_HOST ) ); ?>']
metrics_path: '/metrics/'
scheme: '<?php echo esc_html( wp_parse_url( home_url(), PHP_URL_SCHEME ) ); ?>'
authorization:
type: Bearer
credentials: '<?php echo esc_html( $token ); ?>'</pre>
<p>
<?php
printf(
/* translators: %s: Endpoint URL */
esc_html__( 'Metrics endpoint: %s', 'wp-prometheus' ),
'<code>' . esc_url( $endpoint_url ) . '</code>'
);
?> ?>
</p> <label style="display: block; margin-bottom: 5px;">
<?php <input type="checkbox" name="wp_prometheus_enabled_metrics[]"
value="<?php echo esc_attr( $key ); ?>"
<?php checked( in_array( $key, $enabled, true ) ); ?>>
<?php echo esc_html( $label ); ?>
</label>
<?php
}
echo '</fieldset>';
} }
/** /**

View File

@@ -3,7 +3,7 @@
* Plugin Name: WP Prometheus * Plugin Name: WP Prometheus
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-prometheus * Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-prometheus
* Description: Prometheus metrics endpoint for WordPress with extensible hooks for custom metrics. * Description: Prometheus metrics endpoint for WordPress with extensible hooks for custom metrics.
* Version: 0.1.0 * Version: 0.1.1
* Requires at least: 6.4 * Requires at least: 6.4
* Requires PHP: 8.3 * Requires PHP: 8.3
* Author: Marco Graetsch * Author: Marco Graetsch
@@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* *
* @var string * @var string
*/ */
define( 'WP_PROMETHEUS_VERSION', '0.1.0' ); define( 'WP_PROMETHEUS_VERSION', '0.1.1' );
/** /**
* Plugin file path. * Plugin file path.