Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import {
OverlaySection,
} from '../overlay';
import css from './style.module.css';
import {
getBlueprintUrl,
healthCheckRecoveryBlueprint,
} from '../../lib/health-check-recovery';

interface MenuOverlayProps {
onClose: () => void;
Expand All @@ -21,6 +25,7 @@ export function MenuOverlay({ onClose }: MenuOverlayProps) {

const [showDeleteButton, setShowDeleteButton] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [showRecoveryButton, setShowRecoveryButton] = useState(false);

async function handleStartOver() {
if (!activeSite || activeSite.metadata.storage === 'none') {
Expand Down Expand Up @@ -107,6 +112,29 @@ export function MenuOverlay({ onClose }: MenuOverlayProps) {
)}
</OverlaySection>
</div>

<OverlaySection title="Recovery">
<p>
If WordPress crashed,{' '}
<button
className={css.textButton}
onClick={() =>
setShowRecoveryButton(!showRecoveryButton)
}
>
you can troubleshoot
</button>
.
</p>
{showRecoveryButton && (
<a
href={getBlueprintUrl(healthCheckRecoveryBlueprint)}
className={css.primaryButton}
>
Install Health Check &amp; Troubleshoot
</a>
)}
</OverlaySection>
</OverlayBody>
</Overlay>
);
Expand Down
83 changes: 83 additions & 0 deletions packages/playground/personal-wp/src/lib/health-check-recovery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { Blueprint } from '@wp-playground/blueprints';

//
// The Health Check MU-plugin requires a database option 'health-check-disable-plugin-hash'
// that matches: cookieValue + md5(REMOTE_ADDR). We add an earlier MU-plugin (alphabetically)
// that uses pre_option filter to return the expected hash, bypassing the database check.
export const healthCheckRecoveryBlueprint: Blueprint = {
steps: [
{
step: 'installPlugin',
pluginData: {
resource: 'wordpress.org/plugins',
slug: 'health-check',
},
options: {
activate: false,
},
},
{
step: 'mkdir',
path: '/wordpress/wp-content/mu-plugins',
},
{
step: 'cp',
fromPath:
'/wordpress/wp-content/plugins/health-check/mu-plugin/health-check-troubleshooting-mode.php',
toPath: '/wordpress/wp-content/mu-plugins/health-check-troubleshooting-mode.php',
},
{
// Add an MU-plugin that loads before health-check (alphabetically: "0" < "h")
// to provide the expected hash via pre_option filter
step: 'writeFile',
path: '/wordpress/wp-content/mu-plugins/0-health-check-hash-bypass.php',
data: `<?php
// Bypass Health Check hash verification by setting both the GET param and option.
// Self-delete when user disables troubleshooting mode via Health Check UI.
if (isset($_GET['health-check-disable-troubleshooting'])) {
unlink(__FILE__);
} else {
$_GET['health-check-disable-plugin-hash'] = 'playground-recovery';
add_filter('pre_option_health-check-disable-plugin-hash', function() {
return 'playground-recovery';
});
// Don't try to switch to a default theme
add_filter('pre_option_health-check-default-theme', function() {
return 'no';
});
// Add admin notice with guidance
add_action('admin_notices', function() {
?>
<div class="notice notice-warning">
<p><strong>Troubleshooting Mode Active</strong></p>
<p>All plugins have been disabled. You can now activate them one by one to find the problematic one.</p>
<p>Once fixed, disable troubleshooting mode via <a href="<?php echo admin_url('site-health.php?tab=troubleshoot'); ?>">Site Health &rarr; Troubleshoot</a>.</p>
</div>
<?php
});
}
`,
},
{
step: 'login',
},
],
landingPage:
'/wp-admin/plugins.php?health-check-disable-plugin-hash=playground-recovery',
};

export function getBlueprintUrl(blueprint: Blueprint): string {
const url = new URL(window.location.href);
url.hash = '';
const jsonStr = JSON.stringify(blueprint);
const encoded = btoa(
encodeURIComponent(jsonStr).replace(/%([0-9A-F]{2})/g, (_, p1) =>
String.fromCharCode(parseInt(p1, 16))
)
);
url.searchParams.set(
'blueprint-url',
`data:application/json;base64,${encoded}`
);
return url.toString();
}
Loading