Files
wp-healthcheck/includes/report.php
Steve Hanlon 6dd050ea1d Rename to ATT Site Healthcheck (private prefix)
Wholesale rename to avoid clashes with generic 'site healthcheck'
plugin names on a target site:

  - Plugin Name:        'Site Healthcheck' → 'ATT Site Healthcheck'
  - Main file:          site-healthcheck.php → att-site-healthcheck.php
  - Plugin folder:      site-healthcheck → att-site-healthcheck
  - Admin menu slug:    site-healthcheck → att-site-healthcheck
  - Settings slug:      site-healthcheck-settings → att-site-healthcheck-settings
  - PHP class prefix:   WPH_ → ATT_HC_
  - Function prefix:    wph_ → att_hc_
  - Option / transient: wph_* → att_hc_*
  - Action/filter:      wph_* → att_hc_*
  - CSS class prefix:   wph- → att-hc-
  - Constants:          WPH_GITEA_* → ATT_HC_GITEA_*
  - Class file names:   class-wph-*.php → class-att-hc-*.php
  - Dev folder:         ~/dev/wp-healthcheck → ~/dev/att-site-healthcheck

Existing in-progress sessions on installs that had the old wph_session
option will not migrate — they were intended for dev use only and the
user has confirmed this is OK for the rename window.

Smoke-tested on testsite: classes load, 14 steps discovered, save/load
round-trip works, admin page renders with new att-hc- CSS classes.

Recovery plugin detection unchanged — that lives in wp-site-recovery
and continues to be detected by Name + Author header.
2026-06-12 11:11:42 +01:00

215 lines
9.2 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if (!defined('ABSPATH')) exit;
/**
* Build a Markdown report for a healthcheck session.
* Per bead hc-5ix.7: pure Markdown, downloadable. No DB-side report storage
* (plugin may be uninstalled at the end of the engagement).
*/
function att_hc_build_markdown_report(ATT_HC_Session $session): string {
$tech = get_userdata($session->technician_id());
$tech_name = $tech ? $tech->display_name : '#' . $session->technician_id();
$lines = [];
$lines[] = '# Site Healthcheck — ' . $session->site_url();
$lines[] = '';
$lines[] = '- **Started:** ' . date('Y-m-d H:i', $session->started_at());
if ($session->is_finished()) {
$lines[] = '- **Finished:** ' . date('Y-m-d H:i', (int) $session->finished_at());
$minutes = max(1, (int) round(((int) $session->finished_at() - $session->started_at()) / 60));
$lines[] = '- **Duration:** ~' . $minutes . ' minute(s)';
}
$lines[] = '- **Technician:** ' . $tech_name;
$lines[] = '- **Site:** ' . $session->site_url();
$lines[] = '- **WordPress:** ' . $session->wp_version();
$lines[] = '- **PHP:** ' . $session->php_version();
$lines[] = '- **Session ID:** ' . $session->id();
$lines[] = '';
// Summary table
$lines[] = '## Summary';
$lines[] = '';
$lines[] = '| Step | Status |';
$lines[] = '|---|---|';
foreach (ATT_HC_Steps::instance()->all() as $step) {
$state = $session->step_state($step->id());
$lines[] = '| ' . $step->title() . ' | ' . att_hc_status_label($state['status']) . ' |';
}
$lines[] = '';
// Per-step detail
$lines[] = '## Detail';
$lines[] = '';
foreach (ATT_HC_Steps::instance()->all() as $step) {
$state = $session->step_state($step->id());
$lines[] = '### ' . $step->title();
$lines[] = '';
$lines[] = '_Status: ' . att_hc_status_label($state['status']) . '_';
if ($state['updated_at']) {
$lines[] = '_Saved: ' . date('Y-m-d H:i', $state['updated_at']) . '_';
}
if ($blurb = $step->blurb()) {
$lines[] = '';
$lines[] = $blurb;
}
if ($items = $step->sub_items()) {
$lines[] = '';
foreach ($items as $item) {
$lines[] = '- [ ] ' . $item;
}
}
if (!empty($state['notes'])) {
$lines[] = '';
$lines[] = '**Notes:**';
$lines[] = '';
foreach (preg_split('/\R/', (string) $state['notes']) as $nl) {
$lines[] = '> ' . $nl;
}
}
$auto = $session->get_autocheck($step->id());
if ($auto && !empty($auto['findings'])) {
$lines[] = '';
$lines[] = '**Automated checks** (run ' . date('Y-m-d H:i', (int) ($auto['checked_at'] ?? 0)) . '):';
$lines[] = '';
foreach ($auto['findings'] as $finding) {
$icon = ['ok' => '✅', 'warn' => '⚠️', 'bad' => '❌', 'info' => ''][$finding['level']] ?? '·';
$bits = [$icon, '**' . $finding['label'] . '**'];
if (!empty($finding['value'])) $bits[] = $finding['value'];
if (!empty($finding['detail'])) $bits[] = '— ' . $finding['detail'];
$lines[] = '- ' . implode(' ', $bits);
}
}
if ($state['status'] === ATT_HC_Session::STATUS_BLOCKED && ($esc = $step->escalation())) {
$lines[] = '';
$lines[] = '> ⚠ **Escalation:** ' . $esc;
}
$lines[] = '';
}
$lines[] = '---';
$lines[] = '_Generated by Site Healthcheck plugin v' . ATT_HC_VERSION . '_';
return implode("\n", $lines) . "\n";
}
function att_hc_build_html_report(ATT_HC_Session $session): string {
$tech = get_userdata($session->technician_id());
$tech_name = $tech ? $tech->display_name : '#' . $session->technician_id();
ob_start();
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Site Healthcheck — <?php echo esc_html($session->site_url()); ?></title>
<style>
body { font: 14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #1d1d1f; background: #f7f7f8; margin: 0; padding: 2rem; }
.wrap { max-width: 820px; margin: 0 auto; background: #fff; border-radius: 10px; padding: 2rem 2.4rem; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
h1 { margin: 0 0 .25rem; font-size: 1.6rem; }
h2 { margin: 1.5rem 0 .5rem; font-size: 1.15rem; border-bottom: 1px solid #e5e5ea; padding-bottom: .25rem; }
h3 { margin: 1.2rem 0 .35rem; font-size: 1rem; }
table { width: 100%; border-collapse: collapse; font-size: .92em; margin: .4rem 0; }
th, td { text-align: left; padding: .35rem .55rem; border-bottom: 1px solid #f0f0f1; vertical-align: top; }
th { background: #fafafb; font-weight: 600; }
.meta { color: #646970; }
.pill { display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
.pill.done { background: #def7e3; color: #155724; }
.pill.skipped { background: #fff3cd; color: #856404; }
.pill.blocked { background: #fbeae8; color: #721c24; }
.pill.n_a { background: #e2e3e5; color: #41464b; }
.pill.not_started { background: #f0f0f1; color: #646970; }
blockquote { margin: .5rem 0; padding: .35rem .9rem; border-left: 3px solid #c3c4c7; background: #fafafa; color: #444; }
ul.findings { list-style: none; padding-left: 0; }
ul.findings li { padding: .15rem 0; }
.lvl-ok::before { content: '✓ '; color: #1a8917; font-weight: 700; }
.lvl-warn::before { content: '⚠ '; color: #b07a00; font-weight: 700; }
.lvl-bad::before { content: '✗ '; color: #c0392b; font-weight: 700; }
.lvl-info::before { content: '· '; color: #646970; font-weight: 700; }
.detail { color: #646970; font-size: .92em; }
footer { margin-top: 2rem; color: #646970; font-size: .85em; text-align: center; }
</style>
</head>
<body>
<div class="wrap">
<h1>Site Healthcheck</h1>
<p class="meta"><?php echo esc_html($session->site_url()); ?></p>
<table>
<tr><th>Started</th><td><?php echo esc_html(date('Y-m-d H:i', $session->started_at())); ?></td></tr>
<?php if ($session->is_finished()): $mins = max(1, (int) round(((int) $session->finished_at() - $session->started_at()) / 60)); ?>
<tr><th>Finished</th><td><?php echo esc_html(date('Y-m-d H:i', (int) $session->finished_at())); ?> (≈<?php echo $mins; ?> min)</td></tr>
<?php endif; ?>
<tr><th>Technician</th><td><?php echo esc_html($tech_name); ?></td></tr>
<tr><th>WordPress</th><td><?php echo esc_html($session->wp_version()); ?></td></tr>
<tr><th>PHP</th><td><?php echo esc_html($session->php_version()); ?></td></tr>
</table>
<h2>Summary</h2>
<table>
<thead><tr><th>Step</th><th>Status</th></tr></thead>
<tbody>
<?php foreach (ATT_HC_Steps::instance()->all() as $step):
$state = $session->step_state($step->id());
?>
<tr>
<td><?php echo esc_html($step->title()); ?></td>
<td><span class="pill <?php echo esc_attr($state['status']); ?>"><?php echo esc_html(str_replace('_', ' ', $state['status'])); ?></span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<h2>Detail</h2>
<?php foreach (ATT_HC_Steps::instance()->all() as $step):
$state = $session->step_state($step->id());
$auto = $session->get_autocheck($step->id());
?>
<h3><?php echo esc_html($step->title()); ?></h3>
<p><span class="pill <?php echo esc_attr($state['status']); ?>"><?php echo esc_html(str_replace('_', ' ', $state['status'])); ?></span></p>
<?php if ($blurb = $step->blurb()): ?>
<p><?php echo esc_html($blurb); ?></p>
<?php endif; ?>
<?php if ($items = $step->sub_items()): ?>
<ul>
<?php foreach ($items as $i): ?>
<li><?php echo esc_html($i); ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if (!empty($state['notes'])): ?>
<blockquote><?php echo nl2br(esc_html($state['notes'])); ?></blockquote>
<?php endif; ?>
<?php if ($auto && !empty($auto['findings'])): ?>
<ul class="findings">
<?php foreach ($auto['findings'] as $f): ?>
<li class="lvl-<?php echo esc_attr($f['level']); ?>">
<strong><?php echo esc_html($f['label']); ?></strong>
<?php if ($f['value'] !== ''): ?>— <?php echo esc_html($f['value']); ?><?php endif; ?>
<?php if ($f['detail'] !== ''): ?><span class="detail"> — <?php echo esc_html($f['detail']); ?></span><?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php endforeach; ?>
<footer>Generated by Site Healthcheck plugin v<?php echo esc_html(ATT_HC_VERSION); ?> · <?php echo esc_html(date('Y-m-d H:i')); ?></footer>
</div>
</body>
</html>
<?php
return (string) ob_get_clean();
}
function att_hc_status_label(string $status): string {
switch ($status) {
case ATT_HC_Session::STATUS_DONE: return '✅ Done';
case ATT_HC_Session::STATUS_SKIPPED: return '⏭ Skipped';
case ATT_HC_Session::STATUS_BLOCKED: return '🛑 Blocked';
case ATT_HC_Session::STATUS_NA: return '— N/A';
case ATT_HC_Session::STATUS_NOT_STARTED:
default: return '◻ Not started';
}
}