updated plugin Jetpack Protect
version 2.2.0
This commit is contained in:
@ -5,6 +5,145 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [4.24.1] - 2024-05-24
|
||||
### Changed
|
||||
- Update Search to require user connection. [#37496]
|
||||
|
||||
## [4.24.0] - 2024-05-23
|
||||
### Changed
|
||||
- Improve the active/inactive statuses on My Jetpack. [#37217]
|
||||
|
||||
## [4.23.3] - 2024-05-20
|
||||
### Added
|
||||
- Add tracks events for dropdown on action buttons. [#37292]
|
||||
|
||||
### Changed
|
||||
- Updated package dependencies. [#37379] [#37380] [#37382]
|
||||
|
||||
## [4.23.2] - 2024-05-09
|
||||
### Changed
|
||||
- My Jetpack Agency banner copy change. [#37248]
|
||||
|
||||
### Fixed
|
||||
- Fix typo [#37303]
|
||||
|
||||
## [4.23.1] - 2024-05-08
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [4.23.0] - 2024-05-06
|
||||
### Added
|
||||
- Add a has_free_offering boolean on My Jetpack products. [#36710]
|
||||
- Add copy for AI features on connection screen. [#37218]
|
||||
- Define `$module_name` static property on `Hybrid_Product` (defaulting to null). [#37201]
|
||||
- Jetpack AI: Add new badge to Featured Image video on the product page. [#37197]
|
||||
- My Jetpack: Add errors from the connection package to the new notice component. [#36840]
|
||||
|
||||
### Changed
|
||||
- Change "go back" in My Jetpack interstitials to always return to the My Jetpack page. [#36685]
|
||||
- Fix z-index issue and close action button dropdown when clicked outside. [#37169]
|
||||
- Jetpack AI: Add featured image generation as a feature on the product interstitial. [#37199]
|
||||
- Jetpack AI: Include video about featured image generation on the product page. [#37192]
|
||||
- Updated package dependencies. [#37147] [#37148] [#37160]
|
||||
|
||||
## [4.22.3] - 2024-04-29
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [4.22.2] - 2024-04-25
|
||||
### Fixed
|
||||
- My Jetpack: Fix issue where the TOS component was being called inside of a <p>, throwing a warning that <p> can't be a descendant of <p>. This also fixes the font size of the TOS text. [#37034]
|
||||
|
||||
## [4.22.1] - 2024-04-22
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [4.22.0] - 2024-04-11
|
||||
### Added
|
||||
- Add new tracking event for product activations made through My Jetpack [#36745]
|
||||
- My Jetpack: add a way to connect a Jetpack site to WordPress.com asynchronously and update the message in the notice to reflect that. [#36771]
|
||||
|
||||
### Fixed
|
||||
- Ensure page_view gets loaded before product_card_loaded [#36790]
|
||||
|
||||
## [4.21.0] - 2024-04-08
|
||||
### Added
|
||||
- Add 'from' property to connection call to WP.com. [#36741]
|
||||
- My Jetpack: Update Notice component to allow adding a loading text when an action is in a loading state. Add a new resetNotice action to NoticeContext. [#36614]
|
||||
|
||||
### Changed
|
||||
- My Jetpack: Show Boost score increase in Boost product card. [#36072]
|
||||
- Updated package dependencies. [#36756] [#36760] [#36761]
|
||||
- Update My Jetpack to use the Notice component from @automattic/jetpack-components and be more consistent with the other notices in Jetpack. [#36711]
|
||||
|
||||
### Removed
|
||||
- My Jetpack: Removed custom Notice component from My Jetpack. [#36772]
|
||||
- My Jetpack: Remove red bubble from connection notice in favor of using the status of the Notice component. [#36773]
|
||||
|
||||
### Fixed
|
||||
- Fix cache on front end request for boost speed scores [#36700]
|
||||
- fix tier upgrades in my Jetpack [#36705]
|
||||
|
||||
## [4.20.2] - 2024-04-02
|
||||
### Fixed
|
||||
- Fix Boost score inconsistency [#36679]
|
||||
|
||||
## [4.20.1] - 2024-04-01
|
||||
### Added
|
||||
- Change Phan baselines. [#36627]
|
||||
|
||||
## [4.20.0] - 2024-03-29
|
||||
### Added
|
||||
- Track active red bubble slugs on My Jetpack page view [#36611]
|
||||
|
||||
### Fixed
|
||||
- Better handling on product interstitial pages if the site already has a paid product [#36570]
|
||||
- Shows Jetpack CRM card as active on My Jetpack if the plugin is installed and active [#36594]
|
||||
|
||||
## [4.19.0] - 2024-03-27
|
||||
### Added
|
||||
- Add red bubble to notices tied to red bubble notifications [#36543]
|
||||
- My Jetpack: add a version of WordPress' Notice component to My Jetpack considering the context of how we use notices on that screen [#36551]
|
||||
|
||||
### Changed
|
||||
- Updated package dependencies. [#36539, #36585]
|
||||
|
||||
### Fixed
|
||||
- Fixed Jetpack Creator going to the wrong screen when the free version is selected" [#36547]
|
||||
- Fixes some pricing showing twice by connecting sites that select a free option [#36533]
|
||||
|
||||
## [4.18.0] - 2024-03-25
|
||||
### Added
|
||||
- Hook into red bubble notification when bad installation is detected [#36449]
|
||||
- Jetpack AI: add notices on product page for exhausted requests [#35910]
|
||||
- Jetpack AI: add plans/tier information on product page and corresponding CTAs [#35910]
|
||||
- Jetpack AI: add support and create post links on product page [#35910]
|
||||
- My Jetpack: add AI product page view event [#36488]
|
||||
- My Jetpack: add feedback link on Jetpack AI product page [#35910]
|
||||
- My Jetpack: AI pricing table is skipped once the user has opted for the free version [#35910]
|
||||
- My Jetpack: change AI product for tiered pricing table display [#35910]
|
||||
|
||||
### Changed
|
||||
- Add notice priorities to My Jetpack [#36438]
|
||||
- Jetpack AI: address responsive issues on the styles [#35910]
|
||||
- My Jetpack: AI product page styles update and responsive fixes [#35910]
|
||||
- My Jetpack: change AI card action button target for upgraded users, point to product page [#35910]
|
||||
|
||||
### Removed
|
||||
- My Jetpack: remove Jetpack AI code added throughout the new product page project [#35910]
|
||||
- Removed reference to Creator Network, which is being deprecated. [#36168]
|
||||
|
||||
### Fixed
|
||||
- Boost tooltips: fix typo in string. [#36520]
|
||||
- My Jetpack: fix AI interstitial "remain free" flow [#35910]
|
||||
- My Jetpack: fix interstitial event property malformed name productSlug -> product_slug [#36486]
|
||||
- My Jetpack: fix spacing issues on the new product page [#35910]
|
||||
- My Jetpack: new AI interstitial margin on admin page was messing with correct top spacing [#35910]
|
||||
|
||||
## [4.17.1] - 2024-03-18
|
||||
### Added
|
||||
- Add a loaded event for My Jetpack product cards [#36397]
|
||||
|
||||
## [4.17.0] - 2024-03-14
|
||||
### Changed
|
||||
- Rewrite My Jetpack hooks to Typescript [#36288]
|
||||
@ -1347,6 +1486,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- Created package
|
||||
|
||||
[4.24.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.24.0...4.24.1
|
||||
[4.24.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.3...4.24.0
|
||||
[4.23.3]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.2...4.23.3
|
||||
[4.23.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.1...4.23.2
|
||||
[4.23.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.0...4.23.1
|
||||
[4.23.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.3...4.23.0
|
||||
[4.22.3]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.2...4.22.3
|
||||
[4.22.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.1...4.22.2
|
||||
[4.22.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.0...4.22.1
|
||||
[4.22.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.21.0...4.22.0
|
||||
[4.21.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.20.2...4.21.0
|
||||
[4.20.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.20.1...4.20.2
|
||||
[4.20.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.20.0...4.20.1
|
||||
[4.20.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.19.0...4.20.0
|
||||
[4.19.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.18.0...4.19.0
|
||||
[4.18.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.17.1...4.18.0
|
||||
[4.17.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.17.0...4.17.1
|
||||
[4.17.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.16.0...4.17.0
|
||||
[4.16.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.15.0...4.16.0
|
||||
[4.15.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.14.0...4.15.0
|
||||
|
@ -1 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => '5782f9bf704638c25046');
|
||||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => 'cc72b3b90919a178b53e');
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -5,23 +5,22 @@
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"automattic/jetpack-admin-ui": "^0.4.1",
|
||||
"automattic/jetpack-assets": "^2.1.4",
|
||||
"automattic/jetpack-boost-speed-score": "^0.3.6",
|
||||
"automattic/jetpack-connection": "^2.4.1",
|
||||
"automattic/jetpack-jitm": "^3.1.1",
|
||||
"automattic/jetpack-licensing": "^2.0.2",
|
||||
"automattic/jetpack-plugins-installer": "^0.3.2",
|
||||
"automattic/jetpack-redirect": "^2.0.1",
|
||||
"automattic/jetpack-constants": "^2.0.1",
|
||||
"automattic/jetpack-plans": "^0.4.3",
|
||||
"automattic/jetpack-status": "^2.1.2"
|
||||
"automattic/jetpack-admin-ui": "^0.4.2",
|
||||
"automattic/jetpack-assets": "^2.1.11",
|
||||
"automattic/jetpack-boost-speed-score": "^0.3.11",
|
||||
"automattic/jetpack-connection": "^2.8.4",
|
||||
"automattic/jetpack-jitm": "^3.1.11",
|
||||
"automattic/jetpack-licensing": "^2.0.5",
|
||||
"automattic/jetpack-plugins-installer": "^0.4.0",
|
||||
"automattic/jetpack-redirect": "^2.0.2",
|
||||
"automattic/jetpack-constants": "^2.0.2",
|
||||
"automattic/jetpack-plans": "^0.4.7",
|
||||
"automattic/jetpack-status": "^3.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"yoast/phpunit-polyfills": "1.1.0",
|
||||
"automattic/jetpack-changelogger": "^4.1.1",
|
||||
"automattic/wordbless": "@dev",
|
||||
"automattic/jetpack-videopress": "^0.23.10"
|
||||
"automattic/jetpack-changelogger": "^4.2.4",
|
||||
"automattic/wordbless": "@dev"
|
||||
},
|
||||
"suggest": {
|
||||
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
|
||||
@ -69,10 +68,16 @@
|
||||
"link-template": "https://github.com/Automattic/jetpack-my-jetpack/compare/${old}...${new}"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-trunk": "4.17.x-dev"
|
||||
"dev-trunk": "4.24.x-dev"
|
||||
},
|
||||
"version-constants": {
|
||||
"::PACKAGE_VERSION": "src/class-initializer.php"
|
||||
},
|
||||
"dependencies": {
|
||||
"test-only": [
|
||||
"packages/search",
|
||||
"packages/videopress"
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
@ -1,7 +1,24 @@
|
||||
declare module '*.png';
|
||||
declare module '*.svg';
|
||||
declare module '*.jpeg';
|
||||
declare module '*.jpg';
|
||||
declare module '*.scss';
|
||||
|
||||
// These libraries don't have types, this suppresses the TypeScript errors
|
||||
declare module '@wordpress/components';
|
||||
declare module '@wordpress/compose';
|
||||
declare module '@wordpress/i18n';
|
||||
declare module '@wordpress/icons';
|
||||
|
||||
interface Window {
|
||||
myJetpackInitialState?: {
|
||||
siteSuffix: string;
|
||||
siteUrl: string;
|
||||
latestBoostSpeedScores: {
|
||||
previousScores: {
|
||||
desktop: number;
|
||||
mobile: number;
|
||||
};
|
||||
scores: {
|
||||
desktop: number;
|
||||
mobile: number;
|
||||
@ -62,8 +79,6 @@ interface Window {
|
||||
features: string[];
|
||||
has_paid_plan_for_product: boolean;
|
||||
features_by_tier: Array< string >;
|
||||
has_required_plan: boolean;
|
||||
has_required_tier: Array< string >;
|
||||
is_bundle: boolean;
|
||||
is_plugin_active: boolean;
|
||||
is_upgradable_by_bundle: string[];
|
||||
@ -179,12 +194,18 @@ interface Window {
|
||||
check_dns: boolean;
|
||||
} >;
|
||||
};
|
||||
redBubbleAlerts: {
|
||||
'missing-site-connection'?: null;
|
||||
'welcome-banner-active'?: null;
|
||||
[ key: `${ string }-bad-installation` ]: {
|
||||
data: {
|
||||
plugin: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
topJetpackMenuItemUrl: string;
|
||||
userIsAdmin: string;
|
||||
userIsNewToJetpack: string;
|
||||
welcomeBanner: {
|
||||
hasBeenDismissed: boolean;
|
||||
};
|
||||
};
|
||||
JP_CONNECTION_INITIAL_STATE: {
|
||||
apiRoot: string;
|
||||
|
@ -9,7 +9,6 @@ namespace Automattic\Jetpack\My_Jetpack;
|
||||
|
||||
use Automattic\Jetpack\Admin_UI\Admin_Menu;
|
||||
use Automattic\Jetpack\Assets;
|
||||
use Automattic\Jetpack\Boost_Speed_Score\Jetpack_Boost_Modules;
|
||||
use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
|
||||
use Automattic\Jetpack\Boost_Speed_Score\Speed_Score_History;
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
@ -26,6 +25,7 @@ use Automattic\Jetpack\Status\Host as Status_Host;
|
||||
use Automattic\Jetpack\Terms_Of_Service;
|
||||
use Automattic\Jetpack\Tracking;
|
||||
use Jetpack;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* The main Initializer class that registers the admin menu and eneuque the assets.
|
||||
@ -37,7 +37,7 @@ class Initializer {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PACKAGE_VERSION = '4.17.0';
|
||||
const PACKAGE_VERSION = '4.24.1';
|
||||
|
||||
/**
|
||||
* HTML container ID for the IDC screen on My Jetpack page.
|
||||
@ -62,7 +62,7 @@ class Initializer {
|
||||
/**
|
||||
* Holds info/data about the site (from the /sites/%d endpoint)
|
||||
*
|
||||
* @var stdClass Object
|
||||
* @var object
|
||||
*/
|
||||
public static $site_info;
|
||||
|
||||
@ -87,8 +87,7 @@ class Initializer {
|
||||
}
|
||||
|
||||
// Initialize Boost Speed Score
|
||||
$boost_modules = Jetpack_Boost_Modules::init();
|
||||
new Speed_Score( $boost_modules, 'jetpack-my-jetpack' );
|
||||
new Speed_Score( array(), 'jetpack-my-jetpack' );
|
||||
|
||||
// Add custom WP REST API endoints.
|
||||
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
|
||||
@ -200,7 +199,14 @@ class Initializer {
|
||||
);
|
||||
$modules = new Modules();
|
||||
$connection = new Connection_Manager();
|
||||
$speed_score_history = new Speed_Score_History( wp_parse_url( get_site_url(), PHP_URL_HOST ) );
|
||||
$speed_score_history = new Speed_Score_History( get_site_url() );
|
||||
$latest_score = $speed_score_history->latest();
|
||||
$previous_score = array();
|
||||
if ( $speed_score_history->count() > 1 ) {
|
||||
$previous_score = $speed_score_history->latest( 1 );
|
||||
}
|
||||
$latest_score['previousScores'] = $previous_score['scores'] ?? array();
|
||||
|
||||
wp_localize_script(
|
||||
'my_jetpack_main_app',
|
||||
'myJetpackInitialState',
|
||||
@ -216,6 +222,7 @@ class Initializer {
|
||||
'myJetpackCheckoutUri' => admin_url( 'admin.php?page=my-jetpack' ),
|
||||
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
|
||||
'siteSuffix' => ( new Status() )->get_site_suffix(),
|
||||
'siteUrl' => esc_url( get_site_url() ),
|
||||
'blogID' => Connection_Manager::get_site_id( true ),
|
||||
'myJetpackVersion' => self::PACKAGE_VERSION,
|
||||
'myJetpackFlags' => self::get_my_jetpack_flags(),
|
||||
@ -237,14 +244,11 @@ class Initializer {
|
||||
'isUserFromKnownHost' => self::is_user_from_known_host(),
|
||||
'isCommercial' => self::is_commercial_site(),
|
||||
'isAtomic' => ( new Status_Host() )->is_woa_site(),
|
||||
'welcomeBanner' => array(
|
||||
'hasBeenDismissed' => \Jetpack_Options::get_option( 'dismissed_welcome_banner', false ),
|
||||
),
|
||||
'jetpackManage' => array(
|
||||
'isEnabled' => Jetpack_Manage::could_use_jp_manage(),
|
||||
'isAgencyAccount' => Jetpack_Manage::is_agency_account(),
|
||||
),
|
||||
'latestBoostSpeedScores' => $speed_score_history->latest(),
|
||||
'latestBoostSpeedScores' => $latest_score,
|
||||
)
|
||||
);
|
||||
|
||||
@ -496,7 +500,7 @@ class Initializer {
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
|
||||
return new \WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ) );
|
||||
return new WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ) );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $body, 200 );
|
||||
@ -505,7 +509,7 @@ class Initializer {
|
||||
/**
|
||||
* Populates the self::$site_info var with site data from the /sites/%d endpoint
|
||||
*
|
||||
* @return Object|WP_Error
|
||||
* @return object|WP_Error
|
||||
*/
|
||||
public static function get_site_info() {
|
||||
static $site_info = null;
|
||||
@ -620,7 +624,7 @@ class Initializer {
|
||||
public static function maybe_show_red_bubble() {
|
||||
global $menu;
|
||||
// filters for the items in this file
|
||||
add_filter( 'my_jetpack_red_bubble_notification_slugs', array( __CLASS__, 'alert_if_missing_site_connection' ) );
|
||||
add_filter( 'my_jetpack_red_bubble_notification_slugs', array( __CLASS__, 'add_red_bubble_alerts' ) );
|
||||
$red_bubble_alerts = self::get_red_bubble_alerts();
|
||||
|
||||
// The Jetpack menu item should be on index 3
|
||||
@ -653,6 +657,22 @@ class Initializer {
|
||||
return $red_bubble_alerts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add relevant red bubble notifications
|
||||
*
|
||||
* @param array $red_bubble_slugs - slugs that describe the reasons the red bubble is showing.
|
||||
* @return array
|
||||
*/
|
||||
public static function add_red_bubble_alerts( array $red_bubble_slugs ) {
|
||||
$welcome_banner_dismissed = \Jetpack_Options::get_option( 'dismissed_welcome_banner', false );
|
||||
if ( self::is_jetpack_user_new() && ! $welcome_banner_dismissed ) {
|
||||
$red_bubble_slugs['welcome-banner-active'] = null;
|
||||
return $red_bubble_slugs;
|
||||
} else {
|
||||
return self::alert_if_missing_site_connection( $red_bubble_slugs );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alert slug if the site is missing a site connection
|
||||
*
|
||||
@ -661,7 +681,7 @@ class Initializer {
|
||||
*/
|
||||
public static function alert_if_missing_site_connection( array $red_bubble_slugs ) {
|
||||
if ( ! ( new Connection_Manager() )->is_connected() ) {
|
||||
$red_bubble_slugs[] = self::MISSING_SITE_CONNECTION_NOTIFICATION_KEY;
|
||||
$red_bubble_slugs[ self::MISSING_SITE_CONNECTION_NOTIFICATION_KEY ] = null;
|
||||
}
|
||||
|
||||
return $red_bubble_slugs;
|
||||
|
@ -36,6 +36,7 @@ class Products {
|
||||
'protect' => Products\Protect::class,
|
||||
'videopress' => Products\Videopress::class,
|
||||
'stats' => Products\Stats::class,
|
||||
'ai' => Products\Jetpack_Ai::class,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -155,7 +156,7 @@ class Products {
|
||||
'status' => array(
|
||||
'title' => 'The product status',
|
||||
'type' => 'string',
|
||||
'enum' => array( 'active', 'inactive', 'plugin_absent', 'needs_purchase', 'needs_purchase_or_free', 'error' ),
|
||||
'enum' => array( 'active', 'inactive', 'plugin_absent', 'needs_purchase', 'needs_purchase_or_free', 'needs_first_site_connection', 'user_connection_error', 'site_connection_error' ),
|
||||
),
|
||||
'class' => array(
|
||||
'title' => 'The product class handler',
|
||||
@ -179,6 +180,7 @@ class Products {
|
||||
'protect',
|
||||
'crm',
|
||||
'search',
|
||||
'ai',
|
||||
);
|
||||
|
||||
// Add plugin action links for the core Jetpack plugin.
|
||||
|
@ -190,6 +190,7 @@ class REST_Products {
|
||||
$activate_product_result->add_data( array( 'status' => 400 ) );
|
||||
return $activate_product_result;
|
||||
}
|
||||
set_transient( 'my_jetpack_product_activated', $product_slug, 10 );
|
||||
|
||||
return rest_ensure_response( Products::get_product( $product_slug ), 200 );
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Automattic\Jetpack\My_Jetpack;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Registers the REST routes for Purchases.
|
||||
@ -42,7 +43,7 @@ class REST_Purchases {
|
||||
$is_site_connected = $connection->is_connected();
|
||||
|
||||
if ( ! $is_site_connected ) {
|
||||
return new \WP_Error(
|
||||
return new WP_Error(
|
||||
'not_connected',
|
||||
__( 'Your site is not connected to Jetpack.', 'jetpack-my-jetpack' ),
|
||||
array(
|
||||
@ -68,7 +69,7 @@ class REST_Purchases {
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
if ( is_wp_error( $response ) || empty( $response['body'] ) || 200 !== $response_code ) {
|
||||
return new \WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ? $response_code : 400 ) );
|
||||
return new WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ? $response_code : 400 ) );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $body, 200 );
|
||||
|
@ -8,6 +8,8 @@
|
||||
namespace Automattic\Jetpack\My_Jetpack;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use WP_Error;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Registers the REST routes for Zendesk Chat.
|
||||
@ -56,11 +58,11 @@ class REST_Zendesk_Chat {
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return \WP_Error|true
|
||||
* @return WP_Error|true
|
||||
*/
|
||||
public static function chat_authentication_permissions_callback() {
|
||||
if ( ! get_current_user_id() ) {
|
||||
return new \WP_Error( 'unauthorized', 'You must be logged in to access this resource.', array( 'status' => 401 ) );
|
||||
return new WP_Error( 'unauthorized', 'You must be logged in to access this resource.', array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -69,7 +71,7 @@ class REST_Zendesk_Chat {
|
||||
/**
|
||||
* Gets the chat authentication token.
|
||||
*
|
||||
* @return \WP_Error|object Object: { token: string }
|
||||
* @return WP_Error|WP_REST_Response { token: string }
|
||||
*/
|
||||
public static function get_chat_authentication() {
|
||||
$authentication = get_transient( self::ZENDESK_AUTH_TOKEN );
|
||||
@ -91,7 +93,7 @@ class REST_Zendesk_Chat {
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
|
||||
return new \WP_Error( 'chat_authentication_failed', 'Chat authentication failed', array( 'status' => $response_code ) );
|
||||
return new WP_Error( 'chat_authentication_failed', 'Chat authentication failed', array( 'status' => $response_code ) );
|
||||
}
|
||||
|
||||
set_transient( self::ZENDESK_AUTH_TOKEN, $body, self::TRANSIENT_EXPIRY );
|
||||
@ -102,7 +104,7 @@ class REST_Zendesk_Chat {
|
||||
* Calls `wpcom/v2/presales/chat?group=jp_presales` endpoint.
|
||||
* This endpoint returns whether or not the Jetpack presales chat group is available
|
||||
*
|
||||
* @return \WP_Error/object Object: { is_available: bool }
|
||||
* @return WP_Error|WP_REST_Response { is_available: bool }
|
||||
*/
|
||||
public static function get_chat_availability() {
|
||||
$wpcom_endpoint = '/presales/chat?group=jp_presales';
|
||||
@ -112,7 +114,7 @@ class REST_Zendesk_Chat {
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
|
||||
return new \WP_Error( 'chat_config_data_fetch_failed', 'Chat config data fetch failed', array( 'status' => $response_code ) );
|
||||
return new WP_Error( 'chat_config_data_fetch_failed', 'Chat config data fetch failed', array( 'status' => $response_code ) );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $body, 200 );
|
||||
|
@ -202,10 +202,10 @@ class Wpcom_Products {
|
||||
/**
|
||||
* Populate the pricing array with the discount information.
|
||||
*
|
||||
* @param {object} $product - The product object.
|
||||
* @param {object} $pricing - The pricing array.
|
||||
* @param {float} $price - The price to be discounted.
|
||||
* @return {object} The pricing array with the discount information.
|
||||
* @param object $product - The product object.
|
||||
* @param array $pricing - The pricing array.
|
||||
* @param float $price - The price to be discounted.
|
||||
* @return array The pricing array with the discount information.
|
||||
*/
|
||||
public static function populate_with_discount( $product, $pricing, $price ) {
|
||||
// Check whether the product has a coupon.
|
||||
|
@ -43,6 +43,13 @@ class Anti_Spam extends Product {
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -94,10 +101,11 @@ class Anti_Spam extends Product {
|
||||
|
||||
/**
|
||||
* Determine if the site has an Akismet plan by checking for an API key
|
||||
* Note that some Akismet Plans are free - we're just checking for an API key and don't have the perspective of the plan attached to it here
|
||||
*
|
||||
* @return bool - whether an API key was found
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
public static function has_paid_plan_for_product() {
|
||||
// Check if the site has an API key for Akismet
|
||||
$akismet_api_key = apply_filters( 'akismet_get_api_key', defined( 'WPCOM_API_KEY' ) ? constant( 'WPCOM_API_KEY' ) : get_option( 'wordpress_api_key' ) );
|
||||
$fallback = ! empty( $akismet_api_key );
|
||||
|
@ -51,6 +51,20 @@ class Backup extends Hybrid_Product {
|
||||
*/
|
||||
public static $has_standalone_plugin = true;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = false;
|
||||
|
||||
/**
|
||||
* Whether this product requires a plan to work at all
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $requires_plan = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -183,7 +197,7 @@ class Backup extends Hybrid_Product {
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
public static function has_paid_plan_for_product() {
|
||||
$rewind_data = static::get_state_from_wpcom();
|
||||
if ( is_wp_error( $rewind_data ) ) {
|
||||
return false;
|
||||
@ -224,13 +238,4 @@ class Backup extends Hybrid_Product {
|
||||
return Redirect::get_url( 'my-jetpack-manage-backup' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Product is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_active() {
|
||||
return parent::is_active() && static::has_required_plan();
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\My_Jetpack\Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class responsible for handling the Boost product
|
||||
@ -50,6 +51,13 @@ class Boost extends Product {
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -303,7 +311,7 @@ class Boost extends Product {
|
||||
* Activates the product by installing and activating its plugin
|
||||
*
|
||||
* @param bool|WP_Error $current_result Is the result of the top level activation actions. You probably won't do anything if it is an WP_Error.
|
||||
* @return boolean|\WP_Error
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public static function do_product_specific_activation( $current_result ) {
|
||||
|
||||
|
@ -49,6 +49,13 @@ class Creator extends Product {
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -156,20 +163,6 @@ class Creator extends Product {
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Creator network', 'jetpack-my-jetpack' ),
|
||||
'info' => array(
|
||||
'content' => __(
|
||||
'<p>The creator network is the network of websites either hosted with WordPress.com or self-hosted and connected with Jetpack.</p>
|
||||
<p>Sites that are part of the creator network can gain exposure to new readers. Sites on the Creator plan have enhanced distribution to more areas of the Reader.</p>',
|
||||
'jetpack-my-jetpack'
|
||||
),
|
||||
),
|
||||
'tiers' => array(
|
||||
self::FREE_TIER_SLUG => array( 'included' => true ),
|
||||
self::UPGRADED_TIER_SLUG => array( 'included' => true ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Jetpack Blocks', 'jetpack-my-jetpack' ),
|
||||
'info' => array(
|
||||
@ -330,7 +323,7 @@ class Creator extends Product {
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
public static function has_paid_plan_for_product() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
@ -352,7 +345,6 @@ class Creator extends Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_upgradable() {
|
||||
$has_required_plan = self::has_required_plan();
|
||||
return ! $has_required_plan;
|
||||
return ! self::has_paid_plan_for_product();
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,13 @@ class Crm extends Product {
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -140,12 +147,14 @@ class Crm extends Product {
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
public static function has_paid_plan_for_product() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: check if CRM has a separate plan
|
||||
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
if ( str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\My_Jetpack\Product;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class responsible for handling the Extras product.
|
||||
|
@ -17,7 +17,6 @@ use WP_Error;
|
||||
* Hybrid products are those that may work both as a stand-alone plugin or with the Jetpack plugin.
|
||||
*/
|
||||
abstract class Hybrid_Product extends Product {
|
||||
|
||||
/**
|
||||
* All hybrid products have a standalone plugin
|
||||
*
|
||||
@ -52,18 +51,6 @@ abstract class Hybrid_Product extends Product {
|
||||
return parent::is_plugin_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Jetpack module is active only if a module_name is defined
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_module_active() {
|
||||
if ( ! empty( static::$module_name ) ) {
|
||||
return ( new Modules() )->is_active( static::$module_name );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Product is active
|
||||
*
|
||||
@ -118,9 +105,13 @@ abstract class Hybrid_Product extends Product {
|
||||
}
|
||||
}
|
||||
|
||||
// Only activate the module if the plan supports it
|
||||
// We don't want to throw an error for a missing plan here since we try activation before purchase
|
||||
if ( static::has_required_plan() && ! empty( static::$module_name ) ) {
|
||||
if ( ! empty( static::$module_name ) ) {
|
||||
// Only activate the module if the plan supports it
|
||||
// We don't want to throw an error for a missing plan here since we try activation before purchase
|
||||
if ( static::$requires_plan && ! static::has_any_plan_for_product() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$module_activation = ( new Modules() )->activate( static::$module_name, false, false );
|
||||
|
||||
if ( ! $module_activation ) {
|
||||
@ -149,7 +140,7 @@ abstract class Hybrid_Product extends Product {
|
||||
* Activate the module as well, if the user has a plan
|
||||
* or the product does not require a plan to work
|
||||
*/
|
||||
if ( static::has_required_plan() && isset( static::$module_name ) ) {
|
||||
if ( static::has_any_plan_for_product() && isset( static::$module_name ) ) {
|
||||
$module_activation = ( new Modules() )->activate( static::$module_name, false, false );
|
||||
|
||||
if ( ! $module_activation ) {
|
||||
|
@ -8,14 +8,19 @@
|
||||
namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\My_Jetpack\Initializer;
|
||||
use Automattic\Jetpack\My_Jetpack\Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
use WP_Post;
|
||||
|
||||
/**
|
||||
* Class responsible for handling the Jetpack AI product
|
||||
*/
|
||||
class Jetpack_Ai extends Product {
|
||||
|
||||
const CURRENT_TIER_SLUG = 'free';
|
||||
const UPGRADED_TIER_SLUG = 'upgraded';
|
||||
|
||||
/**
|
||||
* The product slug
|
||||
*
|
||||
@ -23,6 +28,13 @@ class Jetpack_Ai extends Product {
|
||||
*/
|
||||
public static $slug = 'jetpack-ai';
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the Product info for the API
|
||||
*
|
||||
@ -75,6 +87,84 @@ class Jetpack_Ai extends Product {
|
||||
return 'Jetpack AI';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product's available tiers
|
||||
*
|
||||
* @return string[] Slugs of the available tiers
|
||||
*/
|
||||
public static function get_tiers() {
|
||||
return array(
|
||||
self::UPGRADED_TIER_SLUG,
|
||||
self::CURRENT_TIER_SLUG,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized comparison of free vs upgraded features
|
||||
*
|
||||
* @return array[] Protect features comparison
|
||||
*/
|
||||
public static function get_features_by_tier() {
|
||||
$current_tier = self::get_current_usage_tier();
|
||||
$current_description = 0 === $current_tier
|
||||
? __( 'Up to 20 requests', 'jetpack-my-jetpack' )
|
||||
/* translators: number of requests */
|
||||
: sprintf( __( 'Up to %d requests per month', 'jetpack-my-jetpack' ), $current_tier );
|
||||
$next_tier = self::get_next_usage_tier();
|
||||
$next_description = $next_tier === null
|
||||
? __( 'Let\'s get in touch', 'jetpack-my-jetpack' )
|
||||
/* translators: number of requests */
|
||||
: sprintf( __( 'Up to %d requests per month', 'jetpack-my-jetpack' ), $next_tier );
|
||||
|
||||
return array(
|
||||
array(
|
||||
'name' => __( 'Number of requests', 'jetpack-my-jetpack' ),
|
||||
'info' => array(
|
||||
'title' => __( 'Requests', 'jetpack-my-jetpack' ),
|
||||
'content' => __( 'Increase your monthly request limit. Upgrade now and have the option to further increase your requests with additional upgrades.', 'jetpack-my-jetpack' ),
|
||||
),
|
||||
'tiers' => array(
|
||||
self::CURRENT_TIER_SLUG => array(
|
||||
'included' => true,
|
||||
'description' => $current_description,
|
||||
),
|
||||
self::UPGRADED_TIER_SLUG => array(
|
||||
'included' => true,
|
||||
'description' => $next_description,
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Generate and edit content', 'jetpack-my-jetpack' ),
|
||||
'tiers' => array(
|
||||
self::CURRENT_TIER_SLUG => array( 'included' => true ),
|
||||
self::UPGRADED_TIER_SLUG => array( 'included' => true ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Build forms from prompts', 'jetpack-my-jetpack' ),
|
||||
'tiers' => array(
|
||||
self::CURRENT_TIER_SLUG => array( 'included' => true ),
|
||||
self::UPGRADED_TIER_SLUG => array( 'included' => true ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Get feedback on posts', 'jetpack-my-jetpack' ),
|
||||
'tiers' => array(
|
||||
self::CURRENT_TIER_SLUG => array( 'included' => true ),
|
||||
self::UPGRADED_TIER_SLUG => array( 'included' => true ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Generate featured images', 'jetpack-my-jetpack' ),
|
||||
'tiers' => array(
|
||||
self::CURRENT_TIER_SLUG => array( 'included' => true ),
|
||||
self::UPGRADED_TIER_SLUG => array( 'included' => true ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current usage tier
|
||||
*
|
||||
@ -89,7 +179,7 @@ class Jetpack_Ai extends Product {
|
||||
|
||||
// Bail early if it's not possible to fetch the feature data.
|
||||
if ( is_wp_error( $info ) ) {
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$current_tier = isset( $info['current-tier']['value'] ) ? $info['current-tier']['value'] : null;
|
||||
@ -103,7 +193,7 @@ class Jetpack_Ai extends Product {
|
||||
* @return int
|
||||
*/
|
||||
public static function get_next_usage_tier() {
|
||||
if ( ! self::is_site_connected() || ! self::has_required_plan() ) {
|
||||
if ( ! self::is_site_connected() || ! self::has_paid_plan_for_product() ) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@ -126,7 +216,7 @@ class Jetpack_Ai extends Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'Experimental tool to add AI to your editor', 'jetpack-my-jetpack' );
|
||||
return __( 'The most powerful AI tool for WordPress', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,10 +291,14 @@ class Jetpack_Ai extends Product {
|
||||
/**
|
||||
* Get the product pricing details by tier
|
||||
*
|
||||
* @param int $tier The usage tier.
|
||||
* @param int|null $tier The usage tier.
|
||||
* @return array Pricing details
|
||||
*/
|
||||
public static function get_pricing_for_ui_by_usage_tier( $tier ) {
|
||||
if ( $tier === null ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$product = Wpcom_Products::get_product( static::get_wpcom_product_slug() );
|
||||
|
||||
if ( empty( $product ) ) {
|
||||
@ -266,14 +360,34 @@ class Jetpack_Ai extends Product {
|
||||
* @return array Pricing details
|
||||
*/
|
||||
public static function get_pricing_for_ui() {
|
||||
$next_tier = self::get_next_usage_tier();
|
||||
$next_tier = self::get_next_usage_tier();
|
||||
$current_tier = self::get_current_usage_tier();
|
||||
$current_call_to_action = $current_tier === 0
|
||||
? __( 'Continue for free', 'jetpack-my-jetpack' )
|
||||
: __( 'I\'m fine with my plan, thanks', 'jetpack-my-jetpack' );
|
||||
$next_call_to_action = $next_tier === null
|
||||
? __( 'Contact Us', 'jetpack-my-jetpack' )
|
||||
: __( 'Upgrade', 'jetpack-my-jetpack' );
|
||||
|
||||
return array_merge(
|
||||
array(
|
||||
'available' => true,
|
||||
'wpcom_product_slug' => static::get_wpcom_product_slug(),
|
||||
return array(
|
||||
'tiers' => array(
|
||||
self::CURRENT_TIER_SLUG => array_merge(
|
||||
self::get_pricing_for_ui_by_usage_tier( $current_tier ),
|
||||
array(
|
||||
'available' => true,
|
||||
'is_free' => true,
|
||||
'call_to_action' => $current_call_to_action,
|
||||
)
|
||||
),
|
||||
self::UPGRADED_TIER_SLUG => array_merge(
|
||||
self::get_pricing_for_ui_by_usage_tier( $next_tier ),
|
||||
array(
|
||||
'wpcom_product_slug' => static::get_wpcom_product_slug(),
|
||||
'quantity' => $next_tier,
|
||||
'call_to_action' => $next_call_to_action,
|
||||
)
|
||||
),
|
||||
),
|
||||
self::get_pricing_for_ui_by_usage_tier( $next_tier )
|
||||
);
|
||||
}
|
||||
|
||||
@ -305,12 +419,23 @@ class Jetpack_Ai extends Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the product
|
||||
* Checks whether the site has a paid plan for this product
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
return static::does_site_have_feature( 'ai-assistant' );
|
||||
public static function has_paid_plan_for_product() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
if ( str_contains( $purchase->product_slug, 'jetpack_ai' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,24 +444,60 @@ class Jetpack_Ai extends Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_upgradable() {
|
||||
$has_required_plan = self::has_required_plan();
|
||||
$current_tier = self::get_current_usage_tier();
|
||||
$has_ai_feature = static::does_site_have_feature( 'ai-assistant' );
|
||||
$current_tier = self::get_current_usage_tier();
|
||||
|
||||
// Mark as not upgradable if user is on unlimited tier or does not have any plan.
|
||||
if ( ! $has_required_plan || null === $current_tier || 1 === $current_tier ) {
|
||||
if ( ! $has_ai_feature || null === $current_tier || 1 === $current_tier ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after purchasing the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return '/wp-admin/admin.php?page=my-jetpack#/jetpack-ai';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after activating the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_activation_url() {
|
||||
return '/wp-admin/admin.php?page=my-jetpack#/jetpack-ai';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the user manages the product
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_manage_url() {
|
||||
return '';
|
||||
return '/wp-admin/admin.php?page=my-jetpack#/add-jetpack-ai';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the plugin is installed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_plugin_installed() {
|
||||
return self::is_jetpack_plugin_installed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the plugin is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_plugin_active() {
|
||||
return (bool) static::is_jetpack_plugin_active();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,4 +544,61 @@ class Jetpack_Ai extends Product {
|
||||
private static function is_site_connected() {
|
||||
return ( new Connection_Manager() )->is_connected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the user manages the product
|
||||
*
|
||||
* NOTE: this method is the only thing that resembles an initialization for the product.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function extend_plugin_action_links() {
|
||||
add_action( 'admin_enqueue_scripts', array( static::class, 'admin_enqueue_scripts' ) );
|
||||
add_filter( 'default_content', array( static::class, 'add_ai_block' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the AI Assistant script
|
||||
*
|
||||
* The script is just a global variable used for the nonce, needed for the create post link.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
wp_register_script(
|
||||
'my_jetpack_ai_app',
|
||||
false,
|
||||
array(),
|
||||
Initializer::PACKAGE_VERSION,
|
||||
array( 'in_footer' => true )
|
||||
);
|
||||
wp_localize_script(
|
||||
'my_jetpack_ai_app',
|
||||
'jetpackAi',
|
||||
array(
|
||||
'nonce' => wp_create_nonce( 'ai-assistant-content-nonce' ),
|
||||
)
|
||||
);
|
||||
wp_enqueue_script( 'my_jetpack_ai_app' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add AI block to the post content
|
||||
*
|
||||
* Used only from the link on the product page, the filter will insert an AI Assistant block in the post content.
|
||||
*
|
||||
* @param string $content The post content.
|
||||
* @param WP_Post $post The post object.
|
||||
* @return string
|
||||
*/
|
||||
public static function add_ai_block( $content, WP_Post $post ) {
|
||||
if ( isset( $_GET['use_ai_block'] ) && isset( $_GET['_wpnonce'] )
|
||||
&& wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'ai-assistant-content-nonce' )
|
||||
&& current_user_can( 'edit_post', $post->ID )
|
||||
&& '' === $content
|
||||
) {
|
||||
return '<!-- wp:jetpack/ai-assistant /-->';
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Automattic\Jetpack\My_Jetpack;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Modules;
|
||||
use Automattic\Jetpack\Plugins_Installer;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
@ -25,6 +26,13 @@ abstract class Product {
|
||||
*/
|
||||
public static $slug = null;
|
||||
|
||||
/**
|
||||
* The Jetpack module name, if any.
|
||||
*
|
||||
* @var ?string
|
||||
*/
|
||||
public static $module_name = null;
|
||||
|
||||
/**
|
||||
* The filename (id) of the plugin associated with this product. Can be a string with a single value or a list of possible values
|
||||
*
|
||||
@ -77,6 +85,21 @@ abstract class Product {
|
||||
*/
|
||||
public static $has_standalone_plugin = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = false;
|
||||
|
||||
/**
|
||||
* Whether the product requires a plan to run
|
||||
* The plan could be paid or free
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $requires_plan = false;
|
||||
|
||||
/**
|
||||
* Get the plugin slug
|
||||
*
|
||||
@ -141,13 +164,15 @@ abstract class Product {
|
||||
'pricing_for_ui' => static::get_pricing_for_ui(),
|
||||
'is_bundle' => static::is_bundle_product(),
|
||||
'is_plugin_active' => static::is_plugin_active(),
|
||||
'is_upgradable' => static::is_upgradable(),
|
||||
'is_upgradable_by_bundle' => static::is_upgradable_by_bundle(),
|
||||
'supported_products' => static::get_supported_products(),
|
||||
'wpcom_product_slug' => static::get_wpcom_product_slug(),
|
||||
'requires_user_connection' => static::$requires_user_connection,
|
||||
'has_required_plan' => static::has_required_plan(),
|
||||
'has_any_plan_for_product' => static::has_any_plan_for_product(),
|
||||
'has_free_plan_for_product' => static::has_free_plan_for_product(),
|
||||
'has_paid_plan_for_product' => static::has_paid_plan_for_product(),
|
||||
'has_required_tier' => static::has_required_tier(),
|
||||
'has_free_offering' => static::$has_free_offering,
|
||||
'manage_url' => static::get_manage_url(),
|
||||
'purchase_url' => static::get_purchase_url(),
|
||||
'post_activation_url' => static::get_post_activation_url(),
|
||||
@ -332,19 +357,6 @@ abstract class Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the product
|
||||
*
|
||||
* Returns true if it supports. Return false if a purchase is still required.
|
||||
*
|
||||
* Free products will always return true.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the site has a paid plan for the product
|
||||
* This ignores free products, it only checks if there is a purchase that supports the product
|
||||
@ -352,19 +364,26 @@ abstract class Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_paid_plan_for_product() {
|
||||
// TODO: this is not always the same.
|
||||
// There should be checks on each individual product class for paid plans if the product has a free offering
|
||||
// For products with no free offering, checking has_required_plan works fine
|
||||
return static::has_required_plan();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the tiers
|
||||
* Checks whether the site has a free plan for the product
|
||||
* Note, this should not return true if a product does not have a WPCOM plan (ex: search free, Akismet Free, stats free)
|
||||
*
|
||||
* @return array Key/value pairs of tier slugs and whether they are supported or not.
|
||||
* @return false
|
||||
*/
|
||||
public static function has_required_tier() {
|
||||
return array();
|
||||
public static function has_free_plan_for_product() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the site has any WPCOM plan for a product (paid or free)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_any_plan_for_product() {
|
||||
return static::has_paid_plan_for_product() || static::has_free_plan_for_product();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,25 +453,38 @@ abstract class Product {
|
||||
$status = 'active';
|
||||
// We only consider missing site & user connection an error when the Product is active.
|
||||
if ( static::$requires_site_connection && ! ( new Connection_Manager() )->is_connected() ) {
|
||||
$status = 'error';
|
||||
} elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
|
||||
$status = 'error';
|
||||
} elseif ( static::is_upgradable() ) {
|
||||
// Upgradable plans should ignore whether or not they have the required plan.
|
||||
$status = 'can_upgrade';
|
||||
} elseif ( ! static::has_required_plan() ) { // We need needs_purchase here as well because some products we consider active without the required plan.
|
||||
if ( static::has_trial_support() ) {
|
||||
$status = 'needs_purchase_or_free';
|
||||
// Site has never been connected before
|
||||
if ( ! \Jetpack_Options::get_option( 'id' ) ) {
|
||||
$status = 'needs_first_site_connection';
|
||||
} else {
|
||||
$status = 'needs_purchase';
|
||||
$status = 'site_connection_error';
|
||||
}
|
||||
} elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
|
||||
$status = 'user_connection_error';
|
||||
} elseif ( static::is_upgradable() ) {
|
||||
$status = 'can_upgrade';
|
||||
}
|
||||
} elseif ( ! static::has_required_plan() ) {
|
||||
if ( static::has_trial_support() ) {
|
||||
$status = 'needs_purchase_or_free';
|
||||
} else {
|
||||
$status = 'needs_purchase';
|
||||
// Check specifically for inactive modules, which will prevent a product from being active
|
||||
} elseif ( static::$module_name && ! static::is_module_active() ) {
|
||||
$status = 'module_disabled';
|
||||
// If there is not a plan associated with the disabled module, encourage a plan first
|
||||
// Getting a plan set up should help resolve any connection issues
|
||||
// However if the standalone plugin for this product is active, then we will defer to showing errors that prevent the module from being active
|
||||
// This is because if a standalone plugin is installed, we expect the product to not show as "inactive" on My Jetpack
|
||||
if ( static::$requires_plan || ( ! static::has_any_plan_for_product() && static::$has_standalone_plugin && ! self::is_plugin_active() ) ) {
|
||||
$status = static::$has_free_offering ? 'needs_purchase_or_free' : 'needs_purchase';
|
||||
} elseif ( static::$requires_site_connection && ! ( new Connection_Manager() )->is_connected() ) {
|
||||
// Site has never been connected before
|
||||
if ( ! \Jetpack_Options::get_option( 'id' ) ) {
|
||||
$status = 'needs_first_site_connection';
|
||||
} else {
|
||||
$status = 'site_connection_error';
|
||||
}
|
||||
} elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
|
||||
$status = 'user_connection_error';
|
||||
}
|
||||
} elseif ( ! static::has_any_plan_for_product() ) {
|
||||
$status = static::$has_free_offering ? 'needs_purchase_or_free' : 'needs_purchase';
|
||||
} else {
|
||||
$status = 'inactive';
|
||||
}
|
||||
@ -465,7 +497,7 @@ abstract class Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_active() {
|
||||
return static::is_plugin_active() && static::has_required_plan();
|
||||
return static::is_plugin_active() && ( static::has_any_plan_for_product() || ( ! static::$requires_plan && static::$has_free_offering ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -504,6 +536,18 @@ abstract class Product {
|
||||
return Plugins_Installer::is_plugin_active( static::get_installed_plugin_filename( 'jetpack' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Jetpack module is active only if a module_name is defined
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_module_active() {
|
||||
if ( static::$module_name ) {
|
||||
return ( new Modules() )->is_active( static::$module_name );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the plugin
|
||||
*
|
||||
|
@ -54,6 +54,13 @@ class Protect extends Product {
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
|
@ -47,6 +47,20 @@ class Search extends Hybrid_Product {
|
||||
*/
|
||||
public static $has_standalone_plugin = true;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Whether this product requires a plan to work at all
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $requires_plan = true;
|
||||
|
||||
/**
|
||||
* The filename (id) of the plugin associated with this product.
|
||||
*
|
||||
@ -63,7 +77,7 @@ class Search extends Hybrid_Product {
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
public static $requires_user_connection = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
@ -286,17 +300,46 @@ class Search extends Hybrid_Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan of the site already supports the product
|
||||
* Checks if the site purchases contain a paid search plan
|
||||
*
|
||||
* Returns true if it supports. Return false if a purchase is still required.
|
||||
*
|
||||
* Free products will always return true.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
$search_state = static::get_state_from_wpcom();
|
||||
return ! empty( $search_state->supports_search ) || ! empty( $search_state->supports_instant_search );
|
||||
public static function has_paid_plan_for_product() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
// Search is available as standalone product and as part of the Complete plan.
|
||||
if (
|
||||
( str_contains( $purchase->product_slug, 'jetpack_search' ) && ! str_contains( $purchase->product_slug, 'jetpack_search_free' ) ) ||
|
||||
str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the site purchases contain a free search plan
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_free_plan_for_product() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
if ( str_contains( $purchase->product_slug, 'jetpack_search_free' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +55,13 @@ class Social extends Hybrid_Product {
|
||||
'jetpack-social-dev/jetpack-social.php',
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -142,7 +149,7 @@ class Social extends Hybrid_Product {
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
public static function has_paid_plan_for_product() {
|
||||
// For atomic sites, do a feature check to see if the republicize feature is available
|
||||
// This feature is available by default on all Jetpack sites
|
||||
if ( ( new Host() )->is_woa_site() ) {
|
||||
|
@ -59,6 +59,13 @@ class Stats extends Module_Product {
|
||||
*/
|
||||
public static $has_standalone_plugin = false;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
|
@ -61,6 +61,13 @@ class Videopress extends Hybrid_Product {
|
||||
*/
|
||||
public static $has_standalone_plugin = true;
|
||||
|
||||
/**
|
||||
* Whether this product has a free offering
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_free_offering = true;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
@ -167,11 +174,22 @@ class Videopress extends Hybrid_Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the product
|
||||
* Checks whether the site has a paid plan for this product
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
return static::does_site_have_feature( 'videopress' );
|
||||
public static function has_paid_plan_for_product() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
if ( str_contains( $purchase->product_slug, 'jetpack_videopress' ) || str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user