updated plugin Jetpack Protect
version 2.1.0
This commit is contained in:
@ -9,6 +9,9 @@ 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;
|
||||
use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
@ -34,7 +37,7 @@ class Initializer {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PACKAGE_VERSION = '4.6.0';
|
||||
const PACKAGE_VERSION = '4.17.0';
|
||||
|
||||
/**
|
||||
* HTML container ID for the IDC screen on My Jetpack page.
|
||||
@ -52,6 +55,17 @@ class Initializer {
|
||||
'jetpack-search',
|
||||
);
|
||||
|
||||
const MY_JETPACK_SITE_INFO_TRANSIENT_KEY = 'my-jetpack-site-info';
|
||||
|
||||
const MISSING_SITE_CONNECTION_NOTIFICATION_KEY = 'missing-site-connection';
|
||||
|
||||
/**
|
||||
* Holds info/data about the site (from the /sites/%d endpoint)
|
||||
*
|
||||
* @var stdClass Object
|
||||
*/
|
||||
public static $site_info;
|
||||
|
||||
/**
|
||||
* Initialize My Jetpack
|
||||
*
|
||||
@ -72,6 +86,10 @@ class Initializer {
|
||||
Licensing::instance()->initialize();
|
||||
}
|
||||
|
||||
// Initialize Boost Speed Score
|
||||
$boost_modules = Jetpack_Boost_Modules::init();
|
||||
new Speed_Score( $boost_modules, 'jetpack-my-jetpack' );
|
||||
|
||||
// Add custom WP REST API endoints.
|
||||
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
|
||||
|
||||
@ -85,6 +103,8 @@ class Initializer {
|
||||
);
|
||||
|
||||
add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
|
||||
// This is later than the admin-ui package, which runs on 1000
|
||||
add_action( 'admin_init', array( __CLASS__, 'maybe_show_red_bubble' ), 1001 );
|
||||
|
||||
// Sets up JITMS.
|
||||
JITM::configure();
|
||||
@ -142,6 +162,7 @@ class Initializer {
|
||||
* @return void
|
||||
*/
|
||||
public static function admin_init() {
|
||||
self::$site_info = self::get_site_info();
|
||||
add_filter( 'identity_crisis_container_id', array( static::class, 'get_idc_container_id' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
|
||||
// Product statuses are constantly changing, so we never want to cache the page.
|
||||
@ -177,40 +198,53 @@ class Initializer {
|
||||
'textdomain' => 'jetpack-my-jetpack',
|
||||
)
|
||||
);
|
||||
$modules = new Modules();
|
||||
$modules = new Modules();
|
||||
$connection = new Connection_Manager();
|
||||
$speed_score_history = new Speed_Score_History( wp_parse_url( get_site_url(), PHP_URL_HOST ) );
|
||||
wp_localize_script(
|
||||
'my_jetpack_main_app',
|
||||
'myJetpackInitialState',
|
||||
array(
|
||||
'products' => array(
|
||||
'products' => array(
|
||||
'items' => Products::get_products(),
|
||||
),
|
||||
'purchases' => array(
|
||||
'purchases' => array(
|
||||
'items' => array(),
|
||||
),
|
||||
'plugins' => Plugins_Installer::get_plugins(),
|
||||
'myJetpackUrl' => admin_url( 'admin.php?page=my-jetpack' ),
|
||||
'myJetpackCheckoutUri' => 'admin.php?page=my-jetpack',
|
||||
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
|
||||
'siteSuffix' => ( new Status() )->get_site_suffix(),
|
||||
'blogID' => Connection_Manager::get_site_id( true ),
|
||||
'myJetpackVersion' => self::PACKAGE_VERSION,
|
||||
'myJetpackFlags' => self::get_my_jetpack_flags(),
|
||||
'fileSystemWriteAccess' => self::has_file_system_write_access(),
|
||||
'loadAddLicenseScreen' => self::is_licensing_ui_enabled(),
|
||||
'adminUrl' => esc_url( admin_url() ),
|
||||
'IDCContainerID' => static::get_idc_container_id(),
|
||||
'userIsAdmin' => current_user_can( 'manage_options' ),
|
||||
'userIsNewToJetpack' => self::is_jetpack_user_new(),
|
||||
'isStatsModuleActive' => $modules->is_active( 'stats' ),
|
||||
'isUserFromKnownHost' => self::is_user_from_known_host(),
|
||||
'welcomeBanner' => array(
|
||||
'plugins' => Plugins_Installer::get_plugins(),
|
||||
'myJetpackUrl' => admin_url( 'admin.php?page=my-jetpack' ),
|
||||
'myJetpackCheckoutUri' => admin_url( 'admin.php?page=my-jetpack' ),
|
||||
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
|
||||
'siteSuffix' => ( new Status() )->get_site_suffix(),
|
||||
'blogID' => Connection_Manager::get_site_id( true ),
|
||||
'myJetpackVersion' => self::PACKAGE_VERSION,
|
||||
'myJetpackFlags' => self::get_my_jetpack_flags(),
|
||||
'fileSystemWriteAccess' => self::has_file_system_write_access(),
|
||||
'loadAddLicenseScreen' => self::is_licensing_ui_enabled(),
|
||||
'adminUrl' => esc_url( admin_url() ),
|
||||
'IDCContainerID' => static::get_idc_container_id(),
|
||||
'userIsAdmin' => current_user_can( 'manage_options' ),
|
||||
'userIsNewToJetpack' => self::is_jetpack_user_new(),
|
||||
'lifecycleStats' => array(
|
||||
'jetpackPlugins' => self::get_installed_jetpack_plugins(),
|
||||
'isSiteConnected' => $connection->is_connected(),
|
||||
'isUserConnected' => $connection->is_user_connected(),
|
||||
'purchases' => self::get_purchases(),
|
||||
'modules' => self::get_active_modules(),
|
||||
),
|
||||
'redBubbleAlerts' => self::get_red_bubble_alerts(),
|
||||
'isStatsModuleActive' => $modules->is_active( 'stats' ),
|
||||
'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(
|
||||
'jetpackManage' => array(
|
||||
'isEnabled' => Jetpack_Manage::could_use_jp_manage(),
|
||||
'isAgencyAccount' => Jetpack_Manage::is_agency_account(),
|
||||
),
|
||||
'latestBoostSpeedScores' => $speed_score_history->latest(),
|
||||
)
|
||||
);
|
||||
|
||||
@ -232,6 +266,63 @@ class Initializer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product slugs of the active purchases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_purchases() {
|
||||
$purchases = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_map(
|
||||
function ( $purchase ) {
|
||||
return $purchase->product_slug;
|
||||
},
|
||||
$purchases
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get installed Jetpack plugins
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_installed_jetpack_plugins() {
|
||||
$plugin_slugs = array_keys( Plugins_Installer::get_plugins() );
|
||||
$plugin_slugs = array_map(
|
||||
static function ( $slug ) {
|
||||
$parts = explode( '/', $slug );
|
||||
if ( empty( $parts ) ) {
|
||||
return '';
|
||||
}
|
||||
// Return the last segment of the filepath without the PHP extension
|
||||
return str_replace( '.php', '', $parts[ count( $parts ) - 1 ] );
|
||||
},
|
||||
$plugin_slugs
|
||||
);
|
||||
|
||||
return array_values( array_intersect( self::JETPACK_PLUGIN_SLUGS, $plugin_slugs ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active modules (except ones enabled by default)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_active_modules() {
|
||||
$modules = new Modules();
|
||||
$active_modules = $modules->get_active();
|
||||
|
||||
// if the Jetpack plugin is active, filter out the modules that are active by default
|
||||
if ( class_exists( 'Jetpack' ) && ! empty( $active_modules ) ) {
|
||||
$active_modules = array_diff( $active_modules, Jetpack::get_default_modules() );
|
||||
}
|
||||
return $active_modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current user is "new" to Jetpack
|
||||
* This is used to vary some messaging in My Jetpack
|
||||
@ -305,8 +396,8 @@ class Initializer {
|
||||
*/
|
||||
public static function get_my_jetpack_flags() {
|
||||
$flags = array(
|
||||
'videoPressStats' => Jetpack_Constants::is_true( 'JETPACK_MY_JETPACK_VIDEOPRESS_STATS_ENABLED' ),
|
||||
'showJetpackStatsCard' => class_exists( 'Jetpack' ),
|
||||
'videoPressStats' => Jetpack_Constants::is_true( 'JETPACK_MY_JETPACK_VIDEOPRESS_STATS_ENABLED' ),
|
||||
'showFullJetpackStatsCard' => class_exists( 'Jetpack' ),
|
||||
);
|
||||
|
||||
return $flags;
|
||||
@ -376,6 +467,11 @@ class Initializer {
|
||||
$should = false;
|
||||
}
|
||||
|
||||
// All options presented in My Jetpack require a connection to WordPress.com.
|
||||
if ( ( new Status() )->is_offline_mode() ) {
|
||||
$should = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows filtering whether My Jetpack should be initialized.
|
||||
*
|
||||
@ -406,6 +502,56 @@ class Initializer {
|
||||
return rest_ensure_response( $body, 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the self::$site_info var with site data from the /sites/%d endpoint
|
||||
*
|
||||
* @return Object|WP_Error
|
||||
*/
|
||||
public static function get_site_info() {
|
||||
static $site_info = null;
|
||||
|
||||
if ( $site_info !== null ) {
|
||||
return $site_info;
|
||||
}
|
||||
|
||||
// Check for a cached value before doing lookup
|
||||
$stored_site_info = get_transient( self::MY_JETPACK_SITE_INFO_TRANSIENT_KEY );
|
||||
if ( $stored_site_info !== false ) {
|
||||
return $stored_site_info;
|
||||
}
|
||||
|
||||
$response = self::get_site();
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
$site_info = $response->data;
|
||||
set_transient( self::MY_JETPACK_SITE_INFO_TRANSIENT_KEY, $site_info, DAY_IN_SECONDS );
|
||||
|
||||
return $site_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a site has been determined "commercial" or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_commercial_site() {
|
||||
if ( is_wp_error( self::$site_info ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return empty( self::$site_info->options->is_commercial ) ? false : self::$site_info->options->is_commercial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if site is registered (has been connected before).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_registered() {
|
||||
return (bool) \Jetpack_Options::get_option( 'id' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the welcome banner.
|
||||
*
|
||||
@ -465,4 +611,59 @@ class Initializer {
|
||||
public static function get_idc_container_id() {
|
||||
return static::IDC_CONTAINER_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally append the red bubble notification to the "Jetpack" menu item if there are alerts to show
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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' ) );
|
||||
$red_bubble_alerts = self::get_red_bubble_alerts();
|
||||
|
||||
// The Jetpack menu item should be on index 3
|
||||
if (
|
||||
! empty( $red_bubble_alerts ) &&
|
||||
is_countable( $red_bubble_alerts ) &&
|
||||
isset( $menu[3] ) &&
|
||||
$menu[3][0] === 'Jetpack'
|
||||
) {
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$menu[3][0] .= sprintf( ' <span class="awaiting-mod">%d</span>', count( $red_bubble_alerts ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all possible alerts that we might use a red bubble notification for
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_red_bubble_alerts() {
|
||||
static $red_bubble_alerts = array();
|
||||
|
||||
// using a static cache since we call this function more than once in the class
|
||||
if ( ! empty( $red_bubble_alerts ) ) {
|
||||
return $red_bubble_alerts;
|
||||
}
|
||||
// go find the alerts
|
||||
$red_bubble_alerts = apply_filters( 'my_jetpack_red_bubble_notification_slugs', $red_bubble_alerts );
|
||||
|
||||
return $red_bubble_alerts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alert slug if the site is missing a site connection
|
||||
*
|
||||
* @param array $red_bubble_slugs - slugs that describe the reasons the red bubble is showing.
|
||||
* @return array
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
return $red_bubble_slugs;
|
||||
}
|
||||
}
|
||||
|
@ -174,8 +174,17 @@ class Products {
|
||||
'backup',
|
||||
'boost',
|
||||
'crm',
|
||||
'videopress', // we use videopress here to add the plugin action to the Jetpack plugin itself
|
||||
'videopress',
|
||||
'social',
|
||||
'protect',
|
||||
'crm',
|
||||
'search',
|
||||
);
|
||||
|
||||
// Add plugin action links for the core Jetpack plugin.
|
||||
Product::extend_core_plugin_action_links();
|
||||
|
||||
// Add plugin action links to standalone products.
|
||||
foreach ( $products as $product ) {
|
||||
$class_name = self::get_product_class( $product );
|
||||
$class_name::extend_plugin_action_links();
|
||||
|
@ -237,17 +237,6 @@ class REST_Products {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the product is not hybrid, there is no need to deal with a standalone plugin.
|
||||
*/
|
||||
if ( ! is_subclass_of( $product['class'], Hybrid_Product::class ) ) {
|
||||
return new \WP_Error(
|
||||
'not_hybrid',
|
||||
__( 'This product does not have a standalone plugin to install', 'jetpack-my-jetpack' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$install_product_result = call_user_func( array( $product['class'], 'install_and_activate_standalone' ) );
|
||||
if ( is_wp_error( $install_product_result ) ) {
|
||||
$install_product_result->add_data( array( 'status' => 400 ) );
|
||||
|
@ -44,21 +44,21 @@ class Anti_Spam extends Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Akismet Anti-spam', 'jetpack-my-jetpack' );
|
||||
return 'Akismet Anti-spam';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Akismet Anti-spam', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Akismet Anti-spam';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,21 +52,21 @@ class Backup extends Hybrid_Product {
|
||||
public static $has_standalone_plugin = true;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'VaultPress Backup', 'jetpack-my-jetpack' );
|
||||
return 'VaultPress Backup';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack VaultPress Backup', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack VaultPress Backup';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,6 +129,13 @@ class Backup extends Hybrid_Product {
|
||||
return 'jetpack_backup_t1_yearly';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the user should be redirected after checkout
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return self::get_manage_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product princing details
|
||||
*
|
||||
@ -209,10 +216,12 @@ class Backup extends Hybrid_Product {
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_manage_url() {
|
||||
if ( static::is_jetpack_plugin_active() ) {
|
||||
return Redirect::get_url( 'my-jetpack-manage-backup' );
|
||||
} elseif ( static::is_plugin_active() ) {
|
||||
// check standalone first
|
||||
if ( static::is_standalone_plugin_active() ) {
|
||||
return admin_url( 'admin.php?page=jetpack-backup' );
|
||||
// otherwise, check for the main Jetpack plugin
|
||||
} elseif ( static::is_jetpack_plugin_active() ) {
|
||||
return Redirect::get_url( 'my-jetpack-manage-backup' );
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,15 +233,4 @@ class Backup extends Hybrid_Product {
|
||||
public static function is_active() {
|
||||
return parent::is_active() && static::has_required_plan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the user should be redirected after checkout
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
if ( static::is_jetpack_plugin_active() ) {
|
||||
return 'admin.php?page=jetpack#/recommendations';
|
||||
} elseif ( static::is_plugin_active() ) {
|
||||
return 'admin.php?page=jetpack-backup';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,21 +51,21 @@ class Boost extends Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Boost', 'jetpack-my-jetpack' );
|
||||
return 'Boost';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Boost', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Boost';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +74,7 @@ class Boost extends Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'The easiest speed optimization plugin for WordPress', 'jetpack-my-jetpack' );
|
||||
return __( 'Speed up your site in seconds', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,6 +237,15 @@ class Boost extends Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after purchasing the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return self::get_manage_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product princing details
|
||||
*
|
||||
@ -260,6 +269,27 @@ class Boost extends Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the product
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
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 ) {
|
||||
// Boost is available as standalone bundle and as part of the Complete plan.
|
||||
if ( strpos( $purchase->product_slug, 'jetpack_boost' ) !== false || str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the user manages the product
|
||||
*
|
||||
|
@ -50,21 +50,21 @@ class Creator extends Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Creator', 'jetpack-my-jetpack' );
|
||||
return 'Creator';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Creator', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Creator';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@
|
||||
namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\My_Jetpack\Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
|
||||
/**
|
||||
* Class responsible for handling the CRM product
|
||||
@ -26,7 +27,10 @@ class Crm extends Product {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $plugin_filename = 'zero-bs-crm/ZeroBSCRM.php';
|
||||
public static $plugin_filename = array(
|
||||
'zero-bs-crm/ZeroBSCRM.php',
|
||||
'crm/ZeroBSCRM.php',
|
||||
);
|
||||
|
||||
/**
|
||||
* The slug of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
|
||||
@ -43,21 +47,21 @@ class Crm extends Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'CRM', 'jetpack-my-jetpack' );
|
||||
return 'CRM';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack CRM', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack CRM';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +70,7 @@ class Crm extends Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'Connect with your people', 'jetpack-my-jetpack' );
|
||||
return __( 'Nurture your contacts to grow your business', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,9 +102,17 @@ class Crm extends Product {
|
||||
* @return array Pricing details
|
||||
*/
|
||||
public static function get_pricing_for_ui() {
|
||||
// We are hard coding pricing info for CRM because it is not available to us through the CRM API.
|
||||
return array(
|
||||
'available' => true,
|
||||
'is_free' => true,
|
||||
'available' => true,
|
||||
'is_free' => false,
|
||||
'full_price' => 132,
|
||||
'discount_price' => 132,
|
||||
'is_introductory_offer' => false,
|
||||
'product_term' => 'year',
|
||||
'introductory_offer' => null,
|
||||
// CRM is only sold in USD
|
||||
'currency_code' => 'USD',
|
||||
);
|
||||
}
|
||||
|
||||
@ -121,4 +133,27 @@ class Crm extends Product {
|
||||
public static function get_manage_url() {
|
||||
return admin_url( 'admin.php?page=zerobscrm-dash' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the product
|
||||
* CRM is available as part of Jetpack Complete
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
$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_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -38,21 +38,21 @@ class Extras extends Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Extras', 'jetpack-my-jetpack' );
|
||||
return 'Extras';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Extras', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Extras';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,10 +15,6 @@ use WP_Error;
|
||||
* Class responsible for handling the hybrid products
|
||||
*
|
||||
* Hybrid products are those that may work both as a stand-alone plugin or with the Jetpack plugin.
|
||||
*
|
||||
* In case Jetpack plugin is active, it will not attempt to install its stand-alone plugin.
|
||||
*
|
||||
* But if Jetpack plugin is not active, then it will prompt to install and activate its stand-alone plugin.
|
||||
*/
|
||||
abstract class Hybrid_Product extends Product {
|
||||
|
||||
@ -29,6 +25,15 @@ abstract class Hybrid_Product extends Product {
|
||||
*/
|
||||
public static $has_standalone_plugin = true;
|
||||
|
||||
/**
|
||||
* For Hybrid products, we can use either the standalone or Jetpack plugin
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_plugin_installed() {
|
||||
return parent::is_plugin_installed() || parent::is_jetpack_plugin_installed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Product is active
|
||||
*
|
||||
@ -47,15 +52,6 @@ abstract class Hybrid_Product extends Product {
|
||||
return parent::is_plugin_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the plugin is installed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_plugin_installed() {
|
||||
return parent::is_plugin_installed() || static::is_jetpack_plugin_installed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Jetpack module is active only if a module_name is defined
|
||||
*
|
||||
@ -122,12 +118,11 @@ abstract class Hybrid_Product extends Product {
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( static::$module_name ) ) {
|
||||
if ( ! static::has_required_plan() ) {
|
||||
// translators: %s is the product name. e.g. Jetpack Search.
|
||||
return new WP_Error( 'not_supported', sprintf( __( 'Your plan does not support %s.', 'jetpack-my-jetpack' ), static::get_title() ) );
|
||||
}
|
||||
// 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 ) ) {
|
||||
$module_activation = ( new Modules() )->activate( static::$module_name, false, false );
|
||||
|
||||
if ( ! $module_activation ) {
|
||||
return new WP_Error( 'module_activation_failed', __( 'Error activating Jetpack module', 'jetpack-my-jetpack' ) );
|
||||
}
|
||||
@ -143,33 +138,8 @@ abstract class Hybrid_Product extends Product {
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
final public static function install_and_activate_standalone() {
|
||||
/**
|
||||
* Check for the presence of the standalone plugin, ignoring Jetpack presence.
|
||||
*
|
||||
* If the standalone plugin is not installed and the user can install plugins, proceed with the installation.
|
||||
*/
|
||||
if ( ! parent::is_plugin_installed() ) {
|
||||
/**
|
||||
* Check for permissions
|
||||
*/
|
||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||
return new WP_Error( 'not_allowed', __( 'You are not allowed to install plugins on this site.', 'jetpack-my-jetpack' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the plugin
|
||||
*/
|
||||
$installed = Plugins_Installer::install_plugin( static::get_plugin_slug() );
|
||||
if ( is_wp_error( $installed ) ) {
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the installed plugin
|
||||
*/
|
||||
$result = static::activate_plugin();
|
||||
public static function install_and_activate_standalone() {
|
||||
$result = parent::install_and_activate_standalone();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
@ -179,7 +149,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() ) {
|
||||
if ( static::has_required_plan() && isset( static::$module_name ) ) {
|
||||
$module_activation = ( new Modules() )->activate( static::$module_name, false, false );
|
||||
|
||||
if ( ! $module_activation ) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Boost product
|
||||
* AI product
|
||||
*
|
||||
* @package my-jetpack
|
||||
*/
|
||||
@ -58,21 +58,21 @@ class Jetpack_Ai extends Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Jetpack AI', 'jetpack-my-jetpack' );
|
||||
return 'AI';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack AI', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack AI';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,7 +103,7 @@ class Jetpack_Ai extends Product {
|
||||
* @return int
|
||||
*/
|
||||
public static function get_next_usage_tier() {
|
||||
if ( ! self::is_site_connected() ) {
|
||||
if ( ! self::is_site_connected() || ! self::has_required_plan() ) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@ -205,12 +205,6 @@ class Jetpack_Ai extends Product {
|
||||
* @return array Pricing details
|
||||
*/
|
||||
public static function get_pricing_for_ui_by_usage_tier( $tier ) {
|
||||
|
||||
// Bail early if the site is not connected.
|
||||
if ( ! self::is_site_connected() ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$product = Wpcom_Products::get_product( static::get_wpcom_product_slug() );
|
||||
|
||||
if ( empty( $product ) ) {
|
||||
@ -316,24 +310,7 @@ class Jetpack_Ai extends Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
$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_starts_with( $purchase->product_slug, static::get_wpcom_product_slug() ) ) {
|
||||
return true;
|
||||
}
|
||||
if ( str_starts_with( $purchase->product_slug, static::get_wpcom_monthly_product_slug() ) ) {
|
||||
return true;
|
||||
}
|
||||
if ( str_starts_with( $purchase->product_slug, static::get_wpcom_bi_yearly_product_slug() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return static::does_site_have_feature( 'ai-assistant' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +86,7 @@ abstract class Module_Product extends Product {
|
||||
*/
|
||||
public static function get_status() {
|
||||
$status = parent::get_status();
|
||||
if ( 'active' === $status && ! static::is_module_active() ) {
|
||||
if ( 'inactive' === $status && ! static::is_module_active() ) {
|
||||
$status = 'module_disabled';
|
||||
}
|
||||
return $status;
|
||||
|
@ -7,8 +7,10 @@
|
||||
|
||||
namespace Automattic\Jetpack\My_Jetpack;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Plugins_Installer;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
@ -47,13 +49,20 @@ abstract class Product {
|
||||
/**
|
||||
* The Jetpack plugin filename
|
||||
*
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
const JETPACK_PLUGIN_FILENAME = array(
|
||||
'jetpack/jetpack.php',
|
||||
'jetpack-dev/jetpack.php',
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether this product requires a site connection
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $requires_site_connection = true;
|
||||
|
||||
/**
|
||||
* Whether this product requires a user connection
|
||||
*
|
||||
@ -118,44 +127,91 @@ abstract class Product {
|
||||
throw new \Exception( 'Product classes must declare the $slug attribute.' );
|
||||
}
|
||||
return array(
|
||||
'slug' => static::$slug,
|
||||
'plugin_slug' => static::$plugin_slug,
|
||||
'name' => static::get_name(),
|
||||
'title' => static::get_title(),
|
||||
'description' => static::get_description(),
|
||||
'long_description' => static::get_long_description(),
|
||||
'tiers' => static::get_tiers(),
|
||||
'features' => static::get_features(),
|
||||
'features_by_tier' => static::get_features_by_tier(),
|
||||
'disclaimers' => static::get_disclaimers(),
|
||||
'status' => static::get_status(),
|
||||
'pricing_for_ui' => static::get_pricing_for_ui(),
|
||||
'is_bundle' => static::is_bundle_product(),
|
||||
'is_plugin_active' => static::is_plugin_active(),
|
||||
'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_required_tier' => static::has_required_tier(),
|
||||
'manage_url' => static::get_manage_url(),
|
||||
'purchase_url' => static::get_purchase_url(),
|
||||
'post_activation_url' => static::get_post_activation_url(),
|
||||
'standalone_plugin_info' => static::get_standalone_info(),
|
||||
'class' => static::class,
|
||||
'post_checkout_url' => static::get_post_checkout_url(),
|
||||
'slug' => static::$slug,
|
||||
'plugin_slug' => static::$plugin_slug,
|
||||
'name' => static::get_name(),
|
||||
'title' => static::get_title(),
|
||||
'description' => static::get_description(),
|
||||
'long_description' => static::get_long_description(),
|
||||
'tiers' => static::get_tiers(),
|
||||
'features' => static::get_features(),
|
||||
'features_by_tier' => static::get_features_by_tier(),
|
||||
'disclaimers' => static::get_disclaimers(),
|
||||
'status' => static::get_status(),
|
||||
'pricing_for_ui' => static::get_pricing_for_ui(),
|
||||
'is_bundle' => static::is_bundle_product(),
|
||||
'is_plugin_active' => static::is_plugin_active(),
|
||||
'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_paid_plan_for_product' => static::has_paid_plan_for_product(),
|
||||
'has_required_tier' => static::has_required_tier(),
|
||||
'manage_url' => static::get_manage_url(),
|
||||
'purchase_url' => static::get_purchase_url(),
|
||||
'post_activation_url' => static::get_post_activation_url(),
|
||||
'standalone_plugin_info' => static::get_standalone_info(),
|
||||
'class' => static::class,
|
||||
'post_checkout_url' => static::get_post_checkout_url(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Collect the site's active features
|
||||
*
|
||||
* @return WP_Error|array
|
||||
*/
|
||||
private static function get_site_features_from_wpcom() {
|
||||
static $features = null;
|
||||
|
||||
if ( $features !== null ) {
|
||||
return $features;
|
||||
}
|
||||
|
||||
$site_id = Jetpack_Options::get_option( 'id' );
|
||||
$response = Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d/features', $site_id ), '1.1' );
|
||||
|
||||
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
return new WP_Error( 'site_features_fetch_failed' );
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$feature_return = json_decode( $body );
|
||||
$features = $feature_return->active;
|
||||
|
||||
return $features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the site has a feature
|
||||
* This will check the features provided by the site plans and products (including free ones)
|
||||
*
|
||||
* @param string $feature - the feature to check for.
|
||||
* @return bool
|
||||
*/
|
||||
public static function does_site_have_feature( $feature ) {
|
||||
if ( ! $feature ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$features = self::get_site_features_from_wpcom();
|
||||
if ( is_wp_error( $features ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array( $feature, $features, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public static function get_name();
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -289,6 +345,19 @@ abstract class Product {
|
||||
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
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current plan (or purchases) of the site already supports the tiers
|
||||
*
|
||||
@ -358,13 +427,15 @@ abstract class Product {
|
||||
public static function get_status() {
|
||||
if ( ! static::is_plugin_installed() ) {
|
||||
$status = 'plugin_absent';
|
||||
if ( static::has_required_plan() ) {
|
||||
if ( static::has_paid_plan_for_product() ) {
|
||||
$status = 'plugin_absent_with_plan';
|
||||
}
|
||||
} elseif ( static::is_active() ) {
|
||||
$status = 'active';
|
||||
// We only consider missing user connection an error when the Product is active.
|
||||
if ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
|
||||
// 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.
|
||||
@ -452,7 +523,8 @@ abstract class Product {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! static::is_plugin_installed() ) {
|
||||
// Default to installing the standalone plugin for the product
|
||||
if ( ! self::is_plugin_installed() ) {
|
||||
$installed = Plugins_Installer::install_plugin( static::get_plugin_slug() );
|
||||
if ( is_wp_error( $installed ) ) {
|
||||
return $installed;
|
||||
@ -537,15 +609,11 @@ abstract class Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the plugin action links.
|
||||
* Filter the action links for the plugins specified.
|
||||
*
|
||||
* @param string|string[] $filenames The plugin filename(s) to filter the action links for.
|
||||
*/
|
||||
public static function extend_plugin_action_links() {
|
||||
|
||||
$filenames = static::get_plugin_filename();
|
||||
if ( ! is_array( $filenames ) ) {
|
||||
$filenames = array( $filenames );
|
||||
}
|
||||
|
||||
private static function filter_action_links( $filenames ) {
|
||||
foreach ( $filenames as $filename ) {
|
||||
$hook = 'plugin_action_links_' . $filename;
|
||||
$callback = array( static::class, 'get_plugin_actions_links' );
|
||||
@ -554,4 +622,65 @@ abstract class Product {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the plugin action links.
|
||||
*/
|
||||
public static function extend_plugin_action_links() {
|
||||
$filenames = static::get_plugin_filename();
|
||||
if ( ! is_array( $filenames ) ) {
|
||||
$filenames = array( $filenames );
|
||||
}
|
||||
|
||||
self::filter_action_links( $filenames );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the Jetpack plugin action links.
|
||||
*/
|
||||
public static function extend_core_plugin_action_links() {
|
||||
$filenames = self::JETPACK_PLUGIN_FILENAME;
|
||||
|
||||
self::filter_action_links( $filenames );
|
||||
}
|
||||
|
||||
/**
|
||||
* Install and activate the standalone plugin in the case it's missing.
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public static function install_and_activate_standalone() {
|
||||
/**
|
||||
* Check for the presence of the standalone plugin, ignoring Jetpack presence.
|
||||
*
|
||||
* If the standalone plugin is not installed and the user can install plugins, proceed with the installation.
|
||||
*/
|
||||
if ( ! static::is_plugin_installed() ) {
|
||||
/**
|
||||
* Check for permissions
|
||||
*/
|
||||
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||
return new WP_Error( 'not_allowed', __( 'You are not allowed to install plugins on this site.', 'jetpack-my-jetpack' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the plugin
|
||||
*/
|
||||
$installed = Plugins_Installer::install_plugin( static::get_plugin_slug() );
|
||||
if ( is_wp_error( $installed ) ) {
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the installed plugin
|
||||
*/
|
||||
$result = static::activate_plugin();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,11 @@
|
||||
|
||||
namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\My_Jetpack\Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class responsible for handling the Protect product
|
||||
@ -52,21 +55,21 @@ class Protect extends Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Protect', 'jetpack-my-jetpack' );
|
||||
return 'Protect';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Protect', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Protect';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,7 +78,7 @@ class Protect extends Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'Stay one step ahead of threats', 'jetpack-my-jetpack' );
|
||||
return __( 'Powerful, automated site security', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,6 +104,33 @@ class Protect extends Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hits the wpcom api to check scan status.
|
||||
*
|
||||
* @todo Maybe add caching.
|
||||
*
|
||||
* @return Object|WP_Error
|
||||
*/
|
||||
private static function get_state_from_wpcom() {
|
||||
static $status = null;
|
||||
|
||||
if ( $status !== null ) {
|
||||
return $status;
|
||||
}
|
||||
|
||||
$site_id = Jetpack_Options::get_option( 'id' );
|
||||
|
||||
$response = Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d/scan', $site_id ) . '?force=wpcom', '2', array( 'timeout' => 2 ), null, 'wpcom' );
|
||||
|
||||
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
return new WP_Error( 'scan_state_fetch_failed' );
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$status = json_decode( $body );
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product's available tiers
|
||||
*
|
||||
@ -217,6 +247,28 @@ class Protect extends Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the site has a paid plan for the product
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_paid_plan_for_product() {
|
||||
$scan_data = static::get_state_from_wpcom();
|
||||
if ( is_wp_error( $scan_data ) ) {
|
||||
return false;
|
||||
}
|
||||
return is_object( $scan_data ) && isset( $scan_data->state ) && 'unavailable' !== $scan_data->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after purchasing the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return self::get_manage_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL where the user manages the product
|
||||
*
|
||||
|
@ -34,21 +34,21 @@ class Scan extends Module_Product {
|
||||
public static $module_name = 'scan';
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Scan', 'jetpack-my-jetpack' );
|
||||
return 'Scan';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Scan', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Scan';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,7 +57,7 @@ class Scan extends Module_Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'Stay one step ahead of threats', 'jetpack-my-jetpack' );
|
||||
return __( 'Powerful, automated site security', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,21 +66,21 @@ class Search extends Hybrid_Product {
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Search', 'jetpack-my-jetpack' );
|
||||
return 'Search';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Search', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Search';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +89,7 @@ class Search extends Hybrid_Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'Help them find what they need', 'jetpack-my-jetpack' );
|
||||
return __( 'Custom instant site search', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,6 +144,15 @@ class Search extends Hybrid_Product {
|
||||
return array_merge( $pricing, $search_pricing );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after purchasing the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return self::get_manage_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WPCOM product slug used to make the purchase
|
||||
*
|
||||
|
@ -31,21 +31,21 @@ class Security extends Module_Product {
|
||||
public static $module_name = 'security';
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return _x( 'Security', 'Jetpack product name', 'jetpack-my-jetpack' );
|
||||
return 'Security';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return _x( 'Security', 'Jetpack product name', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Security';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,7 @@ namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\My_Jetpack\Hybrid_Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
|
||||
/**
|
||||
* Class responsible for handling the Social product
|
||||
@ -55,21 +56,21 @@ class Social extends Hybrid_Product {
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Social', 'jetpack-my-jetpack' );
|
||||
return 'Social';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Social', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Social';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,7 +79,7 @@ class Social extends Hybrid_Product {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_description() {
|
||||
return __( 'Reach your audience on social media', 'jetpack-my-jetpack' );
|
||||
return __( 'Auto-publish to social media', 'jetpack-my-jetpack' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,6 +119,15 @@ class Social extends Hybrid_Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after purchasing the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return self::get_manage_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WPCOM product slug used to make the purchase
|
||||
*
|
||||
@ -133,6 +143,12 @@ class Social extends Hybrid_Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
// 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() ) {
|
||||
return static::does_site_have_feature( 'republicize' );
|
||||
}
|
||||
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
|
@ -31,21 +31,21 @@ class Starter extends Module_Product {
|
||||
public static $module_name = 'starter';
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return _x( 'Starter', 'Jetpack product name', 'jetpack-my-jetpack' );
|
||||
return 'Starter';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return _x( 'Jetpack Starter', 'Jetpack product name', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Starter';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,8 +7,10 @@
|
||||
|
||||
namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\My_Jetpack\Initializer;
|
||||
use Automattic\Jetpack\My_Jetpack\Module_Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
use Jetpack_Options;
|
||||
|
||||
/**
|
||||
@ -44,21 +46,35 @@ class Stats extends Module_Product {
|
||||
public static $plugin_filename = self::JETPACK_PLUGIN_FILENAME;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Stats only requires site connection, not user connection
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $requires_user_connection = false;
|
||||
|
||||
/**
|
||||
* Stats does not have a standalone plugin (yet?)
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $has_standalone_plugin = false;
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'Stats', 'jetpack-my-jetpack' );
|
||||
return 'Stats';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack Stats', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack Stats';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,21 +156,72 @@ class Stats extends Module_Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the site already supports this product through an existing plan or purchase
|
||||
* Gets the 'status' of the Stats product
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_status() {
|
||||
$status = parent::get_status();
|
||||
if ( 'module_disabled' === $status && ! Initializer::is_registered() ) {
|
||||
// If the site has never been connected before, show the "Learn more" CTA,
|
||||
// that points to the add Stats product interstitial.
|
||||
$status = 'needs_purchase_or_free';
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
/**
|
||||
* Checks whether the product can be upgraded to a different product.
|
||||
* Stats Commercial plan (highest tier) & Jetpack Complete are not upgradable.
|
||||
* Also we don't push PWYW users to upgrade.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
public static function is_upgradable() {
|
||||
// For now, atomic sites with stats are not in a position to upgrade
|
||||
if ( ( new Host() )->is_woa_site() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( ! is_wp_error( $purchases_data ) && is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
// Jetpack complete includes Stats commercial & cannot be upgraded
|
||||
if ( str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
return false;
|
||||
} elseif (
|
||||
// Stats commercial purchased with highest tier cannot be upgraded.
|
||||
in_array(
|
||||
$purchase->product_slug,
|
||||
array( 'jetpack_stats_yearly', 'jetpack_stats_monthly', 'jetpack_stats_bi_yearly' ),
|
||||
true
|
||||
) && $purchase->current_price_tier_slug === 'more_than_1m_views'
|
||||
) {
|
||||
return false;
|
||||
} elseif (
|
||||
// If user already has Stats PWYW, we won't push them to upgrade.
|
||||
$purchase->product_slug === 'jetpack_stats_pwyw_yearly'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the site has a paid plan that supports this product
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
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_starts_with( $purchase->product_slug, 'jetpack_stats' ) ) {
|
||||
return true;
|
||||
}
|
||||
if ( str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
// Stats is available as standalone product and as part of the Complete plan.
|
||||
if ( strpos( $purchase->product_slug, 'jetpack_stats' ) !== false || str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -163,58 +230,19 @@ class Stats extends Module_Product {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the product can be upgraded to a different product.
|
||||
* Only Jetpack Stats Commercial plan is not upgradable.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_upgradable() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
// For now, only the free and commercial tiered subs show as upgradable
|
||||
$upgradeable_stats_purchases = array_filter(
|
||||
$purchases_data,
|
||||
static function ( $purchase ) {
|
||||
// Free plan is upgradeable
|
||||
if ( $purchase->product_slug === 'jetpack_stats_free_yearly' ) {
|
||||
return true;
|
||||
// Commercial plans are upgradeable if they have a tier
|
||||
} elseif (
|
||||
in_array(
|
||||
$purchase->product_slug,
|
||||
array( 'jetpack_stats_yearly', 'jetpack_stats_monthly', 'jetpack_stats_bi_yearly' ),
|
||||
true
|
||||
) &&
|
||||
! empty( $purchase->current_price_tier_slug )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return ! empty( $upgradeable_stats_purchases );
|
||||
}
|
||||
|
||||
// If there are no plans found, don't consider the product as upgradeable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a redirect parameter for an upgrade URL if current purchase license is a free license
|
||||
* or an empty string otherwise.
|
||||
* Returns a productType parameter for an upgrade URL, determining whether
|
||||
* to show the PWYW upgrade interstitial or commercial upgrade interstitial.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_url_redirect_string() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
public static function get_url_product_type() {
|
||||
$purchases_data = Wpcom_Products::get_site_current_purchases();
|
||||
$is_commercial_site = Initializer::is_commercial_site();
|
||||
if ( is_wp_error( $purchases_data ) ) {
|
||||
return '';
|
||||
return $is_commercial_site ? '&productType=commercial' : '';
|
||||
}
|
||||
if ( $is_commercial_site ) {
|
||||
return '&productType=commercial';
|
||||
}
|
||||
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
|
||||
foreach ( $purchases_data as $purchase ) {
|
||||
@ -258,7 +286,7 @@ class Stats extends Module_Product {
|
||||
'%s#!/stats/purchase/%d?from=jetpack-my-jetpack%s&redirect_uri=%s',
|
||||
admin_url( 'admin.php?page=stats' ),
|
||||
Jetpack_Options::get_option( 'id' ),
|
||||
static::get_url_redirect_string(),
|
||||
static::get_url_product_type(),
|
||||
rawurlencode( 'admin.php?page=stats' )
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
namespace Automattic\Jetpack\My_Jetpack\Products;
|
||||
|
||||
use Automattic\Jetpack\Current_Plan;
|
||||
use Automattic\Jetpack\My_Jetpack\Hybrid_Product;
|
||||
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
|
||||
|
||||
@ -63,21 +62,21 @@ class Videopress extends Hybrid_Product {
|
||||
public static $has_standalone_plugin = true;
|
||||
|
||||
/**
|
||||
* Get the internationalized product name
|
||||
* Get the product name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return __( 'VideoPress', 'jetpack-my-jetpack' );
|
||||
return 'VideoPress';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internationalized product title
|
||||
* Get the product title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return __( 'Jetpack VideoPress', 'jetpack-my-jetpack' );
|
||||
return 'Jetpack VideoPress';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,6 +126,15 @@ class Videopress extends Hybrid_Product {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL the user is taken after purchasing the product through the checkout
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function get_post_checkout_url() {
|
||||
return self::get_manage_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WPCOM product slug used to make the purchase
|
||||
*
|
||||
@ -164,7 +172,6 @@ class Videopress extends Hybrid_Product {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_required_plan() {
|
||||
// using second argument `true` to force fetching from wpcom
|
||||
return Current_Plan::supports( 'videopress-1tb-storage', true );
|
||||
return static::does_site_have_feature( 'videopress' );
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user