fix: hide unavailable My Jetpack links in Boost and Jetpack - #52606
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. 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. |
Code Coverage SummaryCoverage changed in 2 files.
|
|
Validation history moved from the PR description: Validation: Boost Config passed 9 tests / 40 assertions; Jetpack admin chrome passed 9 tests / 30 assertions. The Boost regression fails against the original code. Pipeline harnesses also reproduced both original bugs and checked the fixed rendering. PHPCS, PHP syntax, compatibility commit hooks, Jetpack Phan, and changelog checks passed. Boost Phan reports two existing findings in unchanged files. Boost's JavaScript suite passed. An initial Jetpack JavaScript failure did not recur on clean trunk or the separate editor-fix branch: both reruns passed all 225 suites / 1,872 tests. Styled masthead browser verification remains outside the pipeline's isolated test environment; its rendered evidence used controlled WordPress functions without compiled styles. |
ReviewReviewed at The core logic is right, and the gating is consistent across every My Jetpack entry point in No blockers. Six suggestions below; 5 and 6 are the ones I'd actually action, the rest are polish. Markup1.
$my_jetpack_url = admin_url( 'admin.php?page=my-jetpack#/overview' );
$jetpack_logo = '<svg xmlns="…" role="img" aria-label="…">…</svg>';<?php if ( $my_jetpack_available ) : ?>
<a class="jp-masthead__logo-link" href="<?php echo esc_url( $my_jetpack_url ); ?>"><?php echo $jetpack_logo; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
<?php else : ?>
<span class="jp-masthead__logo-link"><?php echo $jetpack_logo; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<?php endif; ?>2.
(Minor, related: 3. The PR adds the third. The Backward compatibility4. jetpack/projects/plugins/boost/app/admin/class-config.php Lines 141 to 144 in b444cd9 When the loaded copy of
if ( method_exists( My_Jetpack_Initializer::class, 'is_admin_page_available' ) ) {
return My_Jetpack_Initializer::is_admin_page_available();
}
return class_exists( My_Jetpack_Initializer::class ) && did_action( 'my_jetpack_init' ) > 0;Calibration: jetpack-autoloader loads the highest bundled version and Boost ships its own copy of the package, so in practice the guard should always be true once this lands. Treat it as convention-alignment rather than a live risk — though the failure mode is silent, which is the argument for the fallback. Tests5.
6. The $this->assertStringContainsString( 'class="jp-masthead__logo-link"', $masthead );in both the available and unavailable states. Otherwise the tests are well-targeted: the Boost test exercises the real What I verified
Other checks all clear: changelogs (both plugins touched directly, both entries valid and correctly typed — no package changed, so the indirect-plugin rule doesn't apply); no public API removed or resignatured; no new cross-package FQN references and Security-wise the tightened check is strictly more restrictive, and Two non-code notes: the |
…test Hoist the masthead logo and My Jetpack URL once, give the logo role=img, state the product-name note once, fall back to did_action( 'my_jetpack_init' ) when is_admin_page_available() is missing, and simplify the masthead test.
|
Thanks @xavier-lc . I applied all six in 103c950:
I also set the labels to |
…tions Move the product-name note out of the block that assigns the translated breadcrumb labels, so it sits with the bare "Jetpack" literals it describes. Assert the logo's exact wrapper in each state: the looser class-only check passed whether the element was an anchor, a span, or absent entirely. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019svoMpcVk128YhErbnqPUr
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The critical WordPress.com Simple compatibility issue and moderate test-state cleanup remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (2)
What changed in this PR
This PR hides unavailable My Jetpack links in Boost and Jetpack while preserving branding and supporting older package versions.
Changes:
- Adds availability-aware Boost configuration and tests.
- Gates Jetpack masthead and footer links.
- Adds Boost and Jetpack changelog entries.
| File | Summary | Review notes |
|---|---|---|
projects/plugins/jetpack/tests/php/_inc/lib/Akismet_Admin_Chrome_Test.php |
Tests unavailable masthead behavior. | Moderate: restore $_GET['page'] after the test (3 votes). |
projects/plugins/jetpack/changelog/masthead-my-jetpack-availability |
Documents the Jetpack fix. | No final comments. |
projects/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php |
Gates masthead and footer links. | Critical: guard Footer_Links availability on WordPress.com Simple (1 vote). |
projects/plugins/boost/tests/php/admin/Config_Test.php |
Tests registered-page availability. | No final comments. |
projects/plugins/boost/changelog/boost-my-jetpack-availability |
Documents the Boost fix. | Nit: use the adjacent component prefix and clearer user-visible wording (1 vote). |
projects/plugins/boost/app/admin/class-config.php |
Adds compatibility-aware availability checks. | No final comments. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // If Jetpack is connected OR in offline mode, this will be false. | ||
| $connectable = ! Jetpack::is_connection_ready() && ! ( new Status() )->is_offline_mode(); | ||
|
|
||
| $my_jetpack_available = Footer_Links::is_my_jetpack_available(); |
There was a problem hiding this comment.
I don't think this holds, on two counts.
The cited docblock says the opposite. src/class-footer-links.php:15-16 reads:
Autoloaded from
src/rather than shared throughJetpack_Admin_Page: WordPress.com Simple declares its own stub of that class, so loading ours there fatals.
"That class" is Jetpack_Admin_Page, the immediately preceding noun — not Footer_Links. The docblock exists to explain why Footer_Links was extracted into src/ in the first place: so it can be resolved without pulling in Jetpack_Admin_Page. There is no wpcom stub of Footer_Links.
Jetpack_Admin_Page is already loaded by the time this line runs. The statement is inside Jetpack_Admin_Page::wrap_ui(), so it cannot be what loads the colliding class — every route into it needs the class resolved first:
_inc/lib/admin-pages/class.jetpack-admin-page.php:124(self::wrap_ui())_inc/lib/admin-pages/class-jetpack-about-page.php:88class.jetpack-admin.php:570modules/stats.php:373projects/packages/publicize/src/class-publicize-ui.php:88
On the partial-bootstrap concern specifically: AGENTS.md asks that code reachable that way not depend on non-autoloaded dependencies. Footer_Links is classmap-autoloaded ("autoload": { "classmap": ["src"] } in the plugin's composer.json), which is exactly that shape.
The ! is_wpcom_platform() check on the footer is a product decision about which links wpcom shows, not a load guard — and it stays where it is.
| $_GET['page'] = 'jetpack_modules'; | ||
| $render_masthead = function () { | ||
| $markup = $this->render( | ||
| static function () { | ||
| Jetpack_Admin_Page::wrap_ui( '__return_empty_string' ); | ||
| } | ||
| ); | ||
| return substr( $markup, 0, strpos( $markup, '</header>' ) ); |
There was a problem hiding this comment.
$_GET can't leak between tests here — WordPress's own test case resets it before every test, so the save/restore was dead weight (which is why it was removed).
The chain, end to end:
WP_UnitTestCase_Base::set_up()callsclean_up_global_scope()unconditionally —tests/phpunit/includes/abstract-testcase.php:123clean_up_global_scope()does$_GET = array();— same file,:241-246abstract class WP_UnitTestCase extends WP_UnitTestCase_Base {}— empty body, no overrideAutomattic\Jetpack\PHPUnit\WP_UnitTestCase_Fixdefines onlygetAnnotations(),getGroups()andcheckRequirements(); it doesn't touchset_up()
So a later wrap_ui() test starts with an empty $_GET regardless of what this one leaves behind, and can't render the Modules title unexpectedly.
Verified by running the class against this tree: jp docker phpunit jetpack -- --filter=Akismet_Admin_Chrome_Test → OK (9 tests, 32 assertions).
# Conflicts: # projects/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php
Keeping the product-name note in its own PHP block emitted the block's indentation twice in a row, which PhanPluginDuplicateAdjacentStatement flags. Fold it back into the block above, separated by a blank line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019svoMpcVk128YhErbnqPUr
# Conflicts: # projects/plugins/jetpack/_inc/lib/admin-pages/class.jetpack-admin-page.php
The two new phpcs:ignore lines carried no justification, unlike the identical suppression in class-akismet-admin-chrome.php and the one further down this same file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019svoMpcVk128YhErbnqPUr


Proposed changes
Use My Jetpack's shared admin-page availability check for Boost's links, with a compatibility guard for older package versions. Only link Jetpack's masthead logo and title to My Jetpack when its page is reachable; preserve the branding when it is unavailable.
Include one focused regression test per behavior and changelog entries for Boost and Jetpack.
Before / after
Captured on a live Jurassic Ninja site at
/wp-admin/admin.php?page=jetpack_modules, 1440×900. The site is in Offline Mode, soMy_Jetpack_Initializer::should_initialize()returns false and the My Jetpack page is genuinely never registered — the exact condition this PR handles. Baseline is the Jetpack build JN ships (16.3-a.3), which already has #52557'sFooter_Linksbut still renders the masthead anchor unconditionally.Both shots are hovering "Jetpack". At rest the two states are pixel-identical:
.jp-masthead__title-linkuses the same colour as the surrounding text withtext-decoration: none, and.jp-masthead__logo-linkis styled by class, so swapping<a>for<span>moves nothing. Hover is where the difference is visible.Measured in the DOM on the same page, same site:
.jp-masthead__logo-linktagASPANhref…page=my-jetpack#/overview.jp-masthead__title-linkpresentrole="img")Jetpack / Offline Mode / ModulesJetpack / Offline Mode / ModulesBranding is preserved in both — only the links go away.
Related product discussion/links
Follow-up to #52557. The editor page-load correction is being handled separately.
Does this pull request change what data or activity we track or use?
No.
Testing instructions
Config_Testand Jetpack'sAkismet_Admin_Chrome_TestPHPUnit cases.