installed plugin Jetpack Protect version 1.0.2

This commit is contained in:
2022-07-28 18:42:13 +00:00
committed by Gitium
parent d55c4af45c
commit a3483bf62f
286 changed files with 64090 additions and 0 deletions

View File

@ -0,0 +1,302 @@
<?php
/**
* WP Admin page with information and configuration shared among all Jetpack stand-alone plugins
*
* @package automattic/my-jetpack
*/
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.
*/
class Initializer {
/**
* My Jetpack package version
*
* @var string
*/
const PACKAGE_VERSION = '1.8.2';
/**
* Initialize My Jetapack
*
* @return void
*/
public static function init() {
if ( ! self::should_initialize() || did_action( 'my_jetpack_init' ) ) {
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' ),
'manage_options',
'my-jetpack',
array( __CLASS__, 'admin_page' ),
999
);
add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
/**
* Fires after the My Jetpack package is initialized
*
* @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_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_scripts() {
Assets::register_script(
'my_jetpack_main_app',
'../build/index.js',
__FILE__,
array(
'enqueue' => true,
'in_footer' => true,
'textdomain' => 'jetpack-my-jetpack',
)
);
wp_localize_script(
'my_jetpack_main_app',
'myJetpackInitialState',
array(
'products' => array(
'items' => Products::get_products(),
),
'purchases' => array(
'items' => array(),
),
'myJetpackUrl' => 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 );
}
}
/**
* 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;
}
}

View File

@ -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();
}
}

View File

@ -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 );
}
}

View File

@ -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'] ) || 200 !== $response_code ) {
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 );
}
}

View File

@ -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;
}
}

View File

@ -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' );
}
}

View File

@ -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();
}
}

View File

@ -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 worlds 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' );
}
}

View File

@ -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' );
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -0,0 +1,438 @@
<?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,
'plugin_slug' => static::$plugin_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 );
}
}
}
}

View File

@ -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' );
}
}

View File

@ -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' );
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,253 @@
<?php
/**
* Search product
*
* @package my-jetpack
*/
namespace Automattic\Jetpack\My_Jetpack\Products;
use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Constants;
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() );
$search_pricing = static::get_pricing_from_wpcom( $record_count );
if ( is_wp_error( $search_pricing ) ) {
return $pricing;
}
return array_merge( $pricing, $search_pricing );
}
/**
* Get the WPCOM product slug used to make the purchase
*
* @return ?string
*/
public static function get_wpcom_product_slug() {
return 'jetpack_search';
}
/**
* Use centralized Search pricing API.
*
* The function is also used by the search package, as a result it could be called before site connection - i.e. blog token might not be available.
*
* @param int $record_count Record count to estimate pricing.
*
* @return array|WP_Error
*/
public static function get_pricing_from_wpcom( $record_count ) {
static $pricings = array();
if ( isset( $pricings[ $record_count ] ) ) {
return $pricings[ $record_count ];
}
$response = wp_remote_get(
sprintf( Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ) . '/wpcom/v2/jetpack-search/pricing?record_count=%1$d&locale=%2$s', $record_count, get_user_locale() ),
array( 'timeout' => 2 )
);
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
return new WP_Error( 'search_pricing_fetch_failed' );
}
$body = wp_remote_retrieve_body( $response );
$pricings[ $record_count ] = json_decode( $body, true );
return $pricings[ $record_count ];
}
/**
* 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' );
}
}

View File

@ -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 '';
}
}

View File

@ -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' );
}
}
}

View File

@ -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' );
}
}
}