fix(my-jetpack): fix page loading for editors on unconnected sites - #52614
Conversation
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! Jetpack plugin: The Jetpack plugin has different release cadences depending on the platform:
If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Backup plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Boost plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Search plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Social plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Protect plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Videopress plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. Stats Data plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. |
xavier-lc
left a comment
There was a problem hiding this comment.
The diagnosis holds up: Jetpack_Admin_Page::add_actions() bails for non-admins on unconnected or offline sites, so $admin_page_hooks['jetpack'] is never set and core names the page admin_page_my-jetpack, while Admin_Menu::add_menu() always returns jetpack_page_<slug>. Registering both is the right shape — deriving the name here isn't possible, since add_my_jetpack_menu_item() runs at admin_menu priority 10 and Jetpack registers the parent at 998. Changelog scope is right too: without the Jetpack plugin, admin-ui registers the parent itself with edit_posts, so standalone plugins never reach the fallback.
Four suggestions, no blockers.
1. The page now loads, but its styling still keys off the other prefix. Two gaps, one of them unowned:
- Admin_Menu's own page hooks —
hide_core_admin_notices, and$page_hooksscoping the design tokens — are stilljetpack_page_only. That's #52622; worth landing the two together, or the newly-reachable page renders without design tokens and with core notices showing. - Nothing covers
routes/dashboard/route.scss:6, which selectsbody.jetpack_page_my-jetpack— core derives that class from the hook suffix. The path in the testing instructions is unaffected, since onboarding carries its own full-screen styles. Offline mode on a connected site is not:add_actions()bails on$is_offline_modewhatever the connection state,admin_initthen findsis_connected()true and doesn't redirect, and that editor gets the wp-build dashboard with its layout rules unapplied. Widening the selector tobody[class*="_page_my-jetpack"], or adding a stable class through the existingadmin_body_classfilter, closes it.
2. The test re-implements a helper already in the file. L89-L101 rebuilds capture_admin_init_redirect() (L262-L286) inline: same throw-from-wp_redirect trick, its own phpcs:ignore, its own capture variable. Giving the helper an optional callable — private function capture_admin_init_redirect( ?callable $trigger = null ), defaulting to Initializer::admin_init() — lets the trigger vary instead of the mechanism, and stops the exception type being a detail two places have to agree on.
3. The slug is spelled twice. L219 hardcodes my-jetpack three lines below the 'my-jetpack' passed to add_menu(). One $menu_slug local feeding both keeps them from drifting.
4. The new test has no summary docblock, unlike its neighbours, and nothing says why it hand-registers the page with add_submenu_page() rather than firing admin_menu. That reason is worth a line: without the Jetpack plugin, admin-ui registers the parent itself, so the package's test environment can't produce the fallback through the real path.
Not a finding, but for whoever hits this next: ~15 other consumers share the add_action( 'load-' . $page_suffix, … ) shape (backup, scan, search, blaze, publicize, videopress, boost, protect). None can reach it today — they all register with manage_options, and a user without that capability has no route to the page. My Jetpack is the only production caller passing edit_posts. #52622 keeps the return value at jetpack_page_<slug>, so a second low-capability page would need this same two-line fix; an Admin_Menu seam for page-load actions would be the place to solve it once.
Review follow-up: the fallback hook spelled the slug a second time, three lines below the one passed to add_menu(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP
The test rebuilt capture_admin_init_redirect() inline. The helper now takes the trigger as a callable, so the route into admin_init() varies and the interception mechanism stays in one place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP
The layout rules matched the jetpack_page_ body class only, so an editor reaching the dashboard through the fallback page name got them unapplied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP
|
Picked this one up and pushed the review follow-ups (5f24846):
Left alone: admin-ui's own page hooks, which are #52622. 451 My Jetpack PHP tests pass, PHPCS and stylelint clean. Phan not run locally (no |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Two moderate issues and two documentation nits remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (2)
What changed in this PR
Fixes My Jetpack loading for editors on unconnected sites by supporting WordPress’s fallback admin-page hook.
Changes:
- Registers
admin_page_my-jetpack. - Adds fallback styling and regression coverage.
- Adds Jetpack and My Jetpack changelog entries.
| File | Summary | Review notes |
|---|---|---|
projects/plugins/jetpack/changelog/editor-admin-page-load-hook |
Adds the Jetpack changelog entry. | — |
projects/packages/my-jetpack/tests/php/Initializer_Test.php |
Adds regression coverage. | Nit (2 votes): Correct the explanation of when the fallback hook is used. |
projects/packages/my-jetpack/src/class-initializer.php |
Registers fallback page initialization. | Moderate (1 vote): Add shared callbacks to the fallback hook. Moderate (1 vote): Extend Jetpack admin-page recognition for the fallback screen. |
projects/packages/my-jetpack/routes/dashboard/route.scss |
Styles the fallback admin page. | — |
projects/packages/my-jetpack/changelog/editor-admin-page-load-hook |
Adds the My Jetpack changelog entry. | Nit (2 votes): Add matching entries for affected standalone plugins or explain why this is Jetpack-only. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| Significance: patch | ||
| Type: fixed | ||
|
|
||
| Fix My Jetpack failing to load for users who can edit posts but have no Jetpack menu, such as editors on unconnected sites. |
There was a problem hiding this comment.
Jetpack-only, and it's the capability on the parent menu that decides it.
The fallback name appears only when nothing has registered the jetpack parent for this user. With a standalone plugin and no Jetpack plugin, Admin_Menu::admin_menu_hook_callback() registers that parent itself, with edit_posts — so an editor has it and core names the page jetpack_page_my-jetpack. Checked in this package's test environment, where Jetpack_React_Page is absent, i.e. exactly the standalone case:
Jetpack plugin present? false
jetpack parent registered? true
core hook name: jetpack_page_my-jetpack
With the Jetpack plugin active it owns the parent, registers it with jetpack_admin_page, and Jetpack_Admin_Page::add_actions() returns early for non-admins while the site is unconnected or offline — so nothing registers it, and the fallback is reached. A site running Boost and Jetpack hits this through the Jetpack plugin, which is where the entry is.
fix-my-jetpack-duplicate-notices was mirrored to eight plugins because duplicate notices render for every plugin that ships the page. This one can't reach them.
| * The page is registered by hand because core only falls back to the admin_page_ name when the | ||
| * Jetpack plugin owns the parent menu, which this package's tests never have. |
There was a problem hiding this comment.
Fair — reworded in 77138cd. The trigger is that nothing registered the jetpack parent; the Jetpack plugin owning it is why that happens in the field, not the condition core tests. The docblock now says the page is hand-registered with no parent, and why the real registration path can't produce that here.
The docblock named the Jetpack plugin owning the parent menu as the trigger. Core's condition is that nothing registered the parent at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP
Onboarding's single action is the register request, which answers a user without `jetpack_connect` with a 403 — so an editor reaching the page through the fallback hook was sent to a screen they cannot complete. They now get the dashboard, which already renders its connection state without offering them a connect button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP
The site-not-connected state assumed onboarding had already taken anyone who could reach it, so it offered "Connect your site with one click" with no button behind it. Users without `manage_options` now get pointed at an administrator instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP
The gate and the card's copy sit in My Jetpack, which every one of these plugins ships, so a non-admin opening My Jetpack on an unconnected site sees the change whichever of them is installed. The load-hook fix above stays Jetpack-only: without that plugin, admin-ui registers the Jetpack parent menu itself and the fallback page name never comes up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wr8mRdCF1srgLG5W4g9zUP

Proposed changes
Fix My Jetpack loading for editors on unconnected, non-offline sites. When Jetpack's parent menu is unavailable to the user, WordPress registers
admin_page_my-jetpack; My Jetpack now initializes that page as well as its usualjetpack_page_my-jetpackpage.Onboarding is only offered to users who can actually connect the site. Its single action is the register request, which answers anyone without
jetpack_connectwith a 403, so these users stay on the dashboard. That state was previously unreachable, and its connection card still offered "Connect your site with one click" with no button behind it; users withoutmanage_optionsare now pointed at an administrator, the way the card already handles a non-admin who cannot connect their account.Add focused PHPUnit regressions and changelog entries for My Jetpack and Jetpack.
Related product discussion/links
Follow-up to #52557.
Does this pull request change what data or activity we track or use?
No.
Screenshots
Captured on a Jurassic Ninja site at 1440×900, signed in as an editor on an unconnected, non-offline site, at
/wp-admin/admin.php?page=my-jetpack. Before is Jetpack 16.3-a.3 as the site shipped it; after is this branch rsynced over it.TypeError: Cannot destructure property 'loadAddLicenseScreen'.Testing instructions
/wp-admin/admin.php?page=my-jetpack.loadAddLicenseScreenerror, and that?page=my-jetpack&step=onboardingsends the editor back to it.Initializer_TestPHPUnit suite.Both regressions fail without their fix. Checked on a Jurassic Ninja site running this branch: an editor on an unconnected, non-offline site loads the dashboard with no console error, an editor asking for
step=onboardingis sent back to it, and an administrator still gets onboarding. The full My Jetpack PHP suite (453 tests), PHPCS and stylelint pass locally; Phan runs in CI.