summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src')
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php223
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-products.php144
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-products.php208
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-purchases.php76
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-wpcom-products.php209
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-anti-spam.php138
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-backup.php201
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-boost.php117
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-crm.php124
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-extras.php143
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-hybrid-product.php129
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-module-product.php135
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-product.php437
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-protect.php119
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-scan.php218
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search-stats.php89
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search.php251
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-security.php247
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-social.php135
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-videopress.php125
20 files changed, 3457 insertions, 11 deletions
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php
index 94f18ca4..03dac442 100644
--- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php
@@ -9,7 +9,14 @@ namespace Automattic\Jetpack\My_Jetpack;
use Automattic\Jetpack\Admin_UI\Admin_Menu;
use Automattic\Jetpack\Assets;
+use Automattic\Jetpack\Connection\Client as Client;
use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
+use Automattic\Jetpack\Licensing;
+use Automattic\Jetpack\Status as Status;
+use Automattic\Jetpack\Terms_Of_Service;
+use Automattic\Jetpack\Tracking;
/**
* The main Initializer class that registers the admin menu and eneuque the assets.
@@ -17,20 +24,35 @@ use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
class Initializer {
/**
+ * My Jetpack package version
+ *
+ * @var string
+ */
+ const PACKAGE_VERSION = '1.6.0';
+
+ /**
* Initialize My Jetapack
*
* @return void
*/
public static function init() {
- if ( did_action( 'my_jetpack_init' ) ) {
+ if ( ! self::should_initialize() || did_action( 'my_jetpack_init' ) ) {
return;
}
- // Feature flag while we are developing it.
- if ( ! defined( 'JETPACK_ENABLE_MY_JETPACK' ) || ! JETPACK_ENABLE_MY_JETPACK ) {
- return;
+ // Extend jetpack plugins action links.
+ Products::extend_plugins_action_links();
+
+ // Set up the REST authentication hooks.
+ Connection_Rest_Authentication::init();
+
+ if ( self::is_licensing_ui_enabled() ) {
+ Licensing::instance()->initialize();
}
+ // Add custom WP REST API endoints.
+ add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
+
$page_suffix = Admin_Menu::add_menu(
__( 'My Jetpack', 'jetpack-my-jetpack' ),
__( 'My Jetpack', 'jetpack-my-jetpack' ),
@@ -45,26 +67,63 @@ class Initializer {
/**
* Fires after the My Jetpack package is initialized
*
- * @since $$next_version$$
+ * @since 0.1.0
*/
do_action( 'my_jetpack_init' );
}
/**
+ * Acts as a feature flag, returning a boolean for whether we should show the licensing UI.
+ *
+ * @since 1.2.0
+ *
+ * @return boolean
+ */
+ public static function is_licensing_ui_enabled() {
+ /**
+ * Acts as a feature flag, returning a boolean for whether we should show the licensing UI.
+ *
+ * @param bool $is_enabled Defaults to true.
+ *
+ * @since 1.2.0
+ * @since 1.5.0 Update default value to true.
+ */
+ return apply_filters(
+ 'jetpack_my_jetpack_should_enable_add_license_screen',
+ true
+ );
+ }
+
+ /**
* Callback for the load my jetpack page hook.
*
* @return void
*/
public static function admin_init() {
- add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scritps' ) );
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
+ // Product statuses are constantly changing, so we never want to cache the page.
+ header( 'Cache-Control: no-cache, no-store, must-revalidate' );
+ header( 'Pragma: no-cache' );
+ header( 'Expires: 0' );
}
/**
+ * Returns whether we are in condition to track to use
+ * Analytics functionality like Tracks, MC, or GA.
+ */
+ public static function can_use_analytics() {
+ $status = new Status();
+ $connection = new Connection_Manager();
+ $tracking = new Tracking( 'jetpack', $connection );
+
+ return $tracking->should_enable_tracking( new Terms_Of_Service(), $status );
+ }
+ /**
* Enqueue admin page assets.
*
* @return void
*/
- public static function enqueue_scritps() {
+ public static function enqueue_scripts() {
Assets::register_script(
'my_jetpack_main_app',
'../build/index.js',
@@ -79,23 +138,165 @@ class Initializer {
'my_jetpack_main_app',
'myJetpackInitialState',
array(
- 'apiRoot' => esc_url_raw( rest_url() ),
- 'apiNonce' => wp_create_nonce( 'wp_rest' ),
- 'redirectUrl' => admin_url( '?page=my-jetpack' ),
+ 'products' => array(
+ 'items' => Products::get_products(),
+ ),
+ 'purchases' => array(
+ 'items' => array(),
+ ),
+ 'redirectUrl' => admin_url( 'admin.php?page=my-jetpack' ),
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
+ 'siteSuffix' => ( new Status() )->get_site_suffix(),
+ 'myJetpackVersion' => self::PACKAGE_VERSION,
+ 'fileSystemWriteAccess' => self::has_file_system_write_access(),
+ 'loadAddLicenseScreen' => self::is_licensing_ui_enabled(),
+ 'adminUrl' => esc_url( admin_url() ),
+ )
+ );
+
+ wp_localize_script(
+ 'my_jetpack_main_app',
+ 'myJetpackRest',
+ array(
+ 'apiRoot' => esc_url_raw( rest_url() ),
+ 'apiNonce' => wp_create_nonce( 'wp_rest' ),
)
);
// Connection Initial State.
wp_add_inline_script( 'my_jetpack_main_app', Connection_Initial_State::render(), 'before' );
+
+ // Required for Analytics.
+ if ( self::can_use_analytics() ) {
+ Tracking::register_tracks_functions_scripts( true );
+ }
}
/**
- * Echos the admin page content.
+ * Echoes the admin page content.
*
* @return void
*/
public static function admin_page() {
echo '<div id="my-jetpack-container"></div>';
}
+
+ /**
+ * Register the REST API routes.
+ *
+ * @return void
+ */
+ public static function register_rest_endpoints() {
+ new REST_Products();
+ new REST_Purchases();
+
+ register_rest_route(
+ 'my-jetpack/v1',
+ 'site',
+ array(
+ 'methods' => \WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_site',
+ 'permission_callback' => __CLASS__ . '::permissions_callback',
+ )
+ );
+ }
+
+ /**
+ * Check user capability to access the endpoint.
+ *
+ * @access public
+ * @static
+ *
+ * @return true|WP_Error
+ */
+ public static function permissions_callback() {
+ return current_user_can( 'manage_options' );
+ }
+
+ /**
+ * Return true if we should initialize the My Jetpack admin page.
+ */
+ public static function should_initialize() {
+ $should = true;
+
+ if ( is_multisite() ) {
+ $should = false;
+ }
+
+ // Do not initialize My Jetpack if site is not connected.
+ if ( ! ( new Connection_Manager() )->is_connected() ) {
+ $should = false;
+ }
+
+ /**
+ * Allows filtering whether My Jetpack should be initialized.
+ *
+ * @since 0.5.0-alpha
+ *
+ * @param bool $shoud_initialize Should we initialize My Jetpack?
+ */
+ return apply_filters( 'jetpack_my_jetpack_should_initialize', $should );
+ }
+
+ /**
+ * Site full-data endpoint.
+ *
+ * @return object Site data.
+ */
+ public static function get_site() {
+ $site_id = \Jetpack_Options::get_option( 'id' );
+ $wpcom_endpoint = sprintf( '/sites/%d?force=wpcom', $site_id );
+ $wpcom_api_version = '1.1';
+ $response = Client::wpcom_json_api_request_as_blog( $wpcom_endpoint, $wpcom_api_version );
+ $response_code = wp_remote_retrieve_response_code( $response );
+ $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 rest_ensure_response( $body, 200 );
+ }
+
+ /**
+ * Returns true if the site has file write access to the plugins folder, false otherwise.
+ *
+ * @return bool
+ **/
+ public static function has_file_system_write_access() {
+
+ $cache = get_transient( 'my_jetpack_write_access' );
+
+ if ( false !== $cache ) {
+ return $cache;
+ }
+
+ if ( ! function_exists( 'get_filesystem_method' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ require_once ABSPATH . 'wp-admin/includes/template.php';
+
+ $write_access = 'no';
+
+ $filesystem_method = get_filesystem_method( array(), WP_PLUGIN_DIR );
+ if ( 'direct' === $filesystem_method ) {
+ $write_access = 'yes';
+ }
+
+ if ( ! $write_access ) {
+ ob_start();
+ $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
+ ob_end_clean();
+
+ if ( $filesystem_credentials_are_stored ) {
+ $write_access = 'yes';
+ }
+ }
+
+ set_transient( 'my_jetpack_write_access', $write_access, 30 * MINUTE_IN_SECONDS );
+
+ return $write_access;
+ }
+
}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-products.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-products.php
new file mode 100644
index 00000000..d02e1581
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-products.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Class for manipulating products
+ *
+ * @package automattic/my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+/**
+ * A class for everything related to product handling in My Jetpack
+ */
+class Products {
+
+ /**
+ * Get the list of Products classes
+ *
+ * Here's where all the existing Products are registered
+ *
+ * @return array List of class names
+ */
+ public static function get_products_classes() {
+ return array(
+ Products\Anti_Spam::class,
+ Products\Backup::class,
+ Products\Boost::class,
+ Products\Crm::class,
+ Products\Extras::class,
+ Products\Scan::class,
+ Products\Search::class,
+ Products\Social::class,
+ Products\Security::class,
+ Products\Protect::class,
+ Products\Videopress::class,
+ );
+ }
+
+ /**
+ * Product data
+ *
+ * @return array Jetpack products on the site and their availability.
+ */
+ public static function get_products() {
+ $products = array();
+ foreach ( self::get_products_classes() as $class ) {
+ $product_slug = $class::$slug;
+ $products[ $product_slug ] = $class::get_info();
+ }
+ return $products;
+ }
+
+ /**
+ * Get one product data by its slug
+ *
+ * @param string $product_slug The product slug.
+ *
+ * @return ?array
+ */
+ public static function get_product( $product_slug ) {
+ foreach ( self::get_products_classes() as $class ) {
+ $p_slug = $class::$slug;
+ if ( $p_slug === $product_slug ) {
+ return $class::get_info();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return product slugs list.
+ *
+ * @return array Product slugs array.
+ */
+ public static function get_products_slugs() {
+ $slugs = array();
+ foreach ( self::get_products_classes() as $class ) {
+ $slugs[] = $class::$slug;
+ }
+ return $slugs;
+ }
+
+ /**
+ * Gets the json schema for the product data
+ *
+ * @return array
+ */
+ public static function get_product_data_schema() {
+ return array(
+ 'title' => 'The requested product data',
+ 'type' => 'object',
+ 'properties' => array(
+ 'product' => array(
+ 'description' => __( 'Product slug', 'jetpack-my-jetpack' ),
+ 'type' => 'string',
+ 'enum' => __CLASS__ . '::get_product_slugs',
+ 'required' => false,
+ 'validate_callback' => __CLASS__ . '::check_product_argument',
+ ),
+ 'action' => array(
+ 'description' => __( 'Production action to execute', 'jetpack-my-jetpack' ),
+ 'type' => 'string',
+ 'enum' => array( 'activate', 'deactivate' ),
+ 'required' => false,
+ 'validate_callback' => __CLASS__ . '::check_product_argument',
+ ),
+ 'slug' => array(
+ 'title' => 'The product slug',
+ 'type' => 'string',
+ ),
+ 'name' => array(
+ 'title' => 'The product name',
+ 'type' => 'string',
+ ),
+ 'description' => array(
+ 'title' => 'The product description',
+ 'type' => 'string',
+ ),
+ 'status' => array(
+ 'title' => 'The product status',
+ 'type' => 'string',
+ 'enum' => array( 'active', 'inactive', 'plugin_absent', 'needs_purchase', 'error' ),
+ ),
+ 'class' => array(
+ 'title' => 'The product class handler',
+ 'type' => 'string',
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Extend actions links for plugins
+ * tied to the Products.
+ */
+ public static function extend_plugins_action_links() {
+ Products\Backup::extend_plugin_action_links();
+ Products\Boost::extend_plugin_action_links();
+ Products\Crm::extend_plugin_action_links();
+
+ // Extend Jetpack plugin using Videopress instance.
+ Products\Videopress::extend_plugin_action_links();
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-products.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-products.php
new file mode 100644
index 00000000..a0300a84
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-products.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * Sets up the Products REST API endpoints.
+ *
+ * @package automattic/my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use WP_Error;
+
+/**
+ * Registers the REST routes for Products.
+ */
+class REST_Products {
+ /**
+ * Constructor.
+ */
+ public function __construct() {
+ register_rest_route(
+ 'my-jetpack/v1',
+ 'site/products',
+ array(
+ array(
+ 'methods' => \WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_products',
+ 'permission_callback' => __CLASS__ . '::permissions_callback',
+ ),
+ 'schema' => array( $this, 'get_products_schema' ),
+ )
+ );
+
+ $product_arg = array(
+ 'description' => __( 'Product slug', 'jetpack-my-jetpack' ),
+ 'type' => 'string',
+ 'enum' => Products::get_products_slugs(),
+ 'required' => true,
+ 'validate_callback' => __CLASS__ . '::check_product_argument',
+ );
+
+ register_rest_route(
+ 'my-jetpack/v1',
+ 'site/products/(?P<product>[a-z\-]+)',
+ array(
+ array(
+ 'methods' => \WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_product',
+ 'permission_callback' => __CLASS__ . '::permissions_callback',
+ 'args' => array(
+ 'product' => $product_arg,
+ ),
+ ),
+ array(
+ 'methods' => \WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::activate_product',
+ 'permission_callback' => __CLASS__ . '::edit_permissions_callback',
+ 'args' => array(
+ 'product' => $product_arg,
+ ),
+ ),
+ array(
+ 'methods' => \WP_REST_Server::DELETABLE,
+ 'callback' => __CLASS__ . '::deactivate_product',
+ 'permission_callback' => __CLASS__ . '::edit_permissions_callback',
+ 'args' => array(
+ 'product' => $product_arg,
+ ),
+ ),
+ )
+ );
+ }
+
+ /**
+ * Get the schema for the products endpoint
+ *
+ * @return array
+ */
+ public function get_products_schema() {
+ return array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'products',
+ 'type' => 'object',
+ 'properties' => Products::get_product_data_schema(),
+ );
+ }
+
+ /**
+ * Check user capability to access the endpoint.
+ *
+ * @access public
+ * @static
+ *
+ * @return true|WP_Error
+ */
+ public static function permissions_callback() {
+ return current_user_can( 'manage_options' );
+ }
+
+ /**
+ * Check Product arguments.
+ *
+ * @access public
+ * @static
+ *
+ * @param mixed $value - Value of the 'product' argument.
+ * @return true|WP_Error True if the value is valid, WP_Error otherwise.
+ */
+ public static function check_product_argument( $value ) {
+ if ( ! is_string( $value ) ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ esc_html__( 'The product argument must be a string.', 'jetpack-my-jetpack' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Site products endpoint.
+ *
+ * @return array of site products list.
+ */
+ public static function get_products() {
+ $response = Products::get_products();
+ return rest_ensure_response( $response, 200 );
+ }
+
+ /**
+ * Site single product endpoint.
+ *
+ * @param \WP_REST_Request $request The request object.
+ * @return array of site products list.
+ */
+ public static function get_product( $request ) {
+ $product_slug = $request->get_param( 'product' );
+ return rest_ensure_response( Products::get_product( $product_slug ), 200 );
+ }
+
+ /**
+ * Check permission to edit product
+ *
+ * @return bool
+ */
+ public static function edit_permissions_callback() {
+ if ( ! current_user_can( 'activate_plugins' ) ) {
+ return false;
+ }
+ if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Callback for activating a product
+ *
+ * @param \WP_REST_Request $request The request object.
+ * @return \WP_REST_Response
+ */
+ public static function activate_product( $request ) {
+ $product_slug = $request->get_param( 'product' );
+ $product = Products::get_product( $product_slug );
+ if ( ! isset( $product['class'] ) ) {
+ return new \WP_Error(
+ 'not_implemented',
+ esc_html__( 'The product class handler is not implemented', 'jetpack-my-jetpack' ),
+ array( 'status' => 501 )
+ );
+ }
+
+ $activate_product_result = call_user_func( array( $product['class'], 'activate' ) );
+ if ( is_wp_error( $activate_product_result ) ) {
+ $activate_product_result->add_data( array( 'status' => 400 ) );
+ return $activate_product_result;
+ }
+
+ return rest_ensure_response( Products::get_product( $product_slug ), 200 );
+ }
+
+ /**
+ * Callback for deactivating a product
+ *
+ * @param \WP_REST_Request $request The request object.
+ * @return \WP_REST_Response
+ */
+ public static function deactivate_product( $request ) {
+ $product_slug = $request->get_param( 'product' );
+ $product = Products::get_product( $product_slug );
+ if ( ! isset( $product['class'] ) ) {
+ return new \WP_Error(
+ 'not_implemented',
+ esc_html__( 'The product class handler is not implemented', 'jetpack-my-jetpack' ),
+ array( 'status' => 501 )
+ );
+ }
+
+ $deactivate_product_result = call_user_func( array( $product['class'], 'deactivate' ) );
+ if ( is_wp_error( $deactivate_product_result ) ) {
+ $deactivate_product_result->add_data( array( 'status' => 400 ) );
+ return $deactivate_product_result;
+ }
+
+ return rest_ensure_response( Products::get_product( $product_slug ), 200 );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-purchases.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-purchases.php
new file mode 100644
index 00000000..c90e1b43
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-rest-purchases.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Sets up the Purchases REST API endpoints.
+ *
+ * @package automattic/my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use Automattic\Jetpack\Connection\Client as Client;
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+
+/**
+ * Registers the REST routes for Purchases.
+ */
+class REST_Purchases {
+ /**
+ * Constructor.
+ */
+ public function __construct() {
+ register_rest_route(
+ 'my-jetpack/v1',
+ '/site/purchases',
+ array(
+ 'methods' => \WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_site_current_purchases',
+ 'permission_callback' => __CLASS__ . '::permissions_callback',
+ )
+ );
+ }
+
+ /**
+ * Check user capability to access the endpoint.
+ *
+ * @access public
+ * @static
+ *
+ * @return true|WP_Error
+ */
+ public static function permissions_callback() {
+ $connection = new Connection_Manager();
+ $is_site_connected = $connection->is_connected();
+
+ if ( ! $is_site_connected ) {
+ return new \WP_Error(
+ 'not_connected',
+ __( 'Your site is not connected to Jetpack.', 'jetpack-my-jetpack' ),
+ array(
+ 'status' => 400,
+ )
+ );
+ }
+
+ return current_user_can( 'manage_options' );
+ }
+
+ /**
+ * Site purchases endpoint.
+ *
+ * @return array of site purchases.
+ */
+ public static function get_site_current_purchases() {
+ $site_id = \Jetpack_Options::get_option( 'id' );
+ $wpcom_endpoint = sprintf( '/sites/%1$d/purchases?locale=%2$s', $site_id, get_user_locale() );
+ $wpcom_api_version = '1.1';
+ $response = Client::wpcom_json_api_request_as_blog( $wpcom_endpoint, $wpcom_api_version );
+ $response_code = wp_remote_retrieve_response_code( $response );
+ $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 ? $response_code : 400 ) );
+ }
+
+ return rest_ensure_response( $body, 200 );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-wpcom-products.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-wpcom-products.php
new file mode 100644
index 00000000..cac879fe
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-wpcom-products.php
@@ -0,0 +1,209 @@
+<?php
+/**
+ * Fetches and store the list of Jetpack products available in WPCOM
+ *
+ * @package automattic/my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use Automattic\Jetpack\Connection\Client as Client;
+use Automattic\Jetpack\Status\Visitor;
+use WP_Error;
+/**
+ * Stores the list of products available for purchase in WPCOM
+ */
+class Wpcom_Products {
+
+ /**
+ * The meta name used to store the cache date
+ *
+ * @var string
+ */
+ const CACHE_DATE_META_NAME = 'my-jetpack-cache-date';
+
+ /**
+ * The meta name used to store the cache
+ *
+ * @var string
+ */
+ const CACHE_META_NAME = 'my-jetpack-cache';
+
+ /**
+ * Fetches the list of products from WPCOM
+ *
+ * @return Object|WP_Error
+ */
+ private static function get_products_from_wpcom() {
+
+ $blog_id = \Jetpack_Options::get_option( 'id' );
+ $endpoint = sprintf( '/sites/%d/products/?_locale=%s&type=jetpack', $blog_id, get_user_locale() );
+
+ $wpcom_request = Client::wpcom_json_api_request_as_blog(
+ $endpoint,
+ '1.1',
+ array(
+ 'method' => 'GET',
+ 'headers' => array(
+ 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ),
+ ),
+ )
+ );
+
+ $response_code = wp_remote_retrieve_response_code( $wpcom_request );
+
+ if ( 200 === $response_code ) {
+ return json_decode( wp_remote_retrieve_body( $wpcom_request ) );
+ } else {
+ return new WP_Error(
+ 'failed_to_fetch_wpcom_products',
+ esc_html__( 'Unable to fetch the products list from WordPress.com', 'jetpack-my-jetpack' ),
+ array( 'status' => $response_code )
+ );
+ }
+ }
+
+ /**
+ * Update the cache with new information retrieved from WPCOM
+ *
+ * We store one cache for each user, as the information is internationalized based on user preferences
+ * Also, the currency is based on the user IP address
+ *
+ * @param Object $products_list The products list as received from WPCOM.
+ * @return bool
+ */
+ private static function update_cache( $products_list ) {
+ update_user_meta( get_current_user_id(), self::CACHE_DATE_META_NAME, time() );
+ return update_user_meta( get_current_user_id(), self::CACHE_META_NAME, $products_list );
+ }
+
+ /**
+ * Checks if the cache is old, meaning we need to fetch new data from WPCOM
+ */
+ private static function is_cache_old() {
+ if ( empty( self::get_products_from_cache() ) ) {
+ return true;
+ }
+ $cache_date = get_user_meta( get_current_user_id(), self::CACHE_DATE_META_NAME, true );
+ return time() - (int) $cache_date > ( 7 * DAY_IN_SECONDS );
+ }
+
+ /**
+ * Gets the product list from the user cache
+ */
+ private static function get_products_from_cache() {
+ return get_user_meta( get_current_user_id(), self::CACHE_META_NAME, true );
+ }
+
+ /**
+ * Gets the product list
+ *
+ * Attempts to retrieve the products list from the user cache if cache is not too old.
+ * If cache is old, it will attempt to fetch information from WPCOM. If it fails, we return what we have in cache, if anything, otherwise we return an error.
+ *
+ * @param bool $skip_cache If true it will ignore the cache and attempt to fetch fresh information from WPCOM.
+ *
+ * @return Object|WP_Error
+ */
+ public static function get_products( $skip_cache = false ) {
+ // This is only available for logged in users.
+ if ( ! get_current_user_id() ) {
+ return null;
+ }
+ if ( ! self::is_cache_old() && ! $skip_cache ) {
+ return self::get_products_from_cache();
+ }
+
+ $products = self::get_products_from_wpcom();
+ if ( is_wp_error( $products ) ) {
+ // Let's see if we have it cached.
+ $cached = self::get_products_from_cache();
+ if ( ! empty( $cached ) ) {
+ return $cached;
+ } else {
+ return $products;
+ }
+ }
+
+ self::update_cache( $products );
+ return $products;
+
+ }
+
+ /**
+ * Get one product
+ *
+ * @param string $product_slug The product slug.
+ *
+ * @return ?Object The product details if found
+ */
+ public static function get_product( $product_slug ) {
+ $products = self::get_products();
+ if ( ! empty( $products->$product_slug ) ) {
+ return $products->$product_slug;
+ }
+ }
+
+ /**
+ * Get only the product currency code and price in an array
+ *
+ * @param string $product_slug The product slug.
+ *
+ * @return array An array with currency_code and full_price. Empty array if product not found.
+ */
+ public static function get_product_pricing( $product_slug ) {
+ $product = self::get_product( $product_slug );
+ if ( empty( $product ) ) {
+ return array();
+ }
+
+ $cost = $product->cost;
+ $discount_price = $cost;
+
+ // Get/compute the discounted price.
+ if ( isset( $product->introductory_offer->cost_per_interval ) ) {
+ $discount_price = $product->introductory_offer->cost_per_interval;
+ }
+
+ $pricing = array(
+ 'currency_code' => $product->currency_code,
+ 'full_price' => $cost,
+ 'discount_price' => $discount_price,
+ );
+
+ return self::populate_with_discount( $product, $pricing, $discount_price );
+ }
+
+ /**
+ * 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.
+ */
+ public static function populate_with_discount( $product, $pricing, $price ) {
+ // Check whether the product has a coupon.
+ if ( ! isset( $product->sale_coupon ) ) {
+ return $pricing;
+ }
+
+ // Check whether it is still valid.
+ $coupon = $product->sale_coupon;
+ $coupon_start_date = strtotime( $coupon->start_date );
+ $coupon_expires = strtotime( $coupon->expires );
+ if ( $coupon_start_date > time() || $coupon_expires < time() ) {
+ return $pricing;
+ }
+
+ $coupon_discount = intval( $coupon->discount );
+
+ // Populate response with coupon discount.
+ $pricing['coupon_discount'] = $coupon_discount;
+
+ // Apply coupon discount to the price.
+ $pricing['discount_price'] = $price * ( 100 - $coupon_discount ) / 100;
+
+ return $pricing;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-anti-spam.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-anti-spam.php
new file mode 100644
index 00000000..7c8820d0
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-anti-spam.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Anti_Spam product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+
+/**
+ * Class responsible for handling the Anti_Spam product
+ */
+class Anti_Spam extends Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'anti-spam';
+
+ /**
+ * The filename (id) of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
+ *
+ * @var string
+ */
+ public static $plugin_filename = 'akismet/akismet.php';
+
+ /**
+ * The slug of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'akismet';
+
+ /**
+ * Whether this product requires a user connection
+ *
+ * @var string
+ */
+ public static $requires_user_connection = false;
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Anti-Spam', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Anti-Spam', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Stop comment and form spam', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Save time and get better responses by automatically blocking spam from your comments and forms.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Boost features list
+ */
+ public static function get_features() {
+ return array(
+ _x( 'Comment and form spam protection', 'Anti-Spam Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Powered by Akismet', 'Anti-Spam Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Block spam without CAPTCHAs', 'Anti-Spam Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Advanced stats', 'Anti-Spam Product Feature', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_anti_spam';
+ }
+
+ /**
+ * Return product bundles list
+ * that supports the product.
+ *
+ * @return boolean|array Products bundle list.
+ */
+ public static function is_upgradable_by_bundle() {
+ return array( 'security' );
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return admin_url( 'admin.php?page=akismet-key-config' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-backup.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-backup.php
new file mode 100644
index 00000000..37b45caf
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-backup.php
@@ -0,0 +1,201 @@
+<?php
+/**
+ * Boost product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\My_Jetpack\Hybrid_Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+use Automattic\Jetpack\Redirect;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * Class responsible for handling the Backup product
+ */
+class Backup extends Hybrid_Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'backup';
+
+ /**
+ * The filename (id) of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_filename = array(
+ 'jetpack-backup/jetpack-backup.php',
+ 'backup/jetpack-backup.php',
+ 'jetpack-backup-dev/jetpack-backup.php',
+ );
+
+ /**
+ * The slug of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'jetpack-backup';
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Backup', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Backup', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Save every change', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Never lose a word, image, page, or time worrying about your site with automated backups & one-click restores.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Backup features list
+ */
+ public static function get_features() {
+ return array(
+ _x( 'Real-time cloud backups', 'Backup Product Feature', 'jetpack-my-jetpack' ),
+ _x( '10GB of backup storage', 'Backup Product Feature', 'jetpack-my-jetpack' ),
+ _x( '30-day archive & activity log', 'Backup Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'One-click restores', 'Backup Product Feature', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_backup_t1_yearly';
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+ }
+
+ /**
+ * Hits the wpcom api to check rewind 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/rewind', $site_id ) . '?force=wpcom', '2', array( 'timeout' => 2 ), null, 'wpcom' );
+
+ if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return new WP_Error( 'rewind_state_fetch_failed' );
+ }
+
+ $body = wp_remote_retrieve_body( $response );
+ $status = json_decode( $body );
+ return $status;
+ }
+
+ /**
+ * Checks whether the current plan (or purchases) of the site already supports the product
+ *
+ * @return boolean
+ */
+ public static function has_required_plan() {
+ $rewind_data = static::get_state_from_wpcom();
+ if ( is_wp_error( $rewind_data ) ) {
+ return false;
+ }
+ return is_object( $rewind_data ) && isset( $rewind_data->state ) && 'unavailable' !== $rewind_data->state;
+ }
+
+ /**
+ * Return product bundles list
+ * that supports the product.
+ *
+ * @return boolean|array Products bundle list.
+ */
+ public static function is_upgradable_by_bundle() {
+ return array( 'security' );
+ }
+
+ /**
+ * Get the URL the user is taken after activating the product
+ *
+ * @return ?string
+ */
+ public static function get_post_activation_url() {
+ return ''; // stay in My Jetpack page or continue the purchase flow if needed.
+ }
+
+ /**
+ * Get the URL where the user manages the 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() ) {
+ return admin_url( 'admin.php?page=jetpack-backup' );
+ }
+ }
+
+ /**
+ * Checks whether the Product is active
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return parent::is_active() && static::has_required_plan();
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-boost.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-boost.php
new file mode 100644
index 00000000..7c159eb4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-boost.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * Boost product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Product;
+
+/**
+ * Class responsible for handling the Boost product
+ */
+class Boost extends Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'boost';
+
+ /**
+ * The filename (id) of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_filename = array(
+ 'jetpack-boost/jetpack-boost.php',
+ 'boost/jetpack-boost.php',
+ 'jetpack-boost-dev/jetpack-boost.php',
+ );
+ /**
+ * The slug of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'jetpack-boost';
+
+ /**
+ * Whether this product requires a user connection
+ *
+ * @var string
+ */
+ public static $requires_user_connection = false;
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Boost', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Boost', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Instant speed and SEO', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Jetpack Boost gives your site the same performance advantages as the world’s leading websites, no developer required.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Boost features list
+ */
+ public static function get_features() {
+ return array(
+ __( 'Check your site performance', 'jetpack-my-jetpack' ),
+ __( 'Enable improvements in one click', 'jetpack-my-jetpack' ),
+ __( 'Standalone free plugin for those focused on speed', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array(
+ 'available' => true,
+ 'is_free' => true,
+ );
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return admin_url( 'admin.php?page=jetpack-boost' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-crm.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-crm.php
new file mode 100644
index 00000000..9fce5f92
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-crm.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Boost product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Product;
+
+/**
+ * Class responsible for handling the CRM product
+ */
+class Crm extends Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'crm';
+
+ /**
+ * The filename (id) of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
+ *
+ * @var string
+ */
+ public static $plugin_filename = 'zero-bs-crm/ZeroBSCRM.php';
+
+ /**
+ * The slug of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'zero-bs-crm';
+
+ /**
+ * Whether this product requires a user connection
+ *
+ * @var string
+ */
+ public static $requires_user_connection = false;
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'CRM', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack CRM', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Connect with your people', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'All of your contacts in one place. Build better relationships with your customers and clients.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array CRM features list
+ */
+ public static function get_features() {
+ return array(
+ __( 'Manage unlimited contacts', 'jetpack-my-jetpack' ),
+ __( 'Manage billing and create invoices', 'jetpack-my-jetpack' ),
+ __( 'Fully integrated with WordPress & WooCommerce', 'jetpack-my-jetpack' ),
+ __( 'Infinitely customizable with integrations and extensions', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array(
+ 'available' => true,
+ 'is_free' => true,
+ );
+ }
+
+ /**
+ * Get the URL the user is taken after activating the product
+ *
+ * @return ?string
+ */
+ public static function get_post_activation_url() {
+ return admin_url( 'admin.php?page=zerobscrm-plugin' ); // Welcome page.
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return admin_url( 'admin.php?page=zerobscrm-dash' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-extras.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-extras.php
new file mode 100644
index 00000000..d0ffaf8c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-extras.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Extras product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Product;
+
+/**
+ * Class responsible for handling the Extras product.
+ * Extras, so far, could be considered as Jetpack plugin bridge.
+ */
+class Extras extends Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'extras';
+
+ /**
+ * The slug of the plugin associated with this product.
+ * Extras, is in short, Jetpack plugin bridge so far.
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'jetpack';
+
+ /**
+ * Whether this product requires a user connection
+ *
+ * @var string
+ */
+ public static $requires_user_connection = false;
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Extras', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Extras', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Basic tools for a successful site', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( "Secure and speed up your site for free with Jetpack's powerful WordPress tools.", 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Boost features list
+ */
+ public static function get_features() {
+ return array(
+ __( 'Measure your impact with beautiful stats', 'jetpack-my-jetpack' ),
+ __( 'Speed up your site with optimized images', 'jetpack-my-jetpack' ),
+ __( 'Protect your site against bot attacks', 'jetpack-my-jetpack' ),
+ __( 'Get notifications if your site goes offline', 'jetpack-my-jetpack' ),
+ __( 'Enhance your site with dozens of other features', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array(
+ 'available' => true,
+ 'is_free' => true,
+ );
+ }
+
+ /**
+ * Checks whether the Product is active.
+ * If Jetpack plugin is active, then Extras will be inactive.
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return static::is_jetpack_plugin_active();
+ }
+
+ /**
+ * Checks whether the plugin is installed
+ * If Jetpack plugin is installed, then Extras will be inactive.
+ *
+ * @return boolean
+ */
+ public static function is_plugin_installed() {
+ return static::is_jetpack_plugin_installed();
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return admin_url( 'admin.php?page=jetpack' );
+ }
+
+ /**
+ * Activates the Jetpack plugin
+ *
+ * @return null|WP_Error Null on success, WP_Error on invalid file.
+ */
+ public static function activate_plugin() {
+ /*
+ * Silent mode True to avoid redirect
+ */
+ return activate_plugin( static::get_installed_plugin_filename( 'jetpack' ), '', false, true );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-hybrid-product.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-hybrid-product.php
new file mode 100644
index 00000000..1bb995ff
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-hybrid-product.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Base product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use Automattic\Jetpack\Modules;
+use Automattic\Jetpack\Plugins_Installer;
+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 {
+
+ /**
+ * Checks whether the Product is active
+ *
+ * @return boolean
+ */
+ public static function is_plugin_active() {
+ return parent::is_plugin_active() || parent::is_jetpack_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
+ *
+ * @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
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return parent::is_active() && static::is_module_active();
+ }
+
+ /**
+ * Activates the plugin
+ *
+ * @return null|WP_Error Null on success, WP_Error on invalid file.
+ */
+ public static function activate_plugin() {
+ /*
+ * Activate self-installed plugin if it's installed.
+ * Silent mode True to avoid redirects in Backup.
+ * @TODO When new Hybrid products are added, we might not want to go silent with all of them.
+ */
+ if ( parent::is_plugin_installed() ) {
+ return activate_plugin( static::get_installed_plugin_filename(), '', false, true );
+ }
+
+ /*
+ * Otherwise, activate Jetpack plugin.
+ * Silent mode True to avoid redirects.
+ */
+ if ( static::is_jetpack_plugin_installed() ) {
+ return activate_plugin( static::get_installed_plugin_filename( 'jetpack' ) );
+ }
+
+ return new WP_Error( 'plugin_not_found', __( 'Activation failed. Plugin is not installed', 'jetpack-my-jetpack' ) );
+ }
+
+ /**
+ * Activates the product. If the Hybrid product has declared a jetpack module name, let's try to activate it if Jetpack plugin is active
+ *
+ * @param bool|WP_Error $product_activation Is the result of the top level activation actions. You probably won't do anything if it is an WP_Error.
+ * @return bool|WP_Error
+ */
+ public static function do_product_specific_activation( $product_activation ) {
+
+ if ( is_wp_error( $product_activation ) ) {
+ // If we failed to install the stand-alone plugin because the package was not found, let's try and install Jetpack plugin instead.
+ // This might happens, for example, while the stand-alone plugin was not released to the WP.org repository yet.
+ if ( 'no_package' === $product_activation->get_error_code() ) {
+ $product_activation = Plugins_Installer::install_plugin( self::JETPACK_PLUGIN_SLUG );
+ if ( ! is_wp_error( $product_activation ) ) {
+ $product_activation = static::activate_plugin();
+ }
+ }
+ if ( is_wp_error( $product_activation ) ) {
+ return $product_activation;
+ }
+ }
+
+ 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() ) );
+ }
+ $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' ) );
+ }
+
+ return $module_activation;
+ }
+
+ return true;
+
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-module-product.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-module-product.php
new file mode 100644
index 00000000..e2cf58ef
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-module-product.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * Base Module product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use Jetpack;
+use WP_Error;
+
+/**
+ * Class responsible for handling the Module products
+ *
+ * Module products are those that are a Jetpack module behind the scenes.
+ *
+ * They require Jetpack plugin and will then activate/deactivate a module.
+ */
+abstract class Module_Product extends Product {
+
+ /**
+ * The Jetpack module name associated with this product
+ *
+ * @var string|null
+ */
+ public static $module_name = null;
+
+ /**
+ * Get the plugin slug - ovewrite it ans return Jetpack's
+ *
+ * @return ?string
+ */
+ public static function get_plugin_slug() {
+ return self::JETPACK_PLUGIN_SLUG;
+ }
+
+ /**
+ * Get the plugin filename - ovewrite it ans return Jetpack's
+ *
+ * @return ?string
+ */
+ public static function get_plugin_filename() {
+ return self::JETPACK_PLUGIN_FILENAME;
+ }
+
+ /**
+ * Ensure that child classes define $module_name attribute
+ *
+ * @throws \Exception If required attribute is not declared in the child class.
+ * @return void
+ */
+ private static function check_for_module_name() {
+ if ( empty( static::$module_name ) ) {
+ throw new \Exception( 'Module Product classes must declare the $module_name attribute.' );
+ }
+ }
+
+ /**
+ * Checks whether the Product is active
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return static::is_jetpack_plugin_active() && static::is_module_active();
+ }
+
+ /**
+ * Checks whether the Jetpack module is active
+ *
+ * @return bool
+ */
+ public static function is_module_active() {
+ self::check_for_module_name();
+ if ( ! class_exists( 'Jetpack' ) ) {
+ return false;
+ }
+
+ return Jetpack::is_module_active( static::$module_name );
+ }
+
+ /**
+ * Gets the current status of the product
+ *
+ * @return string
+ */
+ public static function get_status() {
+ $status = parent::get_status();
+ if ( 'active' === $status && ! static::is_module_active() ) {
+ $status = 'module_disabled';
+ }
+ return $status;
+ }
+
+ /**
+ * Activates the product by installing and activating its plugin
+ *
+ * @param bool|WP_Error $plugin_activation 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
+ */
+ public static function do_product_specific_activation( $plugin_activation ) {
+ self::check_for_module_name();
+
+ if ( is_wp_error( $plugin_activation ) ) {
+ return $plugin_activation;
+ }
+
+ if ( ! class_exists( 'Jetpack' ) ) {
+ return new WP_Error( 'plugin_activation_failed', __( 'Error activating Jetpack plugin', 'jetpack-my-jetpack' ) );
+ }
+
+ $module_activation = Jetpack::activate_module( static::$module_name, false, false );
+
+ if ( ! $module_activation ) {
+ return new WP_Error( 'module_activation_failed', __( 'Error activating Jetpack module', 'jetpack-my-jetpack' ) );
+ }
+
+ return $module_activation;
+
+ }
+
+ /**
+ * Deactivate the module
+ *
+ * @return boolean
+ */
+ public static function deactivate() {
+ self::check_for_module_name();
+ if ( ! class_exists( 'Jetpack' ) ) {
+ return true;
+ }
+ return Jetpack::deactivate_module( static::$module_name );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-product.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-product.php
new file mode 100644
index 00000000..ef09aecd
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-product.php
@@ -0,0 +1,437 @@
+<?php
+/**
+ * Base product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Automattic\Jetpack\Plugins_Installer;
+use WP_Error;
+
+/**
+ * Class responsible for handling the products
+ */
+abstract class Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 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
+ *
+ * @var string|string[]
+ */
+ protected static $plugin_filename = null;
+
+ /**
+ * The slug of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
+ *
+ * @var string
+ */
+ public static $plugin_slug = null;
+
+ /**
+ * The Jetpack plugin slug
+ *
+ * @var string
+ */
+ const JETPACK_PLUGIN_SLUG = 'jetpack';
+
+ /**
+ * The Jetpack plugin filename
+ *
+ * @var string
+ */
+ const JETPACK_PLUGIN_FILENAME = array(
+ 'jetpack/jetpack.php',
+ 'jetpack-dev/jetpack.php',
+ );
+
+ /**
+ * Whether this product requires a user connection
+ *
+ * @var string
+ */
+ public static $requires_user_connection = true;
+
+ /**
+ * Get the plugin slug
+ *
+ * @return ?string
+ */
+ public static function get_plugin_slug() {
+ return static::$plugin_slug;
+ }
+
+ /**
+ * Get the plugin filename
+ *
+ * @return ?string
+ */
+ public static function get_plugin_filename() {
+ return static::$plugin_filename;
+ }
+
+ /**
+ * Get the installed plugin filename, considering all possible filenames a plugin might have
+ *
+ * @param string $plugin Which plugin to check. jetpack for the jetpack plugin or product for the product specific plugin.
+ *
+ * @return ?string
+ */
+ public static function get_installed_plugin_filename( $plugin = 'product' ) {
+ $all_plugins = Plugins_Installer::get_plugins();
+ $filename = 'jetpack' === $plugin ? self::JETPACK_PLUGIN_FILENAME : static::get_plugin_filename();
+ if ( ! is_array( $filename ) ) {
+ $filename = array( $filename );
+ }
+ foreach ( $filename as $name ) {
+ $installed = array_key_exists( $name, $all_plugins );
+ if ( $installed ) {
+ return $name;
+ }
+ }
+ }
+
+ /**
+ * Get the Product info for the API
+ *
+ * @throws \Exception If required attribute is not declared in the child class.
+ * @return array
+ */
+ public static function get_info() {
+ if ( static::$slug === null ) {
+ throw new \Exception( 'Product classes must declare the $slug attribute.' );
+ }
+ return array(
+ 'slug' => static::$slug,
+ 'name' => static::get_name(),
+ 'title' => static::get_title(),
+ 'description' => static::get_description(),
+ 'long_description' => static::get_long_description(),
+ 'features' => static::get_features(),
+ 'status' => static::get_status(),
+ 'pricing_for_ui' => static::get_pricing_for_ui(),
+ 'is_bundle' => static::is_bundle_product(),
+ '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(),
+ 'manage_url' => static::get_manage_url(),
+ 'post_activation_url' => static::get_post_activation_url(),
+ 'class' => get_called_class(),
+ );
+ }
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ abstract public static function get_name();
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ abstract public static function get_title();
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ abstract public static function get_description();
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ abstract public static function get_long_description();
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array
+ */
+ abstract public static function get_features();
+
+ /**
+ * Get the product pricing
+ *
+ * @return array
+ */
+ abstract public static function get_pricing_for_ui();
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ abstract public static function get_manage_url();
+
+ /**
+ * Get the URL the user is taken after activating the product
+ *
+ * @return ?string
+ */
+ public static function get_post_activation_url() {
+ return static::get_manage_url();
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return null;
+ }
+
+ /**
+ * 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 product is a bundle.
+ *
+ * @return boolean True if product is a bundle. Otherwise, False.
+ */
+ public static function is_bundle_product() {
+ return false;
+ }
+
+ /**
+ * Check whether the product is upgradable
+ * by a product bundle.
+ *
+ * @return boolean|array Bundles list or False if not upgradable by a bundle.
+ */
+ public static function is_upgradable_by_bundle() {
+ return false;
+ }
+
+ /**
+ * In case it's a bundle product,
+ * return all the products it contains.
+ * Empty array by default.
+ *
+ * @return Array Product slugs
+ */
+ public static function get_supported_products() {
+ return array();
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return string
+ */
+ public static function get_status() {
+
+ if ( ! static::is_plugin_installed() ) {
+ $status = 'plugin_absent';
+ } 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() ) {
+ $status = 'error';
+ } elseif ( ! static::has_required_plan() ) {
+ $status = 'needs_purchase'; // We need needs_purchase here as well because some products we consider active without the required plan.
+ }
+ } elseif ( ! static::has_required_plan() ) {
+ $status = 'needs_purchase';
+ } else {
+ $status = 'inactive';
+ }
+ return $status;
+ }
+
+ /**
+ * Checks whether the Product is active
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return static::is_plugin_active() && static::has_required_plan();
+ }
+
+ /**
+ * Checks whether the plugin is installed
+ *
+ * @return boolean
+ */
+ public static function is_plugin_installed() {
+ return (bool) static::get_installed_plugin_filename();
+ }
+
+ /**
+ * Checks whether the plugin is active
+ *
+ * @return boolean
+ */
+ public static function is_plugin_active() {
+ return Plugins_Installer::is_plugin_active( static::get_installed_plugin_filename() );
+ }
+
+ /**
+ * Checks whether the Jetpack plugin is installed
+ *
+ * @return boolean
+ */
+ public static function is_jetpack_plugin_installed() {
+ return (bool) static::get_installed_plugin_filename( 'jetpack' );
+ }
+
+ /**
+ * Checks whether the Jetpack plugin is active
+ *
+ * @return boolean
+ */
+ public static function is_jetpack_plugin_active() {
+ return Plugins_Installer::is_plugin_active( static::get_installed_plugin_filename( 'jetpack' ) );
+ }
+
+ /**
+ * Activates the plugin
+ *
+ * @return null|WP_Error Null on success, WP_Error on invalid file.
+ */
+ public static function activate_plugin() {
+ return activate_plugin( static::get_installed_plugin_filename() );
+ }
+
+ /**
+ * Perform the top level activation routines, which is installing and activating the required plugin
+ *
+ * @return bool|WP_Error
+ */
+ private static function do_activation() {
+ if ( static::is_active() ) {
+ return true;
+ }
+
+ if ( ! static::is_plugin_installed() ) {
+ $installed = Plugins_Installer::install_plugin( static::get_plugin_slug() );
+ if ( is_wp_error( $installed ) ) {
+ return $installed;
+ }
+ }
+
+ if ( ! current_user_can( 'activate_plugins' ) ) {
+ return new WP_Error( 'not_allowed', __( 'You are not allowed to activate plugins on this site.', 'jetpack-my-jetpack' ) );
+ }
+
+ $result = static::activate_plugin();
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Activates the product by installing and activating its plugin
+ *
+ * @return boolean|WP_Error
+ */
+ final public static function activate() {
+
+ $result = self::do_activation();
+
+ $result = static::do_product_specific_activation( $result );
+
+ $product_slug = static::$slug;
+
+ /**
+ * Fires after My Jetpack activates a product and filters the result
+ * Use this filter to run additional routines for a product activation on stand-alone plugins
+ *
+ * @param bool|WP_Error $result The result of the previous steps of activation.
+ */
+ $result = apply_filters( "my_jetpack_{$product_slug}_activation", $result );
+
+ return $result;
+
+ }
+
+ /**
+ * Override this method to perform product specific activation routines.
+ *
+ * @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 bool|WP_Error
+ */
+ public static function do_product_specific_activation( $current_result ) {
+ return $current_result;
+ }
+
+ /**
+ * Deactivate the product
+ *
+ * @return boolean
+ */
+ public static function deactivate() {
+ deactivate_plugins( static::get_installed_plugin_filename() );
+ return true;
+ }
+
+ /**
+ * Returns filtered Jetpack plugin actions links.
+ *
+ * @param array $actions - Jetpack plugin action links.
+ * @return array Filtered Jetpack plugin actions links.
+ */
+ public static function get_plugin_actions_links( $actions ) {
+ // My Jetpack action link.
+ $my_jetpack_home_link = array(
+ 'jetpack-home' => sprintf(
+ '<a href="%1$s" title="%3$s">%2$s</a>',
+ admin_url( 'admin.php?page=my-jetpack' ),
+ __( 'My Jetpack', 'jetpack-my-jetpack' ),
+ __( 'My Jetpack dashboard', 'jetpack-my-jetpack' )
+ ),
+ );
+
+ // Otherwise, add it to the beginning of the array.
+ return array_merge( $my_jetpack_home_link, $actions );
+ }
+
+ /**
+ * Extend the plugin action links.
+ */
+ public static function extend_plugin_action_links() {
+
+ $filenames = static::get_plugin_filename();
+ if ( ! is_array( $filenames ) ) {
+ $filenames = array( $filenames );
+ }
+
+ foreach ( $filenames as $filename ) {
+ $hook = 'plugin_action_links_' . $filename;
+ $callback = array( static::class, 'get_plugin_actions_links' );
+ if ( ! has_filter( $hook, $callback ) ) {
+ add_filter( $hook, $callback, 20, 2 );
+ }
+ }
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-protect.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-protect.php
new file mode 100644
index 00000000..a4cca50a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-protect.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Protect product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Product;
+
+/**
+ * Class responsible for handling the Protect product
+ */
+class Protect extends Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'protect';
+
+ /**
+ * The filename (id) of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_filename = array(
+ 'jetpack-protect/jetpack-protect.php',
+ 'protect/jetpack-protect.php',
+ 'jetpack-protect-dev/jetpack-protect.php',
+ );
+
+ /**
+ * The slug of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'jetpack-protect';
+
+ /**
+ * Whether this product requires a user connection
+ *
+ * @var string
+ */
+ public static $requires_user_connection = false;
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Protect', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Protect', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Protect your site and scan for security vulnerabilities.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Protect your site and scan for security vulnerabilities listed in our database.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Protect features list
+ */
+ public static function get_features() {
+ return array(
+ __( 'Over 20,000 listed vulnerabilities', 'jetpack-my-jetpack' ),
+ __( 'Daily automatic scans', 'jetpack-my-jetpack' ),
+ __( 'Check plugin and theme version status', 'jetpack-my-jetpack' ),
+ __( 'Easy to navigate and use', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array(
+ 'available' => true,
+ 'is_free' => true,
+ );
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return admin_url( 'admin.php?page=jetpack-protect' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-scan.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-scan.php
new file mode 100644
index 00000000..452b1644
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-scan.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * Scan product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\My_Jetpack\Module_Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+use Automattic\Jetpack\Redirect;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * Class responsible for handling the Scan product
+ */
+class Scan extends Module_Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'scan';
+
+ /**
+ * The Jetpack module name
+ *
+ * @var string
+ */
+ public static $module_name = 'scan';
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Scan', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Scan', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Stay one step ahead of threats', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Automatic scanning and one-click fixes keep your site one step ahead of security threats and malware.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Scan features list
+ */
+ public static function get_features() {
+ return array(
+ _x( 'Automated daily scanning', 'Scan Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'One-click fixes for most issues', 'Scan Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Instant email notifications', 'Scan Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Access to latest Firewall rules', 'Scan Product Feature', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_scan';
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Checks whether the current plan (or purchases) of the site already supports the product
+ *
+ * @return boolean
+ */
+ public static function has_required_plan() {
+ $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;
+ }
+
+ /**
+ * Checks whether the Product is active
+ *
+ * Scan is not actually a module. Activation takes place on WPCOM. So lets consider it active if jetpack is active and has the plan.
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return static::is_jetpack_plugin_active() && static::has_required_plan();
+ }
+
+ /**
+ * 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
+ */
+ public static function do_product_specific_activation( $current_result ) {
+
+ $product_activation = parent::do_product_specific_activation( $current_result );
+
+ if ( is_wp_error( $product_activation ) && 'module_activation_failed' === $product_activation->get_error_code() ) {
+ // Scan is not a module. There's nothing in the plugin to be activated, so it's ok to fail to activate the module.
+ $product_activation = true;
+ }
+
+ return $product_activation;
+
+ }
+
+ /**
+ * Checks whether the Jetpack module is active
+ *
+ * Scan is not a module. Nothing needs to be active. Let's always consider it active.
+ *
+ * @return bool
+ */
+ public static function is_module_active() {
+ return true;
+ }
+
+ /**
+ * Return product bundles list
+ * that supports the product.
+ *
+ * @return boolean|array Products bundle list.
+ */
+ public static function is_upgradable_by_bundle() {
+ return array( 'security' );
+ }
+
+ /**
+ * Get the URL the user is taken after activating the product
+ *
+ * @return ?string
+ */
+ public static function get_post_activation_url() {
+ return ''; // stay in My Jetpack page.
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return Redirect::get_url( 'my-jetpack-manage-scan' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search-stats.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search-stats.php
new file mode 100644
index 00000000..66e81139
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search-stats.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Get search stats for use in the wp-admin dashboard.
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\Connection\Client;
+use Jetpack_Options;
+
+/**
+ * Search stats (e.g. post count, post type breakdown)
+ */
+class Search_Stats {
+ const CACHE_EXPIRY = 5 * MINUTE_IN_SECONDS;
+ const CACHE_GROUP = 'jetpack_search';
+ const COUNT_ESTIMATE_CACHE_KEY = 'count_estimate';
+
+ /**
+ * Get stats from the WordPress.com API for the current blog ID.
+ */
+ public function get_stats_from_wpcom() {
+ $blog_id = Jetpack_Options::get_option( 'id' );
+
+ if ( ! is_numeric( $blog_id ) ) {
+ return null;
+ }
+
+ $response = Client::wpcom_json_api_request_as_blog(
+ '/sites/' . (int) $blog_id . '/jetpack-search/stats',
+ '2',
+ array(),
+ null,
+ 'wpcom'
+ );
+
+ return $response;
+ }
+
+ /**
+ * Estimate record counts via a local database query.
+ */
+ public static function estimate_count() {
+ $cached_value = wp_cache_get( self::COUNT_ESTIMATE_CACHE_KEY, self::CACHE_GROUP );
+ if ( false !== $cached_value ) {
+ return $cached_value;
+ }
+
+ global $wpdb;
+ $indexable_statuses = get_post_stati( array( 'public' => true ) );
+ $unindexable_post_types = array_merge(
+ // Explicitly exclude various post types registered by plugins.
+ array(
+ 'elementor_library', // Used by Elementor.
+ 'jp_sitemap', // Used by Jetpack.
+ 'product_variation', // Used by Woocommerce.
+ 'redirect_rule', // Used by the Safe Redirect plugin.
+ 'reply', // Used by bbpress.
+ 'scheduled-action', // Used by Woocommerce.
+ ),
+ get_post_types(
+ array(
+ 'exclude_from_search' => true,
+ 'public' => false,
+ ),
+ 'names',
+ 'or'
+ )
+ );
+
+ $prep_for_query = function ( $string ) use ( $wpdb ) {
+ // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.QuotedSimplePlaceholder -- This is used to sanitize post type names.
+ return $wpdb->prepare( "'%s'", $string );
+ };
+
+ $statuses_list = implode( ',', array_map( $prep_for_query, $indexable_statuses ) );
+ $post_types_list = implode( ',', array_map( $prep_for_query, $unindexable_post_types ) );
+
+ $count = (int) $wpdb->get_var(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- This is properly prepared, but the query is constructed using variables.
+ "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status IN ($statuses_list) AND post_type NOT IN ($post_types_list)"
+ );
+
+ wp_cache_set( self::COUNT_ESTIMATE_CACHE_KEY, $count, self::CACHE_GROUP, self::CACHE_EXPIRY );
+ return $count;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search.php
new file mode 100644
index 00000000..fcd2654e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-search.php
@@ -0,0 +1,251 @@
+<?php
+/**
+ * Search product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\My_Jetpack\Hybrid_Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+use Automattic\Jetpack\Search\Module_Control as Search_Module_Control;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * Class responsible for handling the Search product
+ */
+class Search extends Hybrid_Product {
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'search';
+
+ /**
+ * The Jetpack module name
+ *
+ * @var string
+ */
+ public static $module_name = 'search';
+
+ /**
+ * The slug of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'jetpack-search';
+
+ /**
+ * The filename (id) of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_filename = array(
+ 'jetpack-search/jetpack-search.php',
+ 'search/jetpack-search.php',
+ 'jetpack-search-dev/jetpack-search.php',
+ );
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Search', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Search', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Help them find what they need', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Help your site visitors find answers instantly so they keep reading and buying. Great for sites with a lot of content.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Boost features list
+ */
+ public static function get_features() {
+ return array(
+ __( 'Instant search and indexing', 'jetpack-my-jetpack' ),
+ __( 'Powerful filtering', 'jetpack-my-jetpack' ),
+ __( 'Supports 29 languages', 'jetpack-my-jetpack' ),
+ __( 'Spelling correction', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ // Basic pricing info.
+ $pricing = array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+
+ $record_count = intval( Search_Stats::estimate_count() );
+
+ // Check whether the price is available.
+ // Bail early return the pricing info if not.
+ $product = Wpcom_Products::get_product( static::get_wpcom_product_slug() );
+ if ( ! isset( $product->price_tier_list ) ) {
+ return $pricing;
+ }
+
+ // Sort the tiers.
+ $price_tier_list = $product->price_tier_list;
+ array_multisort( array_column( $price_tier_list, 'maximum_units' ), SORT_ASC, $price_tier_list );
+
+ // Pick the first tier that is less than or equal to the record count.
+ foreach ( $product->price_tier_list as $price_tier ) {
+ if ( $record_count <= $price_tier->maximum_units ) {
+ break;
+ }
+ }
+
+ // Compute the minimum price.
+ $minimum_price = $price_tier->minimum_price / 100;
+
+ // Re define the display price based on the tier.
+ $pricing = Wpcom_Products::populate_with_discount( $product, $pricing, $minimum_price );
+
+ // 1. Flat fee in the same tier, so for search, `minimum_price == maximum_price`.
+ // 2. `maximum_units` is empty on the highest tier, so the logic displays the highest or the highest matching tier.
+ return array_merge(
+ $pricing,
+ array(
+ 'minimum_units' => $price_tier->minimum_units,
+ 'maximum_units' => $price_tier->maximum_units,
+ 'estimated_count' => $record_count,
+ 'full_price' => $minimum_price, // reset the full price to the minimum price.
+ )
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_search';
+ }
+
+ /**
+ * Hits the wpcom api to check Search status.
+ *
+ * @todo Maybe add caching.
+ *
+ * @return Object|WP_Error
+ */
+ private static function get_state_from_wpcom() {
+ static $status = null;
+
+ if ( $status !== null ) {
+ return $status;
+ }
+
+ $blog_id = Jetpack_Options::get_option( 'id' );
+
+ $response = Client::wpcom_json_api_request_as_blog(
+ '/sites/' . $blog_id . '/jetpack-search/plan',
+ '2',
+ array( 'timeout' => 2 ),
+ null,
+ 'wpcom'
+ );
+
+ if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return new WP_Error( 'search_state_fetch_failed' );
+ }
+
+ $body = wp_remote_retrieve_body( $response );
+ $status = json_decode( $body );
+ return $status;
+ }
+
+ /**
+ * Checks whether the current plan 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() {
+ $search_state = static::get_state_from_wpcom();
+ return ! empty( $search_state->supports_search ) || ! empty( $search_state->supports_instant_search );
+ }
+
+ /**
+ * Activates the product. Try to enable instant search after the Search module was enabled.
+ *
+ * @param bool|WP_Error $product_activation Is the result of the top level activation actions. You probably won't do anything if it is an WP_Error.
+ * @return bool|WP_Error
+ */
+ public static function do_product_specific_activation( $product_activation ) {
+ $product_activation = parent::do_product_specific_activation( $product_activation );
+ if ( is_wp_error( $product_activation ) ) {
+ return $product_activation;
+ }
+
+ if ( class_exists( 'Automattic\Jetpack\Search\Module_Control' ) ) {
+ ( new Search_Module_Control() )->enable_instant_search();
+ }
+
+ // we don't want to change the success of the activation if we fail to activate instant search. That's not mandatory.
+ return $product_activation;
+ }
+
+ /**
+ * Get the URL the user is taken after activating the product
+ *
+ * @return ?string
+ */
+ public static function get_post_activation_url() {
+ return ''; // stay in My Jetpack page or continue the purchase flow if needed.
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return admin_url( 'admin.php?page=jetpack-search' );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-security.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-security.php
new file mode 100644
index 00000000..f23cfdcf
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-security.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * Security product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\My_Jetpack\Module_Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * Class responsible for handling the Security product
+ */
+class Security extends Module_Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'security';
+
+ /**
+ * The Jetpack module name
+ *
+ * @var string
+ */
+ public static $module_name = 'security';
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Security', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Security', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Comprehensive site security, including Backup, Scan, and Anti-spam.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Comprehensive site security, including Backup, Scan, and Anti-spam.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Boost features list
+ */
+ public static function get_features() {
+ return array(
+ _x( 'Real-time cloud backups with 10GB storage', 'Security Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Automated real-time malware scan', 'Security Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'One-click fixes for most threats', 'Security Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Comment & form spam protection', 'Security Product Feature', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_security_t1_yearly';
+ }
+
+ /**
+ * Checks whether the Jetpack module is active
+ *
+ * This is a bundle and not a product. We should not use this information for anything
+ *
+ * @return bool
+ */
+ public static function is_module_active() {
+ return false;
+ }
+
+ /**
+ * 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
+ */
+ public static function do_product_specific_activation( $current_result ) {
+
+ $product_activation = parent::do_product_specific_activation( $current_result );
+
+ if ( is_wp_error( $product_activation ) && 'module_activation_failed' === $product_activation->get_error_code() ) {
+ // A bundle is not a module. There's nothing in the plugin to be activated, so it's ok to fail to activate the module.
+ $product_activation = true;
+ }
+
+ // At this point, Jetpack plugin is installed. Let's activate each individual product.
+ $activation = Anti_Spam::activate();
+ if ( is_wp_error( $activation ) ) {
+ return $activation;
+ }
+
+ $activation = Backup::activate();
+ if ( is_wp_error( $activation ) ) {
+ return $activation;
+ }
+
+ $activation = Scan::activate();
+ if ( is_wp_error( $activation ) ) {
+ return $activation;
+ }
+
+ return $activation;
+
+ }
+
+ /**
+ * Checks whether the Product is active
+ *
+ * Security is a bundle and not a module. Activation takes place on WPCOM. So lets consider it active if jetpack is active and has the plan.
+ *
+ * @return boolean
+ */
+ public static function is_active() {
+ return static::is_jetpack_plugin_active() && static::has_required_plan();
+ }
+
+ /**
+ * 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/purchases', $site_id ),
+ '1.1',
+ array(
+ 'method' => 'GET',
+ )
+ );
+ if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return new WP_Error( 'purchases_state_fetch_failed' );
+ }
+
+ $body = wp_remote_retrieve_body( $response );
+ $status = json_decode( $body );
+ return $status;
+ }
+
+ /**
+ * Checks whether the current plan (or purchases) of the site already supports the product
+ *
+ * @return boolean
+ */
+ public static function has_required_plan() {
+ $purchases_data = static::get_state_from_wpcom();
+ if ( is_wp_error( $purchases_data ) ) {
+ return false;
+ }
+ if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
+ foreach ( $purchases_data as $purchase ) {
+ if (
+ 0 === strpos( $purchase->product_slug, 'jetpack_security' ) ||
+ 0 === strpos( $purchase->product_slug, 'jetpack_complete' )
+ ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether product is a bundle.
+ *
+ * @return boolean True
+ */
+ public static function is_bundle_product() {
+ return true;
+ }
+
+ /**
+ * Return all the products it contains.
+ *
+ * @return Array Product slugs
+ */
+ public static function get_supported_products() {
+ return array( 'backup', 'scan', 'anti-spam' );
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ return '';
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-social.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-social.php
new file mode 100644
index 00000000..432c3501
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-social.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * Search product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Hybrid_Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+
+/**
+ * Class responsible for handling the Social product
+ */
+class Social extends Hybrid_Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'social';
+
+ /**
+ * The Jetpack module name
+ *
+ * @var string
+ */
+ public static $module_name = 'publicize';
+
+ /**
+ * The slug of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_slug = 'jetpack-social';
+
+ /**
+ * The filename (id) of the plugin associated with this product.
+ *
+ * @var string
+ */
+ public static $plugin_filename = array(
+ 'jetpack-social/jetpack-social.php',
+ 'social/jetpack-social.php',
+ 'jetpack-social-dev/jetpack-social.php',
+ );
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'Social', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack Social', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'Reach your audience on social media', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'Promote your content on social media by automatically publishing when you publish on your site.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Social features list
+ */
+ public static function get_features() {
+ return array(
+ __( 'Post to social networks', 'jetpack-my-jetpack' ),
+ __( 'Schedule publishing', 'jetpack-my-jetpack' ),
+ __( 'Supports the major social networks', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product pricing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_social';
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return string
+ */
+ public static function get_manage_url() {
+ if ( static::is_jetpack_plugin_active() ) {
+ return admin_url( 'admin.php?page=jetpack#/settings?term=publicize' );
+ } elseif ( static::is_plugin_active() ) {
+ return admin_url( 'admin.php?page=jetpack-social' );
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-videopress.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-videopress.php
new file mode 100644
index 00000000..d16152da
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/products/class-videopress.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * VideoPress product
+ *
+ * @package my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack\Products;
+
+use Automattic\Jetpack\My_Jetpack\Module_Product;
+use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
+
+/**
+ * Class responsible for handling the VideoPress product
+ */
+class Videopress extends Module_Product {
+
+ /**
+ * The product slug
+ *
+ * @var string
+ */
+ public static $slug = 'videopress';
+
+ /**
+ * The Jetpack module name
+ *
+ * @var string
+ */
+ public static $module_name = 'videopress';
+
+ /**
+ * Get the internationalized product name
+ *
+ * @return string
+ */
+ public static function get_name() {
+ return __( 'VideoPress', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product title
+ *
+ * @return string
+ */
+ public static function get_title() {
+ return __( 'Jetpack VideoPress', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product description
+ *
+ * @return string
+ */
+ public static function get_description() {
+ return __( 'High quality, ad-free video', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized product long description
+ *
+ * @return string
+ */
+ public static function get_long_description() {
+ return __( 'High-quality, ad-free video built specifically for WordPress.', 'jetpack-my-jetpack' );
+ }
+
+ /**
+ * Get the internationalized features list
+ *
+ * @return array Boost features list
+ */
+ public static function get_features() {
+ return array(
+ _x( '1TB of storage', 'VideoPress Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Built into WordPress editor', 'VideoPress Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Ad-free and brandable player', 'VideoPress Product Feature', 'jetpack-my-jetpack' ),
+ _x( 'Unlimited users', 'VideoPress Product Feature', 'jetpack-my-jetpack' ),
+ );
+ }
+
+ /**
+ * Get the product princing details
+ *
+ * @return array Pricing details
+ */
+ public static function get_pricing_for_ui() {
+ return array_merge(
+ array(
+ 'available' => true,
+ 'wpcom_product_slug' => static::get_wpcom_product_slug(),
+ ),
+ Wpcom_Products::get_product_pricing( static::get_wpcom_product_slug() )
+ );
+ }
+
+ /**
+ * Get the WPCOM product slug used to make the purchase
+ *
+ * @return ?string
+ */
+ public static function get_wpcom_product_slug() {
+ return 'jetpack_videopress';
+ }
+
+ /**
+ * Get the URL the user is taken after activating the product
+ *
+ * @return ?string
+ */
+ public static function get_post_activation_url() {
+ return ''; // stay in My Jetpack page.
+ }
+
+ /**
+ * Get the URL where the user manages the product
+ *
+ * @return ?string
+ */
+ public static function get_manage_url() {
+ if ( static::is_active() ) {
+ return admin_url( 'admin.php?page=jetpack#/settings?term=videopress' );
+ }
+ }
+}