';
echo '
Site Healthcheck ';
if ($msg = get_transient('att_hc_install_message')) {
delete_transient('att_hc_install_message');
echo '';
}
if (!$session) {
att_hc_render_start_panel();
echo '';
return;
}
if ($session->is_finished()) {
att_hc_render_finished_panel($session);
echo '';
return;
}
att_hc_render_active_session($session);
echo '';
}
function att_hc_render_start_panel(): void {
?>
Start a healthcheck
This will create a new in-progress session for . One session per site at a time.
progress();
$tech = get_userdata($session->technician_id());
?>
Session: id()); ?> ·
Started: started_at())); ?> ·
Technician: display_name : '#' . $session->technician_id()); ?> ·
/ steps
WP wp_version()); ?> · PHP php_version()); ?> · Site site_url()); ?>
';
att_hc_render_sidebar($session);
echo '';
foreach (ATT_HC_Steps::instance()->all() as $step) {
att_hc_render_step_card($session, $step);
}
echo '
';
}
function att_hc_render_sidebar(ATT_HC_Session $session): void {
echo '';
}
function att_hc_render_blocked_summary(ATT_HC_Session $session): void {
$blocked = [];
foreach (ATT_HC_Steps::instance()->all() as $step) {
$state = $session->step_state($step->id());
if ($state['status'] === ATT_HC_Session::STATUS_BLOCKED) {
$blocked[] = ['step' => $step, 'state' => $state];
}
}
if (!$blocked) return;
echo '';
echo '
Stop & escalate ';
echo '
The following steps are blocked. Resolve or escalate before continuing:
';
echo '
';
foreach ($blocked as $b) {
$esc = $b['step']->escalation();
echo '' . esc_html($b['step']->title()) . ' ';
if ($esc) echo ' — ' . esc_html($esc);
if (!empty($b['state']['notes'])) echo '' . esc_html($b['state']['notes']) . ' ';
echo ' ';
}
echo ' ';
}
function att_hc_render_diff_summary(ATT_HC_Session $session): void {
$prev = ATT_HC_Session::previous();
if (!$prev) return;
// Aggregate findings by step+id from each session
$current_idx = [];
foreach (ATT_HC_Steps::instance()->all() as $step) {
$r = $session->get_autocheck($step->id());
if (!$r) continue;
foreach ($r['findings'] as $f) {
$current_idx[$step->id() . '|' . $f['id']] = $f;
}
}
$prev_idx = [];
foreach (ATT_HC_Steps::instance()->all() as $step) {
$r = $prev->get_autocheck($step->id());
if (!$r) continue;
foreach ($r['findings'] as $f) {
$prev_idx[$step->id() . '|' . $f['id']] = $f;
}
}
$new = $resolved = $changed = [];
foreach ($current_idx as $k => $f) {
if (!isset($prev_idx[$k])) {
if (in_array($f['level'], ['warn', 'bad'], true)) $new[] = $f;
} elseif ($prev_idx[$k]['level'] !== $f['level'] || $prev_idx[$k]['value'] !== $f['value']) {
$changed[] = ['was' => $prev_idx[$k], 'now' => $f];
}
}
foreach ($prev_idx as $k => $f) {
if (!isset($current_idx[$k]) && in_array($f['level'], ['warn', 'bad'], true)) {
$resolved[] = $f;
}
}
if (!$new && !$resolved && !$changed) return;
echo 'Δ vs. previous session (finished ' . esc_html(date('Y-m-d', (int) $prev->finished_at())) . ')';
if ($new) echo ' · ' . count($new) . ' new issue(s) ';
if ($resolved) echo ' · ' . count($resolved) . ' resolved ';
if ($changed) echo ' · ' . count($changed) . ' changed ';
echo '
';
}
function att_hc_render_step_card(ATT_HC_Session $session, ATT_HC_Step $step): void {
$state = $session->step_state($step->id());
$status = $state['status'];
$notes = $state['notes'];
?>
blurb()): ?>
sub_items()): ?>
watch_outs()): ?>
escalation())): ?>
render_extra($session->data());
?>
is_finished()) wp_die('No active session.');
if (!ATT_HC_Steps::instance()->get($step_id)) wp_die('Unknown step.');
$status = isset($_POST['status']) ? sanitize_key((string) $_POST['status']) : ATT_HC_Session::STATUS_NOT_STARTED;
$notes = isset($_POST['notes']) ? wp_unslash((string) $_POST['notes']) : '';
$session->update_step($step_id, $status, $notes);
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck#step-' . rawurlencode($step_id)));
exit;
}
function att_hc_handle_finish(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
check_admin_referer('att_hc_finish');
$session = ATT_HC_Session::current();
if (!$session) wp_die('No active session.');
$session->finish();
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck'));
exit;
}
function att_hc_handle_discard(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
check_admin_referer('att_hc_discard');
ATT_HC_Session::discard();
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck'));
exit;
}
function att_hc_render_autocheck(ATT_HC_Session $session, ATT_HC_Step $step): void {
if (!$step->has_autocheck()) return;
$result = $session->get_autocheck($step->id());
?>
Automated checks
id()); ?>
No automated checks have been run for this step yet.
'✓', 'warn' => '⚠', 'bad' => '✗', 'info' => '·'][$finding['level']] ?? '·';
?>
Checked ago ()
Site Healthcheck — Settings
← Back to healthcheck
Recovery plugin source (Gitea)
One-click install pulls site-recovery from a private Gitea repo. The token needs read access to the repo only — a deploy / read-only PAT is safer than a personal token.
Each field can be set via a constant in wp-config.php (then it takes precedence and the field below is locked).
Save settings
Install now
Install from gitea (latest)
✓ Site Recovery is already installed at .
(string) wp_unslash($_POST['host'] ?? ''),
'owner' => (string) wp_unslash($_POST['owner'] ?? ''),
'repo' => (string) wp_unslash($_POST['repo'] ?? ''),
'token' => (string) wp_unslash($_POST['token'] ?? ''),
]);
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck-settings&att_hc_saved=1'));
exit;
}
function att_hc_handle_recovery_install(): void {
if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) wp_die('Forbidden');
check_admin_referer('att_hc_recovery_install');
@set_time_limit(120);
$result = ATT_HC_Recovery_Installer::install_and_activate();
if (is_wp_error($result)) {
wp_die('Install failed: ' . esc_html($result->get_error_message()) . ' Back to settings
');
}
set_transient('att_hc_install_message', sprintf('Site Recovery installed from %s "%s" and activated.', $result['ref']['type'], $result['ref']['ref']), 60);
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck'));
exit;
}
function att_hc_handle_step_action(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
$step_id = isset($_POST['step']) ? sanitize_key((string) $_POST['step']) : '';
$action_name = isset($_POST['step_action']) ? sanitize_key((string) $_POST['step_action']) : '';
check_admin_referer('att_hc_step_action_' . $step_id . '_' . $action_name);
$session = ATT_HC_Session::current();
if (!$session || $session->is_finished()) wp_die('No active session.');
$step = ATT_HC_Steps::instance()->get($step_id);
if (!$step) wp_die('Unknown step.');
// Pass POST through unslashed so handlers see the raw user input.
$input = wp_unslash($_POST);
@set_time_limit(60);
$finding = $step->handle_action($action_name, is_array($input) ? $input : []);
if (is_array($finding)) {
// Append (or replace by id) onto this step's stored findings.
$existing = $session->get_autocheck($step_id);
$findings = $existing['findings'] ?? [];
$replaced = false;
foreach ($findings as $i => $f) {
if (($f['id'] ?? '') === ($finding['id'] ?? '')) {
$findings[$i] = $finding;
$replaced = true;
break;
}
}
if (!$replaced) $findings[] = $finding;
$session->set_autocheck($step_id, $findings);
}
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck#step-' . rawurlencode($step_id)));
exit;
}
function att_hc_handle_refresh_checks(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
$step_id = isset($_POST['step']) ? sanitize_key((string) $_POST['step']) : '';
check_admin_referer('att_hc_refresh_checks_' . $step_id);
$session = ATT_HC_Session::current();
if (!$session || $session->is_finished()) wp_die('No active session.');
$step = ATT_HC_Steps::instance()->get($step_id);
if (!$step) wp_die('Unknown step.');
@set_time_limit(60);
$findings = $step->autocheck($session->data());
$session->set_autocheck($step_id, $findings);
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck#step-' . rawurlencode($step_id)));
exit;
}
function att_hc_handle_download_report(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
check_admin_referer('att_hc_download_report');
$session = ATT_HC_Session::current();
if (!$session) wp_die('No session.');
att_hc_stream_report($session, 'md');
}
function att_hc_handle_download_html(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
check_admin_referer('att_hc_download_html');
$session = ATT_HC_Session::current();
if (!$session) wp_die('No session.');
att_hc_stream_report($session, 'html');
}
function att_hc_stream_report(ATT_HC_Session $session, string $format): void {
$host = parse_url(get_site_url(), PHP_URL_HOST) ?: 'site';
$host = preg_replace('/[^a-z0-9.-]/i', '', (string) $host);
$stamp = date('Ymd', $session->started_at() ?: time());
nocache_headers();
if ($format === 'html') {
header('Content-Type: text/html; charset=UTF-8');
header('Content-Disposition: attachment; filename="att-hc-report-' . $host . '-' . $stamp . '.html"');
echo att_hc_build_html_report($session);
} else {
header('Content-Type: text/markdown; charset=UTF-8');
header('Content-Disposition: attachment; filename="att-hc-report-' . $host . '-' . $stamp . '.md"');
echo att_hc_build_markdown_report($session);
}
exit;
}
function att_hc_handle_email_report(): void {
if (!current_user_can('manage_options')) wp_die('Forbidden');
check_admin_referer('att_hc_email_report');
$session = ATT_HC_Session::current();
if (!$session) wp_die('No session.');
$to = isset($_POST['to']) ? sanitize_email((string) wp_unslash($_POST['to'])) : '';
if (!is_email($to)) wp_die('Bad email address.');
$host = parse_url(get_site_url(), PHP_URL_HOST) ?: 'site';
$subject = 'Site Healthcheck — ' . $host . ' — ' . date('Y-m-d', $session->started_at() ?: time());
$html = att_hc_build_html_report($session);
$ok = wp_mail($to, $subject, $html, ['Content-Type: text/html; charset=UTF-8']);
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck&att_hc_mail=' . ($ok ? '1' : '0')));
exit;
}