Skip to content
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
1cbc66a
Admin Menu: Reorder menu items so external links appear last
DevinWalker Mar 2, 2026
cd60b97
Remove unnecessary paragraph bottom padding
DevinWalker Mar 2, 2026
9c736c8
Add devin's wp.org handle to readme
DevinWalker Mar 3, 2026
fad3e8f
Remove paragraph bottom padding causing vertical alignment issues
DevinWalker Mar 3, 2026
36d5cea
add devin (dlocc) to contributors list
DevinWalker Mar 3, 2026
0a1cc55
Backup: Replace Jetpack Button with WordPress Button component
DevinWalker Mar 3, 2026
7259e4d
Admin Menu: Simplify menu item titles
DevinWalker Mar 3, 2026
d611f0e
Add missing changelog entries for admin header improvements
DevinWalker Mar 3, 2026
a21d087
Update projects/packages/backup/src/class-jetpack-backup.php
DevinWalker Mar 3, 2026
649069d
Update projects/packages/my-jetpack/src/class-activitylog.php
DevinWalker Mar 3, 2026
10b8090
Update BackupNowButton propTypes to include new variant options
DevinWalker Mar 3, 2026
84fc4af
Update projects/plugins/jetpack/readme.txt
DevinWalker Mar 3, 2026
be32c94
Update projects/js-packages/components/components/admin-page/style.mo…
DevinWalker Mar 3, 2026
ebdfb8e
admin-ui: Add Upgrade to Pro menu item for free users
DevinWalker Mar 3, 2026
3f16dcc
admin-ui: Update "Upgrade to Pro" menu item styles and tests
DevinWalker Mar 3, 2026
657b5d6
admin-ui: Update upgrade menu redirect slug to use SaaS-managed URL
DevinWalker Mar 3, 2026
23f24f7
Merge branch 'trunk' of github.com:Automattic/jetpack into feature/up…
DevinWalker Mar 10, 2026
eed8e20
Address code review feedback
DevinWalker Mar 10, 2026
f342bc0
Merge branch 'trunk' of github.com:Automattic/jetpack into feature/up…
DevinWalker Mar 10, 2026
d4b6491
Merge branch 'trunk' of github.com:Automattic/jetpack into feature/up…
DevinWalker Mar 13, 2026
683eba1
Admin UI: Update menu item text to "Upgrade Jetpack"
DevinWalker Mar 13, 2026
617ffd3
Admin UI: Clean up changelog entries for this PR
DevinWalker Mar 13, 2026
4812910
Address code review feedback for upgrade menu feature
DevinWalker Mar 13, 2026
f5c0603
Merge branch 'trunk' of github.com:Automattic/jetpack into feature/up…
DevinWalker Mar 13, 2026
1653308
Fix upgrade menu showing when site has active license
DevinWalker Mar 13, 2026
34cf1f0
Fix upgrade menu detection by checking is_free field
DevinWalker Mar 13, 2026
fa2df07
Add comprehensive tests for upgrade menu plan detection
DevinWalker Mar 13, 2026
de846ec
Update composer.lock to include new Jetpack dependencies and update e…
DevinWalker Mar 13, 2026
5b70ad8
Admin UI: Update docblock to reference "Upgrade Jetpack"
DevinWalker Mar 13, 2026
dbf2f01
Add changelog entries.
Mar 13, 2026
8e39460
Merge branch 'feature/upsell-to-pro-wp-admin-menu' of github.com:Auto…
DevinWalker Mar 13, 2026
7395c74
Merge branch 'trunk' into feature/upsell-to-pro-wp-admin-menu
simison Mar 31, 2026
ec433d9
Update composer.lock
simison Mar 31, 2026
0b07819
Satisfy Phan
simison Mar 31, 2026
bda9b52
Ran ./tools/composer-update-monorepo.sh
simison Mar 31, 2026
25b78be
Remove circular dependency
simison Apr 1, 2026
0cecf4d
Update composer locks after circular dependency fix
simison Apr 1, 2026
d40e4ba
Update also A4A client
simison Apr 1, 2026
7aab45f
Changelogs
simison Apr 1, 2026
345ad70
Update Admin_Menu_Test.php
simison Apr 1, 2026
676964e
Add legacy active_plan['class'] check
simison Apr 1, 2026
8f51d33
Use true null but still suppress Phan
simison Apr 1, 2026
c27bfbc
Update tests
simison Apr 1, 2026
c611366
See if separate process for each test helps
simison Apr 1, 2026
ce1b5dc
Run tests regularly, remove the static caching of $is_free
simison Apr 1, 2026
fa1702f
Remove deprecated setAccessible
simison Apr 1, 2026
f49f943
Run deprecated method on older PHP versions
simison Apr 1, 2026
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
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Part of admin header normalization work

Remove padding from admin page header subtitle for consistent spacing.
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,3 @@
:global(.jetpack-admin-page #dolly) {
background-color: #fff;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add "Upgrade Jetpack" menu item for free users in the Jetpack admin menu.
4 changes: 3 additions & 1 deletion projects/packages/admin-ui/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"type": "jetpack-library",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.2"
"php": ">=7.2",
"automattic/jetpack-redirect": "@dev",
"automattic/jetpack-status": "@dev"
},
"require-dev": {
"yoast/phpunit-polyfills": "^4.0.0",
Expand Down
141 changes: 141 additions & 0 deletions projects/packages/admin-ui/src/class-admin-menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ class Admin_Menu {

const PACKAGE_VERSION = '0.6.0';

/**
* Redirect source slug used as the upgrade URL identifier and CSS class.
*
* @var string
*/
const UPGRADE_MENU_SLUG = 'jetpack-wpadmin-sidebar-free-plan-upsell-menu-item';

/**
* Fallback upgrade URL when the Redirect class is unavailable.
*
* @var string
*/
const UPGRADE_MENU_FALLBACK_URL = 'https://jetpack.com/upgrade/';

/**
* Whether this class has been initialized
*
Expand All @@ -40,6 +54,7 @@ public static function init() {
self::handle_akismet_menu();
add_action( 'admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998.
add_action( 'network_admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998.
add_action( 'admin_head', array( __CLASS__, 'add_upgrade_menu_item_styles' ) );
}
}

Expand Down Expand Up @@ -140,6 +155,8 @@ function ( $a, $b ) {
if ( ! $can_see_toplevel_menu ) {
remove_menu_page( 'jetpack' );
}

self::maybe_add_upgrade_menu_item();
}

/**
Expand Down Expand Up @@ -225,4 +242,128 @@ public static function get_top_level_menu_item_url( $fallback = false ) {
$url = $fallback ? $fallback : admin_url();
return $url;
}

/**
* Checks whether the current site should show the upgrade menu item.
*
* The upgrade menu is only shown to administrators on free-plan sites
* that are not hosted on WordPress.com.
*
* @return bool True if the upgrade menu should be shown.
*/
private static function should_show_upgrade_menu() {
// Only show to administrators.
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}

// Don't show upsells on WordPress.com platform.
if ( class_exists( '\Automattic\Jetpack\Status\Host' ) ) {
$host = new \Automattic\Jetpack\Status\Host();
if ( $host->is_wpcom_platform() ) {
return false;
}
}

// Only show to free-plan sites.
return self::is_free_plan();
}

/**
* Checks whether the current site is on a free Jetpack plan with no active paid license.
*
* @return bool True if the site has no paid plan.
*/
private static function is_free_plan() {
// Check the active plan - use the is_free field or product_slug.
$plan = get_option( 'jetpack_active_plan', array() );

// Back-compat: older plan payloads use class to indicate paid plans.
if ( isset( $plan['class'] ) && 'free' !== $plan['class'] ) {
return false;
}

// If the plan explicitly says it's not free, trust that.
if ( isset( $plan['is_free'] ) && false === $plan['is_free'] ) {
return false;
}

// Check if the product slug indicates a paid plan.
if ( isset( $plan['product_slug'] ) && 'jetpack_free' !== $plan['product_slug'] ) {
return false;
}

// Also check for site products (licenses can add products without changing plan).
$products = get_option( 'jetpack_site_products', array() );
if ( ! empty( $products ) && is_array( $products ) ) {
return false;
}

return true;
}

/**
* Conditionally adds an "Upgrade Jetpack" submenu item for free-plan sites.
*
* Only shown to users with manage_options capability on self-hosted sites without a paid Jetpack plan or license.
*
* @return void
*/
private static function maybe_add_upgrade_menu_item() {
if ( ! self::should_show_upgrade_menu() ) {
return;
}

$upgrade_url = class_exists( '\Automattic\Jetpack\Redirect' )
? \Automattic\Jetpack\Redirect::get_url( self::UPGRADE_MENU_SLUG )
: self::UPGRADE_MENU_FALLBACK_URL;

$menu_title = esc_html__( 'Upgrade Jetpack', 'jetpack-admin-ui' )
. ' <span aria-hidden="true">↗</span>';

add_submenu_page(
'jetpack',
__( 'Upgrade Jetpack', 'jetpack-admin-ui' ),
$menu_title,
'manage_options',
esc_url( $upgrade_url ),
null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. https://core.trac.wordpress.org/ticket/52539.
999
);

// Add a CSS class to the <li> element so styles can target it precisely.
global $submenu;
if ( ! empty( $submenu['jetpack'] ) ) {
foreach ( $submenu['jetpack'] as $index => $item ) {
if ( isset( $item[2] ) && false !== strpos( $item[2], self::UPGRADE_MENU_SLUG ) ) {
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$submenu['jetpack'][ $index ][4] = ( ! empty( $item[4] ) ? $item[4] . ' ' : '' ) . self::UPGRADE_MENU_SLUG;
break;
}
}
}
}

/**
* Outputs inline CSS to style the "Upgrade Jetpack" menu item in Jetpack green.
*
* The sidebar menu is visible on every admin page, so styles must load globally.
* Only outputs for free-plan sites on self-hosted installs.
*
* @return void
*/
public static function add_upgrade_menu_item_styles() {
if ( ! self::should_show_upgrade_menu() ) {
return;
}
?>
<style>
#adminmenu li.<?php echo esc_attr( self::UPGRADE_MENU_SLUG ); ?> > a,
#adminmenu li.<?php echo esc_attr( self::UPGRADE_MENU_SLUG ); ?> > a:hover {
color: #069e08 !important;
font-weight: 600;
}
</style>
<?php
}
}
Loading
Loading