installed plugin Jetpack Protect
version 1.0.2
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
|
||||
}
|
@ -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 '';
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
||||
}
|
@ -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' );
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user