chore: initial snapshot of 2meet-data-optimizer v0.1.0

Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
This commit is contained in:
2026-07-31 05:06:36 +08:00
commit d36bb954d1
206 changed files with 66538 additions and 0 deletions
@@ -0,0 +1,238 @@
<?php
/**
* TMDO_Email_Notifier — Threshold-based email alerts (v2.4.0 M10).
*
* Subscribes to action `wpdo/health_alert_critical` (fired by Health_Cron M6
* when critical_count > 0). Sends a plain-text email to the configured
* recipient(s) with a 24h throttle key (per-alert-fingerprint) so admins
* don't get spammed.
*
* Default OFF — admin must explicitly enable via Settings tab.
*
* @package WP_Data_Optimizer
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Email notifier — stateless static API.
*/
class TMDO_Email_Notifier {
public const OPT_ENABLED = 'wpdo_email_alerts_enabled';
public const OPT_RECIPIENT = 'wpdo_alert_email';
public const OPT_THROTTLE_HRS = 'wpdo_alert_throttle_hours';
/** Default throttle window. */
public const DEFAULT_THROTTLE_HOURS = 24;
/**
* Register subscriber.
*
* @return void
*/
public static function register(): void {
add_action( 'wpdo/health_alert_critical', array( __CLASS__, 'maybe_send' ), 10, 1 );
}
/**
* Decide + send.
*
* @param array $summary Health summary from Health_Cron::run().
* @return bool true on send, false on skip / fail.
*/
public static function maybe_send( array $summary ): bool {
if ( ! self::is_enabled() ) {
return false;
}
$recipient = self::recipient();
if ( ! is_email( $recipient ) ) {
return false;
}
$fingerprint = self::fingerprint( $summary );
if ( self::is_throttled( $fingerprint ) ) {
return false;
}
$subject = self::build_subject( $summary );
$body = self::build_body( $summary );
$sent = wp_mail( $recipient, $subject, $body );
if ( $sent ) {
self::mark_sent( $fingerprint );
if ( class_exists( 'TMDO_Logger' ) ) {
TMDO_Logger::info(
'email_alert_sent',
array(
'fingerprint' => $fingerprint,
'critical' => (int) ( $summary['critical_count'] ?? 0 ),
'recipient' => $recipient,
)
);
}
}
return (bool) $sent;
}
// ─── settings accessors ─────────────────────────────────────────────
/**
* Whether email alerts are enabled.
*
* @return bool
*/
public static function is_enabled(): bool {
return '1' === (string) get_option( self::OPT_ENABLED, '0' );
}
/**
* Alert recipient email address (falls back to admin_email).
*
* @return string
*/
public static function recipient(): string {
$v = (string) get_option( self::OPT_RECIPIENT, '' );
if ( '' === $v ) {
$v = (string) get_option( 'admin_email', '' );
}
return $v;
}
/**
* Throttle window in hours (clamped 1..168).
*
* @return int
*/
public static function throttle_hours(): int {
$v = (int) get_option( self::OPT_THROTTLE_HRS, self::DEFAULT_THROTTLE_HOURS );
return max( 1, min( 168, $v ) ); // Clamp 1h..1week.
}
// ─── private helpers ──────────────────────────────────────────────
/**
* Stable fingerprint from the alert content (so identical incidents
* dedupe within the throttle window).
*
* @param array $summary Summary array.
* @return string md5 hash.
*/
private static function fingerprint( array $summary ): string {
$relevant = array(
'critical_count' => (int) ( $summary['critical_count'] ?? 0 ),
'first_critical' => null,
);
foreach ( (array) ( $summary['tests'] ?? array() ) as $slug => $t ) {
if ( 'critical' === ( $t['status'] ?? '' ) ) {
$relevant['first_critical'] = $slug;
break;
}
}
return md5( wp_json_encode( $relevant ) );
}
/**
* Check whether an alert with this fingerprint was recently sent.
*
* @param string $fingerprint Alert fingerprint (md5).
* @return bool
*/
private static function is_throttled( string $fingerprint ): bool {
$key = 'wpdo_alert_sent_' . $fingerprint;
return false !== get_transient( $key );
}
/**
* Record that an alert was sent (sets throttle transient).
*
* @param string $fingerprint Alert fingerprint (md5).
* @return void
*/
private static function mark_sent( string $fingerprint ): void {
$key = 'wpdo_alert_sent_' . $fingerprint;
set_transient( $key, time(), self::throttle_hours() * HOUR_IN_SECONDS );
}
/**
* Build subject. Site name + critical count + first slug.
*
* @param array $summary Summary array.
* @return string
*/
private static function build_subject( array $summary ): string {
$site = (string) get_option( 'blogname', 'WordPress' );
$crit = (int) ( $summary['critical_count'] ?? 0 );
$first = '';
foreach ( (array) ( $summary['tests'] ?? array() ) as $slug => $t ) {
if ( 'critical' === ( $t['status'] ?? '' ) ) {
$first = $slug;
break;
}
}
return sprintf(
/* translators: 1: site name, 2: critical count, 3: first critical test slug */
__( '[%1$s] WPDO 警告:%2$d 項 critical (%3$s)', '2meet-data-optimizer' ),
$site,
$crit,
$first
);
}
/**
* Build plain-text body.
*
* @param array $summary Summary array.
* @return string
*/
private static function build_body( array $summary ): string {
$site_url = home_url( '/' );
$health_url = admin_url( 'site-health.php' );
$wpdo_url = admin_url( 'tools.php?page=wp-data-optimizer&tab=doctor' );
$lines = array();
$lines[] = __( 'WP Data Optimizer 自動健康檢查發現 critical 警告。', '2meet-data-optimizer' );
$lines[] = '';
$lines[] = sprintf( '站台:%s', $site_url );
$lines[] = sprintf(
/* translators: %s: timestamp */
__( '檢查時間:%s UTC', '2meet-data-optimizer' ),
(string) ( $summary['ran_at'] ?? '?' )
);
$lines[] = sprintf(
/* translators: 1: critical count, 2: recommended count */
__( '結果:%1$d critical / %2$d recommended', '2meet-data-optimizer' ),
(int) ( $summary['critical_count'] ?? 0 ),
(int) ( $summary['recommended_count'] ?? 0 )
);
$lines[] = '';
$lines[] = __( '失敗的檢查:', '2meet-data-optimizer' );
foreach ( (array) ( $summary['tests'] ?? array() ) as $slug => $t ) {
if ( in_array( ( $t['status'] ?? '' ), array( 'critical', 'recommended' ), true ) ) {
$lines[] = sprintf(
' [%s] %s — %s',
strtoupper( (string) $t['status'] ),
$slug,
(string) ( $t['description'] ?? '' )
);
}
}
$lines[] = '';
$lines[] = __( '建議行動:', '2meet-data-optimizer' );
$lines[] = ' · ' . sprintf(
/* translators: %s: WPDO Doctor admin URL */
__( '立即查看 WPDO Doctor%s', '2meet-data-optimizer' ),
$wpdo_url
);
$lines[] = ' · ' . sprintf(
/* translators: %s: WP Site Health admin URL */
__( '或 WP Site Health%s', '2meet-data-optimizer' ),
$health_url
);
$lines[] = '';
$lines[] = sprintf(
/* translators: %d: hours */
__( '註:相同警告在 %d 小時內不會重發;前往設定可調整 throttle / 收件人 / 關閉。', '2meet-data-optimizer' ),
self::throttle_hours()
);
return implode( "\n", $lines );
}
}