Compare commits
2
Commits
85b4edc4f2
...
c74a083eb9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74a083eb9 | ||
|
|
1d674808bb |
@@ -16,6 +16,7 @@
|
|||||||
{"_type":"issue","id":"hc-5ix.4","title":"Session data model: option-backed in-progress healthcheck record","description":"One in-progress session per site at a time. Stored in a custom option (or a CPT — pick during implementation). Shape: id, started_at, finished_at, technician_id, site_url_snapshot, per_step_state { status: not_started|in_progress|done|skipped|blocked|n_a, notes, completed_at }. Designed so phase-3 automation can attach structured findings later.","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:11Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:27Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:27Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.4","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:11Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"hc-5ix.4","title":"Session data model: option-backed in-progress healthcheck record","description":"One in-progress session per site at a time. Stored in a custom option (or a CPT — pick during implementation). Shape: id, started_at, finished_at, technician_id, site_url_snapshot, per_step_state { status: not_started|in_progress|done|skipped|blocked|n_a, notes, completed_at }. Designed so phase-3 automation can attach structured findings later.","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:11Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:27Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:27Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.4","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:11Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_type":"issue","id":"hc-5ix.2","title":"Plugin skeleton: header, activation hook, deactivation hook, admin menu (Tools → Site Healthcheck), capability gate","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:10Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:26Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:26Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.2","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:09Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"hc-5ix.2","title":"Plugin skeleton: header, activation hook, deactivation hook, admin menu (Tools → Site Healthcheck), capability gate","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:10Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:26Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:26Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.2","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:09Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_type":"issue","id":"hc-5ix.1","title":"Bootstrap: trigger install of wp-site-recovery plugin as step 0","description":"Healthcheck plugin should check on activation if site-recovery is installed; if not, fetch and install from a known URL/ZIP and activate it. Block stepper from starting until recovery is in place.","notes":"Detection half done (status panel + manual link). Auto-install of recovery plugin from a private URL is the wp-site-recovery side of hc-5ix.27 — closing this as 'detection complete'.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T11:26:40Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T15:03:02Z","closed_at":"2026-06-11T15:03:02Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.1","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T12:26:39Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"hc-5ix.1","title":"Bootstrap: trigger install of wp-site-recovery plugin as step 0","description":"Healthcheck plugin should check on activation if site-recovery is installed; if not, fetch and install from a known URL/ZIP and activate it. Block stepper from starting until recovery is in place.","notes":"Detection half done (status panel + manual link). Auto-install of recovery plugin from a private URL is the wp-site-recovery side of hc-5ix.27 — closing this as 'detection complete'.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T11:26:40Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T15:03:02Z","closed_at":"2026-06-11T15:03:02Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.1","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T12:26:39Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
{"_type":"issue","id":"hc-7vo","title":"Dashboard: hide archived sites from the All-sites list","description":"Follow-up to hc-b8r: archived healthchecks appeared in both the main All-sites list and the Archived section. Exclude archived sessions from allSitesSummary so a site with only archived sessions drops off the main list and shows only under Archived.","status":"open","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-08-24T11:14:10Z","created_by":"Steve Hanlon","updated_at":"2026-08-24T11:14:10Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_type":"issue","id":"hc-b8r","title":"Dashboard: archive healthchecks + Archived section on homepage","description":"Some audits are no longer needed. Add an archive toggle on the per-site dashboard page (/dashboard?site=X); archived healthchecks are also listed on the homepage under an 'Archived' heading at the bottom. Server/dashboard only — not exposed to the plugin. No hiding of archived items elsewhere for now.","status":"closed","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-08-24T10:57:26Z","created_by":"Steve Hanlon","updated_at":"2026-08-24T11:02:58Z","closed_at":"2026-08-24T11:02:58Z","close_reason":"Implemented: archive toggle on the per-site dashboard page + Archived section on the homepage. Migration 0003, Store::setArchived/archivedHealthchecks, CSRF-guarded POST /dashboard/healthchecks/{id}/archive. 7 new tests.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"hc-b8r","title":"Dashboard: archive healthchecks + Archived section on homepage","description":"Some audits are no longer needed. Add an archive toggle on the per-site dashboard page (/dashboard?site=X); archived healthchecks are also listed on the homepage under an 'Archived' heading at the bottom. Server/dashboard only — not exposed to the plugin. No hiding of archived items elsewhere for now.","status":"closed","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-08-24T10:57:26Z","created_by":"Steve Hanlon","updated_at":"2026-08-24T11:02:58Z","closed_at":"2026-08-24T11:02:58Z","close_reason":"Implemented: archive toggle on the per-site dashboard page + Archived section on the homepage. Migration 0003, Store::setArchived/archivedHealthchecks, CSRF-guarded POST /dashboard/healthchecks/{id}/archive. 7 new tests.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_type":"issue","id":"hc-66c","title":"AJAX step saving on the healthcheck reporting page","description":"The step cards currently save via a full-page POST, which reloads the page and breaks the technician's flow. Switch to AJAX: notes save on blur, status saves on change, with inline save feedback and no reload.","status":"closed","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-07-27T07:21:00Z","created_by":"Steve Hanlon","updated_at":"2026-07-27T07:27:23Z","closed_at":"2026-07-27T07:27:23Z","close_reason":"Implemented in 0.1.5: AJAX auto-save on the step cards — status saves on change, notes on blur, inline feedback, no reload. Progressive enhancement; no-JS form POST retained as fallback.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"hc-66c","title":"AJAX step saving on the healthcheck reporting page","description":"The step cards currently save via a full-page POST, which reloads the page and breaks the technician's flow. Switch to AJAX: notes save on blur, status saves on change, with inline save feedback and no reload.","status":"closed","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-07-27T07:21:00Z","created_by":"Steve Hanlon","updated_at":"2026-07-27T07:27:23Z","closed_at":"2026-07-27T07:27:23Z","close_reason":"Implemented in 0.1.5: AJAX auto-save on the step cards — status saves on change, notes on blur, inline feedback, no reload. Progressive enhancement; no-JS form POST retained as fallback.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_type":"issue","id":"hc-fbd","title":"Next healthcheck due date per site","description":"Technician can set 'next healthcheck due' while working a session; stored on the server against the healthcheck row, surfaced in the dashboard site list and session views so it is visible when planning work.","acceptance_criteria":"Plugin UI sets/clears a date on the active session; server persists + validates YYYY-MM-DD; dashboard shows next-due per site (derived from latest session) and per session; report includes it; tests pass.","status":"closed","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-07-23T10:50:25Z","created_by":"Steve Hanlon","updated_at":"2026-07-23T11:04:08Z","closed_at":"2026-07-23T11:04:08Z","close_reason":"Implemented and released in 0.1.4: server column + validation + dashboard views, plugin date control, 42-test suite. Commits 6313857, 55990be.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"hc-fbd","title":"Next healthcheck due date per site","description":"Technician can set 'next healthcheck due' while working a session; stored on the server against the healthcheck row, surfaced in the dashboard site list and session views so it is visible when planning work.","acceptance_criteria":"Plugin UI sets/clears a date on the active session; server persists + validates YYYY-MM-DD; dashboard shows next-due per site (derived from latest session) and per session; report includes it; tests pass.","status":"closed","priority":2,"issue_type":"feature","owner":"steve@hanlon.co.uk","created_at":"2026-07-23T10:50:25Z","created_by":"Steve Hanlon","updated_at":"2026-07-23T11:04:08Z","closed_at":"2026-07-23T11:04:08Z","close_reason":"Implemented and released in 0.1.4: server column + validation + dashboard views, plugin date control, 42-test suite. Commits 6313857, 55990be.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* Plugin Name: ATT Site Healthcheck
|
* Plugin Name: ATT Site Healthcheck
|
||||||
* Description: Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.
|
* Description: Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.
|
||||||
* Version: 0.1.7
|
* Version: 0.1.8
|
||||||
* Author: Steve Hanlon
|
* Author: Steve Hanlon
|
||||||
* License: Proprietary
|
* License: Proprietary
|
||||||
* License URI: https://git.h12e.com/steve/wp-healthcheck/raw/branch/main/LICENSE
|
* License URI: https://git.h12e.com/steve/wp-healthcheck/raw/branch/main/LICENSE
|
||||||
@@ -15,7 +15,7 @@ if (!defined('ABSPATH')) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
define('ATT_HC_VERSION', '0.1.7');
|
define('ATT_HC_VERSION', '0.1.8');
|
||||||
define('ATT_HC_PLUGIN_FILE', __FILE__);
|
define('ATT_HC_PLUGIN_FILE', __FILE__);
|
||||||
define('ATT_HC_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
define('ATT_HC_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
define('ATT_HC_PLUGIN_URL', plugin_dir_url(__FILE__));
|
define('ATT_HC_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||||
|
|||||||
+49
-1
@@ -3,6 +3,7 @@ if (!defined('ABSPATH')) exit;
|
|||||||
|
|
||||||
add_action('admin_menu', 'att_hc_register_menu');
|
add_action('admin_menu', 'att_hc_register_menu');
|
||||||
add_action('admin_post_att_hc_start', 'att_hc_handle_start');
|
add_action('admin_post_att_hc_start', 'att_hc_handle_start');
|
||||||
|
add_action('admin_post_att_hc_start_next', 'att_hc_handle_start_next');
|
||||||
add_action('admin_post_att_hc_resume', 'att_hc_handle_resume');
|
add_action('admin_post_att_hc_resume', 'att_hc_handle_resume');
|
||||||
add_action('wp_ajax_att_hc_step_history', 'att_hc_handle_step_history_ajax');
|
add_action('wp_ajax_att_hc_step_history', 'att_hc_handle_step_history_ajax');
|
||||||
add_action('wp_ajax_att_hc_save_step_ajax', 'att_hc_handle_save_step_ajax');
|
add_action('wp_ajax_att_hc_save_step_ajax', 'att_hc_handle_save_step_ajax');
|
||||||
@@ -55,6 +56,7 @@ function att_hc_inline_css(): string {
|
|||||||
.att-hc-status-skipped { background:#fff3cd; color:#856404; }
|
.att-hc-status-skipped { background:#fff3cd; color:#856404; }
|
||||||
.att-hc-status-blocked { background:#fbeae8; color:#721c24; }
|
.att-hc-status-blocked { background:#fbeae8; color:#721c24; }
|
||||||
.att-hc-status-n_a { background:#e2e3e5; color:#41464b; }
|
.att-hc-status-n_a { background:#e2e3e5; color:#41464b; }
|
||||||
|
.att-hc-start-next { margin:.5rem 0 1rem; display:flex; flex-wrap:wrap; align-items:center; gap:.6rem; }
|
||||||
.att-hc-next-due { margin:.5rem 0 .75rem; padding:.5rem .75rem; background:#f6f7f7; border:1px solid #dcdcde; border-radius:4px; }
|
.att-hc-next-due { margin:.5rem 0 .75rem; padding:.5rem .75rem; background:#f6f7f7; border:1px solid #dcdcde; border-radius:4px; }
|
||||||
.att-hc-next-due form { display:flex; flex-wrap:wrap; align-items:center; gap:.5rem; }
|
.att-hc-next-due form { display:flex; flex-wrap:wrap; align-items:center; gap:.5rem; }
|
||||||
.att-hc-next-due .description { flex-basis:100%; }
|
.att-hc-next-due .description { flex-basis:100%; }
|
||||||
@@ -714,11 +716,20 @@ function att_hc_render_finished_panel(ATT_HC_Session $session): void {
|
|||||||
<h2>Healthcheck finished</h2>
|
<h2>Healthcheck finished</h2>
|
||||||
<p>Started <?php echo esc_html(date('Y-m-d H:i', $session->started_at())); ?> · Finished <?php echo esc_html(date('Y-m-d H:i', (int) $session->finished_at())); ?></p>
|
<p>Started <?php echo esc_html(date('Y-m-d H:i', $session->started_at())); ?> · Finished <?php echo esc_html(date('Y-m-d H:i', (int) $session->finished_at())); ?></p>
|
||||||
<?php att_hc_render_next_due_form($session); ?>
|
<?php att_hc_render_next_due_form($session); ?>
|
||||||
|
<div class="att-hc-start-next">
|
||||||
|
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline">
|
||||||
|
<?php wp_nonce_field('att_hc_start_next'); ?>
|
||||||
|
<input type="hidden" name="action" value="att_hc_start_next">
|
||||||
|
<input type="hidden" name="site_key" value="<?php echo esc_attr($session->site_key()); ?>">
|
||||||
|
<button class="button button-primary">Start new healthcheck</button>
|
||||||
|
</form>
|
||||||
|
<span class="description">Begins a fresh session for <code><?php echo esc_html($session->site_key()); ?></code>. This finished report stays on the history server.</span>
|
||||||
|
</div>
|
||||||
<div class="att-hc-actions">
|
<div class="att-hc-actions">
|
||||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline">
|
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline">
|
||||||
<?php wp_nonce_field('att_hc_download_report'); ?>
|
<?php wp_nonce_field('att_hc_download_report'); ?>
|
||||||
<input type="hidden" name="action" value="att_hc_download_report">
|
<input type="hidden" name="action" value="att_hc_download_report">
|
||||||
<button class="button button-primary">Download Markdown</button>
|
<button class="button">Download Markdown</button>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline">
|
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline">
|
||||||
<?php wp_nonce_field('att_hc_download_html'); ?>
|
<?php wp_nonce_field('att_hc_download_html'); ?>
|
||||||
@@ -769,6 +780,43 @@ function att_hc_handle_start(): void {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a fresh healthcheck straight from the finished panel, without making the
|
||||||
|
* technician Discard first (Discard reads as destructive, though it only clears
|
||||||
|
* the local cache). Only acts on a finished session; the finished record stays
|
||||||
|
* on the history server.
|
||||||
|
*/
|
||||||
|
function att_hc_handle_start_next(): void {
|
||||||
|
if (!current_user_can('manage_options')) wp_die('Forbidden');
|
||||||
|
check_admin_referer('att_hc_start_next');
|
||||||
|
|
||||||
|
$current = ATT_HC_Session::current();
|
||||||
|
// Guard: only from a finished session. If an active one is somehow current,
|
||||||
|
// don't clobber it — just send the technician back to it.
|
||||||
|
if ($current && !$current->is_finished()) {
|
||||||
|
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the engagement's site_key so history links up and the just-finished
|
||||||
|
// session's handover notes seed the new "before" notes (see start()).
|
||||||
|
$site_key = isset($_POST['site_key']) && $_POST['site_key'] !== ''
|
||||||
|
? sanitize_text_field(wp_unslash((string) $_POST['site_key']))
|
||||||
|
: ($current ? $current->site_key() : null);
|
||||||
|
|
||||||
|
// Drop the finished session from the local cache so start() creates a new one.
|
||||||
|
// The finished record remains on the server.
|
||||||
|
ATT_HC_Session::discard();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ATT_HC_Session::start(get_current_user_id(), $site_key);
|
||||||
|
} catch (ATT_HC_Api_Exception $e) {
|
||||||
|
set_transient('att_hc_api_error', 'Could not start a new healthcheck: ' . $e->getMessage(), 60);
|
||||||
|
}
|
||||||
|
wp_safe_redirect(admin_url('tools.php?page=att-site-healthcheck'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
function att_hc_handle_save_next_due(): void {
|
function att_hc_handle_save_next_due(): void {
|
||||||
if (!current_user_can('manage_options')) wp_die('Forbidden');
|
if (!current_user_can('manage_options')) wp_die('Forbidden');
|
||||||
check_admin_referer('att_hc_save_next_due');
|
check_admin_referer('att_hc_save_next_due');
|
||||||
|
|||||||
@@ -28,5 +28,5 @@ return [
|
|||||||
'db_pass' => 'REPLACE_WITH_DB_PASSWORD',
|
'db_pass' => 'REPLACE_WITH_DB_PASSWORD',
|
||||||
|
|
||||||
// Server version, surfaced on GET /. Bump on deploy.
|
// Server version, surfaced on GET /. Bump on deploy.
|
||||||
'version' => '0.1.0',
|
'version' => '0.1.8',
|
||||||
];
|
];
|
||||||
|
|||||||
+15
-6
@@ -222,10 +222,17 @@ final class Store {
|
|||||||
/**
|
/**
|
||||||
* Summary of all sites for the dashboard: one row per site_key,
|
* Summary of all sites for the dashboard: one row per site_key,
|
||||||
* ordered by most-recently-active first.
|
* ordered by most-recently-active first.
|
||||||
* `next_due` is taken from the most recent session, not from the most recent
|
*
|
||||||
* session that happens to have one set. If the latest visit didn't schedule a
|
* Archived sessions are excluded from the aggregate, so a site whose sessions
|
||||||
* return, the site genuinely has nothing scheduled — carrying an older date
|
* are all archived drops off this list entirely (it still shows, per session,
|
||||||
* forward would show a visit as overdue immediately after it was completed.
|
* under the dashboard's "Archived" heading). Every column — counts, latest
|
||||||
|
* dates, next_due — reflects only the site's non-archived sessions.
|
||||||
|
*
|
||||||
|
* `next_due` is taken from the most recent (non-archived) session, not from
|
||||||
|
* the most recent one that happens to have one set. If the latest visit didn't
|
||||||
|
* schedule a return, the site genuinely has nothing scheduled — carrying an
|
||||||
|
* older date forward would show a visit as overdue immediately after it was
|
||||||
|
* completed.
|
||||||
*
|
*
|
||||||
* @return array<int, array{site_key:string, total:int, finished:int, last_started_at:int, last_finished_at:int|null, last_reporting_url:string, next_due:string|null}>
|
* @return array<int, array{site_key:string, total:int, finished:int, last_started_at:int, last_finished_at:int|null, last_reporting_url:string, next_due:string|null}>
|
||||||
*/
|
*/
|
||||||
@@ -237,16 +244,18 @@ final class Store {
|
|||||||
SUM(CASE WHEN finished_at IS NOT NULL THEN 1 ELSE 0 END) AS finished,
|
SUM(CASE WHEN finished_at IS NOT NULL THEN 1 ELSE 0 END) AS finished,
|
||||||
MAX(started_at) AS last_started_at
|
MAX(started_at) AS last_started_at
|
||||||
FROM healthchecks
|
FROM healthchecks
|
||||||
|
WHERE archived = 0
|
||||||
GROUP BY site_key
|
GROUP BY site_key
|
||||||
ORDER BY MAX(updated_at) DESC'
|
ORDER BY MAX(updated_at) DESC'
|
||||||
);
|
);
|
||||||
$rows = $stmt->fetchAll();
|
$rows = $stmt->fetchAll();
|
||||||
// Fetch the reporting_url and finished_at from the most recent session per site.
|
// Fetch the reporting_url and finished_at from the most recent NON-archived
|
||||||
|
// session per site (the site is only here because it has at least one).
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach ($rows as $r) {
|
foreach ($rows as $r) {
|
||||||
$detail = Db::pdo()->prepare(
|
$detail = Db::pdo()->prepare(
|
||||||
'SELECT reporting_url, finished_at, next_due FROM healthchecks
|
'SELECT reporting_url, finished_at, next_due FROM healthchecks
|
||||||
WHERE site_key = ? ORDER BY started_at DESC LIMIT 1'
|
WHERE site_key = ? AND archived = 0 ORDER BY started_at DESC LIMIT 1'
|
||||||
);
|
);
|
||||||
$detail->execute([$r['site_key']]);
|
$detail->execute([$r['site_key']]);
|
||||||
$last = $detail->fetch() ?: [];
|
$last = $detail->fetch() ?: [];
|
||||||
|
|||||||
+33
-3
@@ -125,6 +125,12 @@ function archived_section(string $html): string {
|
|||||||
return $pos === false ? '' : substr($html, $pos);
|
return $pos === false ? '' : substr($html, $pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The home page up to (not including) the Archived section — i.e. the All-sites list. */
|
||||||
|
function main_site_list(string $html): string {
|
||||||
|
$pos = strpos($html, '<h2 class="archived-heading"');
|
||||||
|
return $pos === false ? $html : substr($html, 0, $pos);
|
||||||
|
}
|
||||||
|
|
||||||
/** The <tr> from a dashboard table that mentions $needle — for precise assertions. */
|
/** The <tr> from a dashboard table that mentions $needle — for precise assertions. */
|
||||||
function row_containing(string $html, string $needle): string {
|
function row_containing(string $html, string $needle): string {
|
||||||
if (preg_match_all('#<tr>.*?</tr>#s', $html, $m)) {
|
if (preg_match_all('#<tr>.*?</tr>#s', $html, $m)) {
|
||||||
@@ -475,9 +481,10 @@ test('the per-site page marks an archived session and offers Unarchive', functio
|
|||||||
assert_contains('status-archived', $page, 'the row should carry the archived badge');
|
assert_contains('status-archived', $page, 'the row should carry the archived badge');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('an unarchived session still shows the normal listing (not hidden)', function () {
|
test('an archived session still shows on its own per-site page', function () {
|
||||||
// "No need to hide them" — an archived session stays in its site's session list.
|
// Hiding is only from the home page's All-sites list — a site's own page
|
||||||
$site = unique_site('archive-not-hidden');
|
// still lists every session (archived ones just carry the badge).
|
||||||
|
$site = unique_site('archive-persite-visible');
|
||||||
$hc = create_hc($site, ['next_due' => '2027-01-01']);
|
$hc = create_hc($site, ['next_due' => '2027-01-01']);
|
||||||
$token = dash_csrf('/dashboard?site=' . rawurlencode($site));
|
$token = dash_csrf('/dashboard?site=' . rawurlencode($site));
|
||||||
archive_hc($hc['id'], true, $token);
|
archive_hc($hc['id'], true, $token);
|
||||||
@@ -486,6 +493,29 @@ test('an unarchived session still shows the normal listing (not hidden)', functi
|
|||||||
assert_contains('2027-01-01', $page, 'the archived session is still in the per-site table');
|
assert_contains('2027-01-01', $page, 'the archived session is still in the per-site table');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a site with only archived sessions drops off the All sites list', function () {
|
||||||
|
$site = unique_site('archive-hidden-main');
|
||||||
|
$hc = create_hc($site);
|
||||||
|
$token = dash_csrf('/dashboard?site=' . rawurlencode($site));
|
||||||
|
archive_hc($hc['id'], true, $token);
|
||||||
|
|
||||||
|
$home = dash('/dashboard')['body'];
|
||||||
|
assert_not_contains($site, main_site_list($home), 'gone from the All sites list');
|
||||||
|
assert_contains($site, archived_section($home), 'but still under Archived');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a site keeps its All sites row while it still has a live session', function () {
|
||||||
|
$site = unique_site('archive-mixed');
|
||||||
|
create_hc($site); // stays active
|
||||||
|
$stale = create_hc($site); // gets archived
|
||||||
|
$token = dash_csrf('/dashboard?site=' . rawurlencode($site));
|
||||||
|
archive_hc($stale['id'], true, $token);
|
||||||
|
|
||||||
|
$home = dash('/dashboard')['body'];
|
||||||
|
assert_contains($site, main_site_list($home), 'still has a live session, so still listed');
|
||||||
|
assert_contains($site, archived_section($home), 'and its archived session shows under Archived');
|
||||||
|
});
|
||||||
|
|
||||||
test('archiving an unknown healthcheck reports not found', function () {
|
test('archiving an unknown healthcheck reports not found', function () {
|
||||||
$site = unique_site('archive-unknown');
|
$site = unique_site('archive-unknown');
|
||||||
create_hc($site); // gives us a page to read a CSRF token from
|
create_hc($site); // gives us a page to read a CSRF token from
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ATT Site Healthcheck",
|
"name": "ATT Site Healthcheck",
|
||||||
"slug": "att-site-healthcheck",
|
"slug": "att-site-healthcheck",
|
||||||
"version": "0.1.7",
|
"version": "0.1.8",
|
||||||
"author": "Steve Hanlon",
|
"author": "Steve Hanlon",
|
||||||
"requires": "6.0",
|
"requires": "6.0",
|
||||||
"tested": "6.6",
|
"tested": "6.6",
|
||||||
"requires_php": "7.4",
|
"requires_php": "7.4",
|
||||||
"last_updated": "2026-07-27 08:42:03",
|
"last_updated": "2026-08-24 12:13:39",
|
||||||
"download_url": "https://git.h12e.com/steve/wp-healthcheck/archive/main.zip",
|
"download_url": "https://git.h12e.com/steve/wp-healthcheck/archive/main.zip",
|
||||||
"sections": {
|
"sections": {
|
||||||
"description": "Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.",
|
"description": "Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.",
|
||||||
|
|||||||
Reference in New Issue
Block a user