Infrastructure:
- WPH_Step::autocheck($session_state) returns an array of findings
shaped {id, level: ok/warn/bad/info, label, value, detail}.
- WPH_Session stores results keyed by step id (persisted in the option-
backed session).
- WPH_Step::has_autocheck() reflection check so the UI only renders the
panel for steps that implement automation.
- 'Run checks' / 'Refresh' button per step, admin-post handler runs
autocheck() and stashes the result on the session.
- Findings rendered as a coloured table on the step card; included
verbatim in the Markdown report with status icons.
Step automations implemented:
- Step 1 (Backup): detection of 11 known backup plugins by slug;
active/inactive state; UpdraftPlus last-backup timestamp.
- Step 2 (Environment): PHP version + EOL, WP version vs latest, disk
usage, wp-config flags, file perms on wp-config/wp-content/uploads,
error-log sizes.
- Step 4 (Plugins): WP.org API enrichment with 24h transient cache —
last_updated, active_installs, abandonment flag, removed-from-repo
flag, update-available count. Summary line at the top.
- Step 8 (Security): SSL cert expiry via stream_socket_client +
openssl_x509_parse, administrator audit, xmlrpc reachability, login
URL hardening detection.
- Step 9 (Database): spam comments, post revisions, autoload size (WP
6.6+ value handling), top 3 largest tables.
- Step 11 (Small fixes): deactivated-but-installed plugin list,
homepage alt-text scan.
Smoke-tested on testsite — all six steps return findings with
correctly-classified levels. Report regenerated with automated findings
section.
98 lines
4.2 KiB
PHP
98 lines
4.2 KiB
PHP
<?php
|
|
if (!defined('ABSPATH')) exit;
|
|
|
|
return new class extends WPH_Step {
|
|
public function id(): string { return 'backup'; }
|
|
public function title(): string { return 'Step 1 — Take a Full Backup'; }
|
|
public function blurb(): string {
|
|
return 'Before any work takes place, take a complete backup manually. Do not rely on the most recent automated backup.';
|
|
}
|
|
public function sub_items(): array {
|
|
return [
|
|
'Back up both the database and all files (wp-content, wp-config.php, .htaccess)',
|
|
'Confirm the backup has completed and is accessible/downloadable',
|
|
'Note the backup location and timestamp in the client record',
|
|
];
|
|
}
|
|
public function escalation(): ?string {
|
|
return 'If the backup fails or cannot be confirmed, stop. Do not proceed until you have a verified backup.';
|
|
}
|
|
|
|
public function autocheck(array $session_state): array {
|
|
if (!function_exists('get_plugins')) require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
|
$plugins = get_plugins();
|
|
|
|
// Map known backup plugins: slug → friendly label.
|
|
$known = [
|
|
'updraftplus/updraftplus.php' => 'UpdraftPlus',
|
|
'backwpup/backwpup.php' => 'BackWPup',
|
|
'backup-backup/backup-backup.php' => 'Backup Migration',
|
|
'duplicator/duplicator.php' => 'Duplicator',
|
|
'wpvivid-backuprestore/wpvivid-backuprestore.php' => 'WPvivid',
|
|
'all-in-one-wp-migration/all-in-one-wp-migration.php' => 'All-in-One WP Migration',
|
|
'blogvault-real-time-backup/blogvault.php' => 'BlogVault',
|
|
'jetpack/jetpack.php' => 'Jetpack (VaultPress)',
|
|
'solid-backups/backupbuddy.php' => 'Solid Backups',
|
|
'backupbuddy/backupbuddy.php' => 'BackupBuddy',
|
|
'wp-time-capsule/wp-time-capsule.php' => 'WP Time Capsule',
|
|
];
|
|
|
|
$found = [];
|
|
foreach ($known as $file => $label) {
|
|
if (isset($plugins[$file])) {
|
|
$found[$file] = [
|
|
'label' => $label,
|
|
'active' => is_plugin_active($file),
|
|
];
|
|
}
|
|
}
|
|
|
|
$f = [];
|
|
if (empty($found)) {
|
|
$f[] = $this->finding(
|
|
'no_backup_plugin', 'bad',
|
|
'No recognised backup plugin installed', '',
|
|
'None of the common backup plugins were detected. Confirm host-side backups instead, or install one.'
|
|
);
|
|
return $f;
|
|
}
|
|
|
|
$any_active = false;
|
|
foreach ($found as $file => $meta) {
|
|
if ($meta['active']) $any_active = true;
|
|
$f[] = $this->finding(
|
|
'plugin_' . sanitize_key($file),
|
|
$meta['active'] ? 'ok' : 'warn',
|
|
$meta['label'],
|
|
$meta['active'] ? 'active' : 'installed but inactive',
|
|
''
|
|
);
|
|
}
|
|
if (!$any_active) {
|
|
$f[] = $this->finding(
|
|
'no_active', 'bad',
|
|
'No backup plugin is active', '',
|
|
'A backup plugin is installed but not active — activate before proceeding.'
|
|
);
|
|
}
|
|
|
|
// UpdraftPlus — surface last backup timestamp if available.
|
|
if (isset($found['updraftplus/updraftplus.php'])) {
|
|
$last = get_option('updraft_last_backup');
|
|
if (is_array($last) && !empty($last['backup_time'])) {
|
|
$age_days = floor((time() - (int) $last['backup_time']) / DAY_IN_SECONDS);
|
|
$level = $age_days > 7 ? 'warn' : 'ok';
|
|
$f[] = $this->finding(
|
|
'updraft_last',
|
|
$level,
|
|
'UpdraftPlus — last backup',
|
|
date('Y-m-d H:i', (int) $last['backup_time']),
|
|
$age_days . ' day(s) ago' . (!empty($last['backup_nonce']) ? '. Nonce ' . substr((string) $last['backup_nonce'], 0, 8) : '')
|
|
);
|
|
}
|
|
}
|
|
|
|
return $f;
|
|
}
|
|
};
|