Skip to content

fix(my-jetpack): fix page loading for editors on unconnected sites - #52614

Merged
xavier-lc merged 9 commits into
trunkfrom
fm/boost-745-editor-load-hook
Sep 23, 2026
Merged

xavier-lc merged 9 commits into
trunkfrom
fm/boost-745-editor-load-hook

Conversation

@LiamSarsfield

@LiamSarsfield LiamSarsfield commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

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 usual jetpack_page_my-jetpack page.

Onboarding is only offered to users who can actually connect the site. Its single action is the register request, which answers anyone without jetpack_connect with 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 without manage_options are 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.

Before After
before after
"Something went wrong!" — the console carries TypeError: Cannot destructure property 'loadAddLicenseScreen'. The dashboard renders, and the connection card points the editor at an administrator rather than offering a connect action they cannot take.

Testing instructions

  1. Activate Jetpack on an unconnected site with offline mode disabled.
  2. Sign in as an editor and visit /wp-admin/admin.php?page=my-jetpack.
  3. Confirm the dashboard renders instead of the loadAddLicenseScreen error, and that
    ?page=my-jetpack&step=onboarding sends the editor back to it.
  4. As an administrator on the same site, confirm onboarding still loads.
  5. Run My Jetpack's Initializer_Test PHPUnit 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=onboarding is 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.

@LiamSarsfield
LiamSarsfield marked this pull request as draft September 22, 2026 10:18
@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the fm/boost-745-editor-load-hook branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack fm/boost-745-editor-load-hook

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions github-actions Bot added [Package] My Jetpack [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Tests] Includes Tests labels Sep 22, 2026
@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

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:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

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:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: October 6, 2026

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.

@github-actions github-actions Bot added [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Status] In Progress labels Sep 22, 2026
@jp-launch-control

jp-launch-control Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Code Coverage Summary

No summary data is available for parent commit 8285ce9, so cannot calculate coverage changes. 😴

If that commit is a feature branch rather than a trunk commit, this is expected. Otherwise, this should be updated once coverage for 8285ce9 is available.

Full summary · PHP report · JS report

@xavier-lc xavier-lc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_hooks scoping the design tokens — are still jetpack_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 selects body.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_mode whatever the connection state, admin_init then finds is_connected() true and doesn't redirect, and that editor gets the wp-build dashboard with its layout rules unapplied. Widening the selector to body[class*="_page_my-jetpack"], or adding a stable class through the existing admin_body_class filter, 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.

Xavier Lozano Carreras and others added 3 commits September 22, 2026 18:21
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
@xavier-lc

Copy link
Copy Markdown
Contributor

Picked this one up and pushed the review follow-ups (5f24846):

  • Dashboard styling under the fallback page name — routes/dashboard/route.scss now matches admin_page_my-jetpack as well. Offline mode on a connected site is the case that reaches the dashboard itself rather than onboarding, and it was getting the layout rules unapplied. The existing changelog entries cover it; it is the same bug as the load hook, not a second one.
  • Test reuses capture_admin_init_redirect() instead of rebuilding it inline. The helper takes the trigger as a callable now, so what reaches admin_init() varies and the interception mechanism lives in one place. Still red without the fix — Expected the page load to reach admin_init().
  • Slug bound to one local, and the new test carries a summary docblock saying why the page is registered by hand.

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 ast extension here), so CI has that one.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Low severity

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +68 to +69
* 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
LiamSarsfield added a commit that referenced this pull request Sep 22, 2026
Xavier Lozano Carreras and others added 3 commits September 22, 2026 23:50
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
@github-actions github-actions Bot added the [Plugin] Backup A plugin that allows users to save every change and get back online quickly with one-click restores. label Sep 23, 2026
@github-actions github-actions Bot added [Plugin] Boost A feature to speed up the site and improve performance. [Plugin] Protect A plugin with features to protect a site: brute force protection, security scanning, and a WAF. [Plugin] Search A plugin to add an instant search modal to your site to help visitors find content faster. [Plugin] Social Issues about the Jetpack Social plugin [Plugin] Stats Data [Plugin] VideoPress A standalone plugin to add high-quality VideoPress videos to your site. labels Sep 23, 2026
@xavier-lc xavier-lc self-assigned this Sep 23, 2026
@xavier-lc
xavier-lc merged commit 3df16c0 into trunk Sep 23, 2026
115 of 116 checks passed
@xavier-lc
xavier-lc deleted the fm/boost-745-editor-load-hook branch September 23, 2026 10:49
@github-actions github-actions Bot added [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. and removed [Status] In Progress [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. labels Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] My Jetpack [Plugin] Backup A plugin that allows users to save every change and get back online quickly with one-click restores. [Plugin] Boost A feature to speed up the site and improve performance. [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] Protect A plugin with features to protect a site: brute force protection, security scanning, and a WAF. [Plugin] Search A plugin to add an instant search modal to your site to help visitors find content faster. [Plugin] Social Issues about the Jetpack Social plugin [Plugin] Stats Data [Plugin] VideoPress A standalone plugin to add high-quality VideoPress videos to your site. [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants