updated plugin `Jetpack Protect` version 2.1.0

This commit is contained in:
KawaiiPunk 2024-04-19 10:49:36 +00:00 committed by Gitium
parent 620280b550
commit 7841fd5dc6
179 changed files with 6360 additions and 1476 deletions

View File

@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 2.1.0 - 2024-04-10
### Added
- Add data to WAF logs and add toggle for users to opt-in to share more data with us if needed. [#36377]
- Added firewall standalone mode indicator. [#34840]
- Added onboarding flows. [#34649]
### Changed
- General: indicate compatibility with the upcoming version of WordPress, 6.5. [#35820]
- Updated package dependencies. [#36325]
- Updated package lockfile. [#35672]
- Use blog ID instead of site slug in checkout links. [#35004]
### Fixed
- Jetpack Protect footer: Ensured that links to the cloud and the promotion around it are not shown if you are on a platform where the firewall is not supported. [#36794]
- Prevent text orphans in the site scanning header. [#35935]
## 2.0.0 - 2024-01-18
### Changed
- Firewall: use datetime versioning for rules file updates. [#34698]

View File

@ -1 +1 @@
<?php return array('dependencies' => array('moment', 'react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => '69b5f82ff2ab6a921833');
<?php return array('dependencies' => array('moment', 'react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => 'c12994dcc943f0a86ea0');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,23 +5,23 @@
"license": "GPL-2.0-or-later",
"require": {
"ext-json": "*",
"automattic/jetpack-assets": "^2.0.4",
"automattic/jetpack-admin-ui": "^0.3.1",
"automattic/jetpack-autoloader": "^3.0.2",
"automattic/jetpack-composer-plugin": "^2.0.0",
"automattic/jetpack-config": "^2.0.0",
"automattic/jetpack-identity-crisis": "^0.15.0",
"automattic/jetpack-my-jetpack": "^4.6.0",
"automattic/jetpack-plugins-installer": "^0.3.1",
"automattic/jetpack-sync": "^2.4.2",
"automattic/jetpack-transport-helper": "^0.2.0",
"automattic/jetpack-plans": "^0.4.1",
"automattic/jetpack-waf": "^0.12.4",
"automattic/jetpack-status": "^2.1.0"
"automattic/jetpack-assets": "^2.1.4",
"automattic/jetpack-admin-ui": "^0.4.1",
"automattic/jetpack-autoloader": "^3.0.3",
"automattic/jetpack-composer-plugin": "^2.0.1",
"automattic/jetpack-config": "^2.0.1",
"automattic/jetpack-identity-crisis": "^0.17.3",
"automattic/jetpack-my-jetpack": "^4.17.0",
"automattic/jetpack-plugins-installer": "^0.3.2",
"automattic/jetpack-sync": "^2.10.1",
"automattic/jetpack-transport-helper": "^0.2.2",
"automattic/jetpack-plans": "^0.4.3",
"automattic/jetpack-waf": "^0.16.0.1",
"automattic/jetpack-status": "^2.1.2"
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.5",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/wordbless": "0.4.2"
},
"autoload": {
@ -71,6 +71,6 @@
"automattic/jetpack-autoloader": true,
"automattic/jetpack-composer-plugin": true
},
"autoloader-suffix": "c4802e05bbcf59fd3b6350e8d3e5482c_protectⓥ2_0_0"
"autoloader-suffix": "c4802e05bbcf59fd3b6350e8d3e5482c_protectⓥ2_1_0"
}
}

View File

@ -3,7 +3,7 @@
* Plugin Name: Jetpack Protect
* Plugin URI: https://wordpress.org/plugins/jetpack-protect
* Description: Security tools that keep your site safe and sound, from posts to plugins.
* Version: 2.0.0
* Version: 2.1.0
* Author: Automattic - Jetpack Security team
* Author URI: https://jetpack.com/protect/
* License: GPLv2 or later
@ -32,7 +32,7 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
define( 'JETPACK_PROTECT_VERSION', '2.0.0' );
define( 'JETPACK_PROTECT_VERSION', '2.1.0' );
define( 'JETPACK_PROTECT_DIR', plugin_dir_path( __FILE__ ) );
define( 'JETPACK_PROTECT_ROOT_FILE', __FILE__ );
define( 'JETPACK_PROTECT_ROOT_FILE_RELATIVE_PATH', plugin_basename( __FILE__ ) );

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.1] - 2024-03-12
### Changed
- Internal updates.
## [2.0.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
@ -131,6 +135,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Creates the MC Stats package
[2.0.1]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.22...v2.0.0
[1.4.22]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.21...v1.4.22
[1.4.21]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.20...v1.4.21

View File

@ -8,7 +8,7 @@
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.0"
"automattic/jetpack-changelogger": "^4.1.1"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.1] - 2024-03-12
### Changed
- Internal updates.
## [0.4.0] - 2024-03-01
### Added
- Register menus in network admin as well as regular admin. [#36058]
## [0.3.2] - 2024-01-29
### Fixed
- Wait until 'admin_menu' action to call `add_menu()`, to avoid triggering the l10n load too early. [#35279]
## [0.3.1] - 2023-11-24
## [0.3.0] - 2023-11-20
@ -132,6 +144,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fixing menu visibility issues.
[0.4.1]: https://github.com/Automattic/jetpack-admin-ui/compare/0.4.0...0.4.1
[0.4.0]: https://github.com/Automattic/jetpack-admin-ui/compare/0.3.2...0.4.0
[0.3.2]: https://github.com/Automattic/jetpack-admin-ui/compare/0.3.1...0.3.2
[0.3.1]: https://github.com/Automattic/jetpack-admin-ui/compare/0.3.0...0.3.1
[0.3.0]: https://github.com/Automattic/jetpack-admin-ui/compare/0.2.25...0.3.0
[0.2.25]: https://github.com/Automattic/jetpack-admin-ui/compare/0.2.24...0.2.25

View File

@ -8,8 +8,8 @@
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.3",
"automattic/jetpack-logo": "^2.0.0",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/jetpack-logo": "^2.0.1",
"automattic/wordbless": "dev-master"
},
"suggest": {
@ -40,7 +40,7 @@
"link-template": "https://github.com/Automattic/jetpack-admin-ui/compare/${old}...${new}"
},
"branch-alias": {
"dev-trunk": "0.3.x-dev"
"dev-trunk": "0.4.x-dev"
},
"version-constants": {
"::PACKAGE_VERSION": "src/class-admin-menu.php"

View File

@ -13,7 +13,7 @@ namespace Automattic\Jetpack\Admin_UI;
*/
class Admin_Menu {
const PACKAGE_VERSION = '0.3.1';
const PACKAGE_VERSION = '0.4.1';
/**
* Whether this class has been initialized
@ -39,6 +39,7 @@ class Admin_Menu {
self::$initialized = true;
self::handle_akismet_menu();
add_action( 'admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998.
add_action( 'network_admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998.
}
}
@ -50,23 +51,23 @@ class Admin_Menu {
*/
private static function handle_akismet_menu() {
if ( class_exists( 'Akismet_Admin' ) ) {
// Prevent Akismet from adding a menu item.
add_action(
'admin_menu',
function () {
// Prevent Akismet from adding a menu item.
remove_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 );
// Add an Anti-spam menu item for Jetpack.
self::add_menu( __( 'Akismet Anti-spam', 'jetpack-admin-ui' ), __( 'Akismet Anti-spam', 'jetpack-admin-ui' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
},
4
);
// Add an Anti-spam menu item for Jetpack.
self::add_menu( __( 'Akismet Anti-spam', 'jetpack-admin-ui' ), __( 'Akismet Anti-spam', 'jetpack-admin-ui' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) );
}
}
/**
* Callback to the admin_menu hook that will register the enqueued menu items
* Callback to the admin_menu and network_admin_menu hooks that will register the enqueued menu items
*
* @return void
*/

View File

@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.1.4] - 2024-03-12
### Changed
- Internal updates.
## [2.1.3] - 2024-03-12
### Changed
- Updated package dependencies. [#36325]
## [2.1.2] - 2024-03-04
### Changed
- Updated package dependencies. [#36095]
## [2.1.1] - 2024-02-13
### Changed
- Updated package dependencies. [#35608]
## [2.1.0] - 2024-02-05
### Added
- Add support for script enqueuing strategies implemented in WordPress 6.3 [#34072]
### Changed
- Updated package dependencies. [#35384]
## [2.0.4] - 2024-01-04
### Changed
- Updated package dependencies. [#34815]
@ -396,6 +419,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Statically access asset tools
[2.1.4]: https://github.com/Automattic/jetpack-assets/compare/v2.1.3...v2.1.4
[2.1.3]: https://github.com/Automattic/jetpack-assets/compare/v2.1.2...v2.1.3
[2.1.2]: https://github.com/Automattic/jetpack-assets/compare/v2.1.1...v2.1.2
[2.1.1]: https://github.com/Automattic/jetpack-assets/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/Automattic/jetpack-assets/compare/v2.0.4...v2.1.0
[2.0.4]: https://github.com/Automattic/jetpack-assets/compare/v2.0.3...v2.0.4
[2.0.3]: https://github.com/Automattic/jetpack-assets/compare/v2.0.2...v2.0.3
[2.0.2]: https://github.com/Automattic/jetpack-assets/compare/v2.0.1...v2.0.2

View File

@ -1 +1 @@
<?php return array('dependencies' => array('wp-i18n'), 'version' => 'ee939953aa2115e2ca59');
<?php return array('dependencies' => array('wp-i18n'), 'version' => 'b5d2a25bb8ad1698db1c');

View File

@ -5,12 +5,12 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-constants": "^2.0.0"
"automattic/jetpack-constants": "^2.0.1"
},
"require-dev": {
"brain/monkey": "2.6.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.5",
"automattic/jetpack-changelogger": "^4.1.1",
"wikimedia/testing-access-wrapper": "^1.0 || ^2.0 || ^3.0"
},
"suggest": {
@ -51,7 +51,7 @@
"link-template": "https://github.com/Automattic/jetpack-assets/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "2.0.x-dev"
"dev-trunk": "2.1.x-dev"
}
}
}

View File

@ -54,40 +54,35 @@ class Assets {
public static function instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new Assets();
self::$instance->init_hooks();
}
return self::$instance;
}
/**
* Initalize the hooks as needed.
*/
private function init_hooks() {
/*
* Load some scripts asynchronously.
*/
add_filter( 'script_loader_tag', array( $this, 'script_add_async' ), 10, 2 );
}
/**
* A public method for adding the async script.
*
* @deprecated Since 2.1.0, the `strategy` feature should be used instead, with the "defer" setting.
*
* @param string $script_handle Script handle.
*/
public static function add_async_script( $script_handle ) {
$assets_instance = self::instance();
$assets_instance->defer_script_handles[] = $script_handle;
_deprecated_function( __METHOD__, '2.1.0' );
wp_script_add_data( $script_handle, 'strategy', 'defer' );
}
/**
* Add an async attribute to scripts that can be loaded deferred.
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*
* @deprecated Since 2.1.0, the `strategy` feature should be used instead.
*
* @param string $tag The <script> tag for the enqueued script.
* @param string $handle The script's registered handle.
*/
public function script_add_async( $tag, $handle ) {
_deprecated_function( __METHOD__, '2.1.0' );
if ( empty( $this->defer_script_handles ) ) {
return $tag;
}
@ -103,6 +98,8 @@ class Assets {
/**
* A helper function that lets you enqueue scripts in an async fashion.
*
* @deprecated Since 2.1.0 - use the strategy feature instead.
*
* @param string $handle Name of the script. Should be unique.
* @param string $min_path Minimized script path.
* @param string $non_min_path Full Script path.
@ -111,6 +108,7 @@ class Assets {
* @param bool $in_footer Should the script be included in the footer.
*/
public static function enqueue_async_script( $handle, $min_path, $non_min_path, $deps = array(), $ver = false, $in_footer = true ) {
_deprecated_function( __METHOD__, '2.1.0' );
$assets_instance = self::instance();
$assets_instance->add_async_script( $handle );
wp_enqueue_script( $handle, self::get_file_url_for_environment( $min_path, $non_min_path ), $deps, $ver, $in_footer );
@ -307,12 +305,13 @@ class Assets {
* This wrapper handles all of that.
*
* @since 1.12.0
* @since 2.1.0 Add a new `strategy` option to leverage WP >= 6.3 script strategy feature. The `async` option is deprecated.
* @param string $handle Name of the script. Should be unique across both scripts and styles.
* @param string $path Minimized script path.
* @param string $relative_to File that `$path` is relative to. Pass `__FILE__`.
* @param array $options Additional options:
* - `asset_path`: (string|null) `.asset.php` to load. Default is to base it on `$path`.
* - `async`: (bool) Set true to register the script as async, like `Assets::enqueue_async_script()`
* - `async`: (bool) Set true to register the script as deferred, like `Assets::enqueue_async_script()`. Deprecated in favor of `strategy`.
* - `css_dependencies`: (string[]) Additional style dependencies to queue.
* - `css_path`: (string|null) `.css` to load. Default is to base it on `$path`.
* - `dependencies`: (string[]) Additional script dependencies to queue.
@ -321,6 +320,7 @@ class Assets {
* - `media`: (string) Media for the css file. Default 'all'.
* - `minify`: (bool|null) Set true to pass `minify=true` in the query string, or `null` to suppress the normal `minify=false`.
* - `nonmin_path`: (string) Non-minified script path.
* - `strategy`: (string) Specify a script strategy to use, eg. `defer` or `async`. Default is `""`.
* - `textdomain`: (string) Text domain for the script. Required if the script depends on wp-i18n.
* - `version`: (string) Override the version from the `asset_path` file.
* @throws \InvalidArgumentException If arguments are invalid.
@ -330,6 +330,10 @@ class Assets {
throw new \InvalidArgumentException( '$path must end in ".js"' );
}
if ( isset( $options['async'] ) ) {
_deprecated_argument( __METHOD__, '2.1.0', 'The `async` option is deprecated in favor of `strategy`' );
}
$dir = dirname( $relative_to );
$base = substr( $path, 0, -3 );
$options += array(
@ -342,6 +346,7 @@ class Assets {
'in_footer' => false,
'media' => 'all',
'minify' => false,
'strategy' => '',
'textdomain' => null,
);
@ -376,10 +381,20 @@ class Assets {
$ver = isset( $options['version'] ) ? $options['version'] : filemtime( "$dir/$path" );
}
wp_register_script( $handle, $url, $options['dependencies'], $ver, $options['in_footer'] );
if ( $options['async'] ) {
self::instance()->add_async_script( $handle );
if ( $options['async'] && '' === $options['strategy'] ) { // Handle the deprecated `async` option
$options['strategy'] = 'defer';
}
wp_register_script(
$handle,
$url,
$options['dependencies'],
$ver,
array(
'in_footer' => $options['in_footer'],
'strategy' => $options['strategy'],
)
);
if ( $options['textdomain'] ) {
// phpcs:ignore Jetpack.Functions.I18n.DomainNotLiteral
wp_set_script_translations( $handle, $options['textdomain'] );

View File

@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.2.4] - 2024-03-18
### Changed
- Internal updates.
## [0.2.3] - 2024-03-14
### Changed
- Internal updates.
## [0.2.2] - 2024-02-27
### Added
- Increasing backup version for new endpoint [#35649]
## [0.2.1] - 2024-02-08
### Fixed
- Write helper script to ABSPATH by default, just like we did before [#35508]
## [0.2.0] - 2024-01-04
### Fixed
- Backup: Add namespace versioning to Helper_Script_Manager and other classes. [#34739]
@ -13,4 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Initial release (improved helper script installer logging). [#34297]
[0.2.4]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.1.0...v0.2.0

View File

@ -8,7 +8,7 @@
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.5",
"automattic/jetpack-changelogger": "^4.1.2",
"automattic/wordbless": "@dev"
},
"suggest": {

View File

@ -9,7 +9,7 @@
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
// are installed, or in some other cases).
namespace Automattic\Jetpack\Backup\V0001;
namespace Automattic\Jetpack\Backup\V0003;
use Exception;
use WP_Error;
@ -123,9 +123,21 @@ class Helper_Script_Manager_Impl {
$locations = array();
// Prioritize trying to write to "wp-content/" and "wp-content/uploads/" first, because those locations are
// expected to be writable more often (unlike ABSPATH), and ABSPATH on some setups might have a weird value
// which doesn't point to document root.
// Prioritize ABSPATH first, because even though ABSPATH constant's value might be weird sometimes, it's the
// path where the PHP scripts will be most likely be able to get executed.
try {
if ( Throw_On_Errors::t_is_dir( ABSPATH ) ) {
$abspath_dir = Throw_On_Errors::t_realpath( ABSPATH );
$locations[ $abspath_dir ] = $abspath_url;
}
} catch ( Exception $exception ) {
$locations[ ABSPATH ] = new WP_Error(
'abspath_missing',
'Unable to access WordPress root "' . ABSPATH . '": ' . $exception->getMessage(),
array( 'status' => 500 )
);
}
try {
if ( Throw_On_Errors::t_is_dir( WP_CONTENT_DIR ) ) {
@ -180,19 +192,6 @@ class Helper_Script_Manager_Impl {
);
}
try {
if ( Throw_On_Errors::t_is_dir( ABSPATH ) ) {
$abspath_dir = Throw_On_Errors::t_realpath( ABSPATH );
$locations[ $abspath_dir ] = $abspath_url;
}
} catch ( Exception $exception ) {
$locations[ ABSPATH ] = new WP_Error(
'abspath_missing',
'Unable to access WordPress root "' . ABSPATH . '": ' . $exception->getMessage(),
array( 'status' => 500 )
);
}
return $locations;
}

View File

@ -9,7 +9,7 @@
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
// are installed, or in some other cases).
namespace Automattic\Jetpack\Backup\V0001;
namespace Automattic\Jetpack\Backup\V0003;
/**
* Manage installation, deletion and cleanup of Helper Scripts to assist with backing up Jetpack Sites.

View File

@ -8,7 +8,7 @@
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
// are installed, or in some other cases).
namespace Automattic\Jetpack\Backup\V0001;
namespace Automattic\Jetpack\Backup\V0003;
use Exception;
use Throwable;
@ -57,7 +57,7 @@ class Throw_On_Errors {
* @param int|null $errline Line number where the error was raised.
* @param array|null $errcontext Deprecated, unused.
*
* @return mixed
* @return never
* @throws Exception
*/
// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable

View File

@ -0,0 +1,60 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.2.5] - 2024-03-14
### Changed
- Internal updates.
## [0.2.4] - 2024-01-25
### Fixed
- Chage get_client() visibility so that older versions of boost do not break. [#35240]
## [0.2.3] - 2024-01-22
### Added
- Send current boost version with API requests to handle requests accordingly [#35132]
### Changed
- Jetpack Boost: Use ARRAY_A when decoding from JSON [#35062]
## [0.2.2] - 2023-11-24
### Changed
- Replaced usage of strpos() with str_contains(). [#34137]
- Replaced usage of substr() with str_starts_with() and str_ends_with(). [#34207]
## [0.2.1] - 2023-11-21
### Fixed
- Made abstract static methods in `Automattic\Jetpack\Boost_Core\Lib\Cacheable` abstract, instead of being implemented to always throw. [#34220]
## [0.2.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
## [0.1.3] - 2023-09-19
- Minor internal updates.
## [0.1.2] - 2023-09-01
### Fixed
- Fix showing default error message and code when parsing cloud response. [#32685]
## [0.1.1] - 2023-08-28
### Changed
- Updated package dependencies. [#32605]
## 0.1.0 - 2023-06-06
### Added
- Introduce new package. [#31163]
[0.2.5]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/Automattic/jetpack-boost-core/compare/v0.1.3...v0.2.0
[0.1.3]: https://github.com/Automattic/jetpack-boost-core/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/Automattic/jetpack-boost-core/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/Automattic/jetpack-boost-core/compare/v0.1.0...v0.1.1

View File

@ -0,0 +1,357 @@
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,47 @@
# Security Policy
Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
## Supported Versions
Generally, only the latest version of Jetpack and its associated plugins have continued support. If a critical vulnerability is found in the current version of a plugin, we may opt to backport any patches to previous versions.
## Reporting a Vulnerability
Our HackerOne program covers the below plugin software, as well as a variety of related projects and infrastructure:
* [Jetpack](https://jetpack.com/)
* Jetpack Backup
* Jetpack Boost
* Jetpack CRM
* Jetpack Protect
* Jetpack Search
* Jetpack Social
* Jetpack VideoPress
**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
Our most critical targets are:
* Jetpack and the Jetpack composer packages (all within this repo)
* Jetpack.com -- the primary marketing site.
* cloud.jetpack.com -- a management site.
* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
## Guidelines
We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
* Pen-testing Production:
* Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
* If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
* **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
* To be eligible for a bounty, all of these guidelines must be followed.
* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.

View File

@ -0,0 +1,52 @@
{
"name": "automattic/jetpack-boost-core",
"description": "Core functionality for boost and relevant packages to depend on",
"type": "jetpack-library",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0"
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/wordbless": "dev-master"
},
"autoload": {
"classmap": [
"src/"
]
},
"scripts": {
"phpunit": [
"./vendor/phpunit/phpunit/phpunit --colors=always"
],
"test-php": [
"@composer phpunit"
],
"build-production": "echo 'Add your build step to composer.json, please!'",
"build-development": "echo 'Add your build step to composer.json, please!'",
"post-install-cmd": "WorDBless\\Composer\\InstallDropin::copy",
"post-update-cmd": "WorDBless\\Composer\\InstallDropin::copy"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"allow-plugins": {
"roots/wordpress-core-installer": true
}
},
"extra": {
"mirror-repo": "Automattic/jetpack-boost-core",
"changelogger": {
"link-template": "https://github.com/Automattic/jetpack-boost-core/compare/v${old}...v${new}"
},
"autotagger": true,
"branch-alias": {
"dev-trunk": "0.2.x-dev"
},
"textdomain": "jetpack-boost-core"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Boost API Client interface.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Contracts;
/**
* An interface to build Boost API client.
*
* Communication with Boost back-end should be done through this interface.
*/
interface Boost_API_Client {
/**
* Submit a request to boost API and return response.
*
* @param string $path - Request path.
* @param mixed[] $payload - Request arguments.
* @return mixed
*/
public function post( $path, $payload = array() );
/**
* Make a get request to boost API and return response.
*
* @param string $path - Request path.
* @param mixed[] $query - Query parameters.
* @return mixed
*/
public function get( $path, $query = array() );
}

View File

@ -0,0 +1,110 @@
<?php
/**
* A helper class to help with Boost API interactions.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Lib;
use Automattic\Jetpack\Boost_Core\Contracts\Boost_API_Client;
/**
* A class that handles the Boost API client.
*
* The communication to the backend is done using this class on top of the Boost_API_Client interface.
*/
class Boost_API {
/**
* The API client instance.
*
* @var Boost_API_Client
*/
private static $api_client;
/**
* Get the API client instance.
*
* @return Boost_API_Client
* @deprecated $$next_version$$ Use get(), and post() directly instead.
*/
public static function get_client() {
return self::get_api_client();
}
/**
* Instantiate the API client.
*
* @return Boost_API_Client
*/
private static function get_api_client() {
if ( ! self::$api_client ) {
$class = apply_filters( 'jetpack_boost_api_client_class', WPCOM_Boost_API_Client::class );
self::$api_client = new $class();
}
return self::$api_client;
}
/**
* Make a get request to boost API and return response.
*
* @param string $path - Request path.
* @param mixed[] $query - Query parameters.
* @param mixed[] $args - Request arguments.
* @return array|\WP_Error
*/
public static function get( $path, $query = array(), $args = null ) {
return self::get_api_client()->get( $path, $query, self::merge_args( $args ) );
}
/**
* Submit a request to boost API and return response.
*
* @param string $path - Request path.
* @param mixed[] $payload - Request arguments.
* @param mixed[] $args - Request arguments.
* @return mixed
*/
public static function post( $path, $payload = array(), $args = null ) {
return self::get_api_client()->post( $path, $payload, self::merge_args( $args ) );
}
/**
* Merge the arguments with the defaults.
*
* @param mixed[] $args - Provided arguments.
* @return mixed[]
*/
private static function merge_args( $args ) {
if ( ! is_array( $args ) ) {
$args = wp_parse_args(
$args,
array(
'headers' => self::default_headers(),
)
);
} else {
$args['headers'] = wp_parse_args( $args['headers'] ?? array(), self::default_headers() );
}
return $args;
}
/**
* Get the default headers to include with each request.
*
* @return string[]
*/
public static function default_headers() {
$headers = array(
'Content-Type' => 'application/json; charset=utf-8',
);
if ( defined( 'JETPACK_BOOST_VERSION' ) ) {
$headers['X-Jetpack-Boost-Version'] = JETPACK_BOOST_VERSION;
}
return $headers;
}
}

View File

@ -0,0 +1,134 @@
<?php
/**
* Base abstract class for cacheable value objects.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Lib;
/**
* Class Cacheable.
*/
abstract class Cacheable implements \JsonSerializable {
/**
* Default cache expiry.
*/
const DEFAULT_EXPIRY = 300; // 5 minutes.
/**
* The ID of this object, if cached as a transient.
*
* @var string|null $cache_id Cache id.
*/
private $cache_id;
/**
* Store the object.
*
* @param int $expiry Expiry in seconds.
*
* @return mixed|void
*/
public function store( $expiry = self::DEFAULT_EXPIRY ) {
if ( ! $this->cache_id ) {
$this->cache_id = $this->generate_cache_id();
}
Transient::set(
static::cache_prefix() . $this->cache_id,
$this->jsonSerialize(),
$expiry
);
return $this->cache_id;
}
/**
* Default implementation for generating cache key. Generates a random ASCII string.
*/
protected function generate_cache_id() {
return wp_generate_password( 20, false );
}
/**
* This is intended to be the reverse of JsonSerializable->jsonSerialize.
*
* @param array $data Serialized data to restore the object from.
*
* @throws \Exception Throw an exception to remind to implement the method in child classes.
*/
abstract public static function jsonUnserialize( $data );
/**
* Fetch an object with the given ID.
*
* @param string $id The object ID.
*
* @return null|mixed
*/
public static function get( $id ) {
$data = Transient::get( static::cache_prefix() . $id, false );
if ( ! $data ) {
return null;
}
$class = get_called_class();
$object = $class::jsonUnserialize( $data, $id );
if ( $object ) {
$object->set_cache_id( $id );
}
return $object;
}
/**
* Set cache id.
*
* This is here so we know how this object was loaded.
*
* @param string $cache_id Cache id.
*/
public function set_cache_id( $cache_id ) {
$this->cache_id = $cache_id;
}
/**
* Getter for the cache ID.
*
* @return string
*/
public function get_cache_id() {
return $this->cache_id;
}
/**
* Delete the cache entry.
*/
public function delete() {
$this->cache_id && static::delete_by_cache_id( $this->cache_id );
}
/**
* Delete all currently cached entries.
*/
public static function clear_cache() {
Transient::delete_by_prefix( static::cache_prefix() );
}
/**
* Delete the cache entry for the given cache id.
*
* @param string $cache_id The cache ID.
*/
public static function delete_by_cache_id( $cache_id ) {
Transient::delete( static::cache_prefix() . $cache_id );
}
/**
* Returns the cache prefix
*
* @throws \Exception Throw an exception to remind to implement the method in child classes.
*/
abstract protected static function cache_prefix();
}

View File

@ -0,0 +1,122 @@
<?php
/**
* Transients for Jetpack Boost.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Lib;
/**
* Class Transient
*/
class Transient {
const OPTION_PREFIX = 'jb_transient_';
/**
* Get the key with prefix.
*
* @param string $key the key to be prefixed.
*/
public static function key( $key ) {
return static::OPTION_PREFIX . $key;
}
/**
* Updates a cache entry. Creates the cache entry if it doesn't exist.
*
* @param string $key Cache key name.
* @param mixed $value Cache value.
* @param int $expiry Cache expiration in seconds.
*
* @return void
*/
public static function set( $key, $value, $expiry = 0 ) {
if ( 0 === $expiry ) {
$expiry = YEAR_IN_SECONDS;
}
$data = array(
'expire' => time() + $expiry,
'data' => $value,
);
update_option( self::key( $key ), $data, false );
}
/**
* Gets a cache entry.
*
* @param string $key Cache key name.
* @param mixed $default Default value.
*
* @return mixed
*/
public static function get( $key, $default = null ) {
// Ensure everything's there.
$option = get_option( self::key( $key ), $default );
if ( $default === $option || ! isset( $option['expire'] ) || ! isset( $option['data'] )
) {
return $default;
}
// Maybe expire the result instead of returning it.
$expire = $option['expire'];
$data = $option['data'];
if ( false !== $expire && $expire < time() ) {
self::delete( $key );
return $default;
}
return $data;
}
/**
* Delete all `Transient` values with certain prefix from database.
*
* @param string $prefix Cache key prefix.
*/
public static function delete_by_prefix( $prefix ) {
global $wpdb;
/**
* The prefix used in option_name.
*/
$option_prefix = static::key( $prefix );
/**
* LIKE search pattern for the delete query.
*/
$prefix_search_pattern = $wpdb->esc_like( $option_prefix ) . '%';
//phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$option_names = $wpdb->get_col(
$wpdb->prepare(
"
SELECT option_name
FROM $wpdb->options
WHERE `option_name` LIKE %s
",
$prefix_search_pattern
)
);
// phpcs:enable
// Go through each option individually to ensure caches are handled properly.
foreach ( $option_names as $option_name ) {
delete_option( $option_name );
}
}
/**
* Delete a cache entry.
*
* @param string $key Cache key name.
*
* @return void
*/
public static function delete( $key ) {
delete_option( self::key( $key ) );
}
}

View File

@ -0,0 +1,96 @@
<?php
/**
* Implement the URL normalizer.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Lib;
/**
* Class Url
*/
class Url {
const PARAMS_TO_EXCLUDE = array( 'utm_campaign', 'utm_medium', 'utm_source', 'utm_content', 'fbclid', '_ga', 'jb-disable-modules' );
const PARAMS_TO_NORMALIZE = array( 's' => '' );
/**
* Normalize a URL - right now, just make sure it's absolute.
*
* @param string $url The URL.
*
* @return string
*/
public static function normalize( $url ) {
$url = self::normalize_query_args( $url );
if ( '/' === $url[0] ) {
$url = site_url( $url );
}
return rtrim( $url, '/' );
}
/**
* Returns the current URL.
*
* @return string
*/
public static function get_current_url() {
// Fallback to the site URL if we're unable to determine the URL from $_SERVER global.
$current_url = site_url();
if ( isset( $_SERVER ) && is_array( $_SERVER ) ) {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens at the end
$scheme = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
$host = ! empty( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : null;
$path = ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
// Support for local plugin development and testing using ngrok.
if ( ! empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) && str_contains( $_SERVER['HTTP_X_ORIGINAL_HOST'], 'ngrok.io' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
$host = wp_unslash( $_SERVER['HTTP_X_ORIGINAL_HOST'] );
}
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( $host ) {
$current_url = esc_url_raw( sprintf( '%s://%s%s', $scheme, $host, $path ) );
}
}
return apply_filters( 'jetpack_boost_current_url', $current_url );
}
/**
* Remove and reorder query parameters.
*
* @param string $url URL.
*
* @return string
*/
protected static function normalize_query_args( $url ) {
$exclude_parameters = apply_filters(
'jetpack_boost_excluded_query_parameters',
self::PARAMS_TO_EXCLUDE
);
// Filter out certain parameters that we know don't constitute a different page.
$url = remove_query_arg( $exclude_parameters, $url );
// Extract the query string, sort it and replace it in the current URL.
$parsed_url = wp_parse_url( $url );
if ( ! empty( $parsed_url['query'] ) ) {
parse_str( $parsed_url['query'], $args );
foreach ( self::PARAMS_TO_NORMALIZE as $key => $value ) {
if ( isset( $args[ $key ] ) ) {
$args[ $key ] = $value;
}
}
ksort( $args );
$sorted_query = http_build_query( $args );
$url = str_replace( '?' . $parsed_url['query'], '?' . $sorted_query, $url );
}
return $url;
}
}

View File

@ -0,0 +1,145 @@
<?php
/**
* Utility class.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Lib;
use Automattic\Jetpack\Connection\Client;
/**
* Class Utils
*/
class Utils {
/**
* Standardize error format.
*
* @param mixed $error Error.
*
* @return array
*/
public static function standardize_error( $error ) {
if ( is_wp_error( $error ) ) {
return array(
'name' => $error->get_error_code(),
'message' => $error->get_error_message(),
);
}
if ( is_string( $error ) ) {
return array(
'name' => 'Error',
'message' => $error,
);
}
if ( is_object( $error ) ) {
return array(
'name' => 'Error',
'message' => json_decode( wp_json_encode( $error ), ARRAY_A ),
);
}
return $error;
}
/**
* Convert relative url to absolute.
*
* @param string $url The URL.
*
* @return string
*/
public static function force_url_to_absolute( $url ) {
if ( str_starts_with( $url, '/' ) ) {
return get_site_url( null, $url );
}
return $url;
}
/**
* Given a post type, look up its label (if available). Returns
* raw post type string if not found.
*
* @param string $post_type Post type to look up.
*
* @return string
*/
public static function get_post_type_label( $post_type ) {
$post_type_object = get_post_type_object( $post_type );
if ( ! $post_type_object ) {
return $post_type;
}
return $post_type_object->labels->name;
}
/**
* Given a taxonomy name, look up its label. Returns raw taxonomy name if
* not found.
*
* @param string $taxonomy_name Taxonomy to look up.
*
* @return string
*/
public static function get_taxonomy_label( $taxonomy_name ) {
$taxonomy = get_taxonomy( $taxonomy_name );
if ( ! $taxonomy ) {
return $taxonomy_name;
}
return $taxonomy->label;
}
/**
* Make a Jetpack-authenticated request to the WPCOM servers
*
* @param string $method Request method.
* @param string $endpoint Endpoint to contact.
* @param array $args Request args.
* @param array $body Request body.
*
* @return \WP_Error|array
*/
public static function send_wpcom_request( $method, $endpoint, $args = null, $body = null ) {
$default_args = array(
'method' => $method,
'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
);
$response = Client::wpcom_json_api_request_as_blog(
$endpoint,
'2',
array_merge( $default_args, empty( $args ) ? array() : $args ),
empty( $body ) ? null : wp_json_encode( $body ),
'wpcom'
);
if ( is_wp_error( $response ) ) {
return $response;
}
// Check for HTTP errors.
$code = wp_remote_retrieve_response_code( $response );
$data = json_decode( wp_remote_retrieve_body( $response ), ARRAY_A );
if ( 200 !== $code ) {
$default_message = sprintf(
/* translators: %d is a numeric HTTP error code */
__( 'HTTP %d while communicating with WordPress.com', 'jetpack-boost-core' ),
$code
);
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$err_code = empty( $data['statusCode'] ) ? 'http_error' : $data['statusCode'];
$message = empty( $data['error'] ) ? $default_message : $data['error'];
return new \WP_Error( $err_code, $message );
}
return $data;
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* WPCOM Boost API Client interface.
*
* @package automattic/jetpack-boost-core
*/
namespace Automattic\Jetpack\Boost_Core\Lib;
use Automattic\Jetpack\Boost_Core\Contracts\Boost_API_Client;
/**
* A class that handles the Boost API client.
*
* The communication to the backend is done using this class on top of the Boost_API_Client interface.
*/
class WPCOM_Boost_API_Client implements Boost_API_Client {
/**
* Submit a POST request to boost API and return response.
*
* @param string $path - Request path.
* @param mixed[] $payload - Request payload.
* @param mixed[] $args - Request arguments.
* @return mixed
*/
public function post( $path, $payload = array(), $args = null ) {
return Utils::send_wpcom_request(
'POST',
$this->get_api_path( $path ),
$args,
$payload
);
}
/**
* Make a get request to boost API and return response.
*
* @param string $path - Request path.
* @param mixed[] $query - Query parameters.
* @param mixed[] $args - Request arguments.
* @return mixed
*/
public function get( $path, $query = array(), $args = null ) {
return Utils::send_wpcom_request(
'GET',
add_query_arg( $query, $this->get_api_path( $path ) ),
$args
);
}
/**
* Get the API path for the given path.
*
* @param string $path - Request path.
* @return string
*/
private function get_api_path( $path ) {
$blog_id = (int) \Jetpack_Options::get_option( 'id' );
return sprintf( '/sites/%d/jetpack-boost/%s', $blog_id, $path );
}
}

View File

@ -0,0 +1,75 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.7] - 2024-03-18
### Changed
- Internal updates.
## [0.3.6] - 2024-03-14
### Changed
- Internal updates.
## [0.3.5] - 2024-03-01
### Changed
- Add gereric Jetpack_Boost_Modules class for when Boost is uninstalled/not activated. [#36080]
## [0.3.4] - 2024-02-13
### Fixed
- Speed Score: Do not return no-boost score if no boost modules are active [#35327]
## [0.3.3] - 2024-01-22
### Added
- Send current boost version with API requests to handle requests accordingly [#35132]
### Changed
- Jetpack Boost: Use Arrays, not objects [#35062]
## [0.3.2] - 2024-01-15
### Changed
- Internal updates.
## [0.3.1] - 2023-12-14
### Changed
- Internal updates.
## [0.3.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
## [0.2.2] - 2023-09-19
### Fixed
- Fixed deprecation notice in PHP 8.2. [#33079]
## [0.2.1] - 2023-08-28
### Added
- Add boost speed score history endpoint [#32016]
### Changed
- Updated package dependencies. [#32605]
- Updated package version [#32016]
## [0.2.0] - 2023-06-06
### Changed
- Moved boost core classes to boost-core package [#31163]
- Updated package dependencies. [#31163]
## 0.1.0 - 2023-05-29
### Added
- Add a new package for Boost Speed Score [#30914]
- Add a new argument to `Speed_Score` to identify where the request was made from (e.g. 'boost-plugin', 'jetpack-dashboard', etc). [#31012]
[0.3.7]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.6...v0.3.7
[0.3.6]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.5...v0.3.6
[0.3.5]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.4...v0.3.5
[0.3.4]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.3...v0.3.4
[0.3.3]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.2...v0.3.3
[0.3.2]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.2.2...v0.3.0
[0.2.2]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.1.0...v0.2.0

View File

@ -0,0 +1,357 @@
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,47 @@
# Security Policy
Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
## Supported Versions
Generally, only the latest version of Jetpack and its associated plugins have continued support. If a critical vulnerability is found in the current version of a plugin, we may opt to backport any patches to previous versions.
## Reporting a Vulnerability
Our HackerOne program covers the below plugin software, as well as a variety of related projects and infrastructure:
* [Jetpack](https://jetpack.com/)
* Jetpack Backup
* Jetpack Boost
* Jetpack CRM
* Jetpack Protect
* Jetpack Search
* Jetpack Social
* Jetpack VideoPress
**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
Our most critical targets are:
* Jetpack and the Jetpack composer packages (all within this repo)
* Jetpack.com -- the primary marketing site.
* cloud.jetpack.com -- a management site.
* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
## Guidelines
We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
* Pen-testing Production:
* Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
* If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
* **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
* To be eligible for a bounty, all of these guidelines must be followed.
* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.

View File

@ -0,0 +1,61 @@
{
"name": "automattic/jetpack-boost-speed-score",
"description": "A package that handles the API to generate the speed score.",
"type": "jetpack-library",
"license": "GPL-2.0-or-later",
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.2",
"brain/monkey": "^2.6"
},
"autoload-dev": {
"psr-4": {
"Automattic\\Jetpack\\Boost_Speed_Score\\Tests\\": "./tests/php"
}
},
"require": {
"php": ">=7.0",
"automattic/jetpack-boost-core": "^0.2.5"
},
"autoload": {
"classmap": [
"src/"
]
},
"scripts": {
"phpunit": [
"./vendor/phpunit/phpunit/phpunit --colors=always"
],
"test-php": [
"@composer phpunit"
],
"build-production": "echo 'Add your build step to composer.json, please!'",
"build-development": "echo 'Add your build step to composer.json, please!'",
"post-install-cmd": "WorDBless\\Composer\\InstallDropin::copy",
"post-update-cmd": "WorDBless\\Composer\\InstallDropin::copy"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"allow-plugins": {
"roots/wordpress-core-installer": true
}
},
"extra": {
"mirror-repo": "Automattic/jetpack-boost-speed-score",
"changelogger": {
"link-template": "https://github.com/Automattic/jetpack-boost-speed-score/compare/v${old}...v${new}"
},
"autotagger": true,
"branch-alias": {
"dev-trunk": "0.3.x-dev"
},
"textdomain": "jetpack-boost-speed-score",
"version-constants": {
"::PACKAGE_VERSION": "src/class-speed-score.php"
}
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
}
}

View File

@ -0,0 +1,56 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Jetpack Boost Active Modules
*
* Since the speed scores API will be used in the Jetpack plugin and in the My Jetpack, if Jetpack Boost
* is uninstalled, all we need is to pass along this placeholder class for the modules that essentially
* tells the API the user doesn't have any Boost modules active.
*
* @package automattic/jetpack/boost_speed_score
*/
namespace Automattic\Jetpack\Boost_Speed_Score;
/**
* Jetpack Boost Modules
*/
class Jetpack_Boost_Modules {
/**
* Holds the singleton instance of the class
*
* @var Jetpack_Boost_Modules
*/
private static $instance = false;
/**
* Singleton
*
* @static
* @return Jetpack_Boost_Modules
*/
public static function init() {
if ( ! self::$instance ) {
self::$instance = new Jetpack_Boost_Modules();
}
return self::$instance;
}
/**
* Returns status of all active boost modules
*
* @return array - An empty array. The user will never have active modules when using the Boost Score API
*/
public function get_status() {
return array();
}
/**
* Returns whether or not the user has active modules
*
* @return false - The user will never have active modules when using the Boost Score API
*/
public function have_enabled_modules() {
return false;
}
}

View File

@ -0,0 +1,138 @@
<?php
/**
* Represents a request to generate an array of speed scores history.
*
* @package automattic/jetpack-boost-speed-score
*/
namespace Automattic\Jetpack\Boost_Speed_Score;
use Automattic\Jetpack\Boost_Core\Lib\Boost_API;
use Automattic\Jetpack\Boost_Core\Lib\Cacheable;
/**
* Class Speed_Score_Graph_History_Request
*/
class Speed_Score_Graph_History_Request extends Cacheable {
/**
* Algorithm to use when defining a hash for the cache.
*/
const CACHE_KEY_HASH_ALGO = 'md5';
/**
* The timestamp start windown in ms.
*
* @var number $start timestamp start windown in ms.
*/
private $start;
/**
* The timestamp end windown in ms.
*
* @var number $end timestamp end windown in ms.
*/
private $end;
/**
* Number of retries attempted.
*
* @var int $retry_count Number of times this Speed Score request has been retried.
*/
private $retry_count;
/**
* The error returned
*
* @var array $error Speed Scores error.
*/
private $error;
/**
* Used when there's an error.
*
* @var string $status Status.
*/
private $status;
/**
* Constructor.
*
* @param number $start timestamp start windown in ms.
* @param number $end timestamp end windown in ms.
* @param array $error Speed Scores error.
*/
public function __construct( $start, $end, $error ) {
$this->start = $start;
$this->end = $end;
$this->error = $error;
$this->retry_count = 0;
}
/**
* Convert this object to a plain array for JSON serialization.
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return array(
'start' => $this->start,
'end' => $this->end,
'error' => $this->error,
'retry_count' => $this->retry_count,
);
}
/**
* This is intended to be the reverse of JsonSerializable->jsonSerialize.
*
* @param mixed $data The data to turn into an object.
*
* @return Speed_Score_Graph_History_Request
*/
public static function jsonUnserialize( $data ) {
$object = new Speed_Score_Graph_History_Request(
$data['start'],
$data['end'],
$data['error']
);
if ( ! empty( $data['retry_count'] ) ) {
$object->retry_count = intval( $data['retry_count'] );
}
return $object;
}
/**
* Return the cache prefix.
*
* @return string
*/
protected static function cache_prefix() {
return 'jetpack_boost_speed_scores_graph_history_';
}
/**
* Send a Speed History request to the API.
*
* @return true|\WP_Error True on success, WP_Error on failure.
*/
public function execute() {
$response = Boost_API::get(
'speed-scores-history',
array(
'start' => $this->start,
'end' => $this->end,
)
);
if ( is_wp_error( $response ) ) {
$this->status = 'error';
$this->error = $response->get_error_message();
$this->store();
return $response;
}
return $response;
}
}

View File

@ -0,0 +1,148 @@
<?php
/**
* Speed score history after Jetpack Boost
*
* @package automattic/jetpack-boost-speed-score
*/
namespace Automattic\Jetpack\Boost_Speed_Score;
use Automattic\Jetpack\Boost_Core\Lib\Transient;
/**
* Class Speed_Score_History
*
* @package Automattic\Jetpack\Boost_Speed_Score\Lib
*/
class Speed_Score_History {
const OPTION_PREFIX = 'jetpack_boost_speed_score_history_';
const STALE_TRANSIENT_KEY = 'speed_score_stale_marker';
/**
* Limit the number of recent records to keep.
*/
const LIMIT = 20;
/**
* The list of history entries.
*
* @var array
*/
private $entries;
/**
* URL of the site the speed score belongs to.
*
* @var string
*/
private $url;
/**
* Speed_Score_History constructor.
*
* @param string $url URL of the site the speed scores belong to.
*/
public function __construct( $url ) {
$this->url = $url;
$this->entries = get_option( $this->get_option_name(), array() );
}
/**
* Determine the option_name used to store the speed score data in options table.
*
* @return string
*/
private function get_option_name() {
return static::OPTION_PREFIX . Speed_Score_Request::generate_cache_id_from_url( $this->url );
}
/**
* Get the number of history records currently stored.
*
* @return int
*/
public function count() {
return count( $this->entries );
}
/**
* Find the latest available speed score history record.
*
* @param int $offset Instead of receiving the last one, you can use offset to receive a slightly older speed score.
*
* @return array|null
*/
public function latest( $offset = 0 ) {
$index = $this->count() - ( $offset + 1 );
if ( $index >= 0 ) {
return $this->entries[ $index ];
}
return null;
}
/**
* Find the latest available speed scores.
*
* @param int $offset Instead of receiving the last one, you can use offset to receive a slightly older speed score.
*
* @return array|null
*/
public function latest_scores( $offset = 0 ) {
$index = $this->count() - ( $offset + 1 );
if ( $index >= 0 ) {
return $this->entries[ $index ]['scores'];
}
return null;
}
/**
* Get a timestamp that marks previous history stale.
*
* All speed score before this timestamp are considered stale.
*
* @return array
*/
public static function get_stale_timestamp() {
$last_stale_marker = Transient::get( static::STALE_TRANSIENT_KEY, 0 );
// Any score that is older than 24 hours or before the last stale marker is considered stale.
return max( $last_stale_marker, time() - DAY_IN_SECONDS );
}
/**
* Mark previous speed score as stale.
*
* If a there were significant changes to the site, we want to mark prior speed scores as stale.
*/
public static function mark_stale() {
Transient::set( static::STALE_TRANSIENT_KEY, time() );
}
/**
* Check if the last item in history is stale.
*/
public function is_stale() {
$last_entry = $this->latest();
if ( ! $last_entry ) {
return true;
}
return $last_entry['timestamp'] < self::get_stale_timestamp();
}
/**
* Record a new history entry for speed scores.
*
* @param array $entry The new entry to save.
*/
public function push( $entry ) {
$this->entries[] = $entry;
$this->entries = array_slice( $this->entries, - static::LIMIT );
update_option( $this->get_option_name(), $this->entries, false );
}
}

View File

@ -0,0 +1,335 @@
<?php
/**
* Represents a request to generate a pair of speed scores.
*
* @package automattic/jetpack-boost-speed-score
*/
namespace Automattic\Jetpack\Boost_Speed_Score;
use Automattic\Jetpack\Boost_Core\Lib\Boost_API;
use Automattic\Jetpack\Boost_Core\Lib\Cacheable;
use Automattic\Jetpack\Boost_Core\Lib\Url;
/**
* Class Speed_Score_Request
*/
class Speed_Score_Request extends Cacheable {
/**
* Algorithm to use when defining a hash for the cache.
*/
const CACHE_KEY_HASH_ALGO = 'md5';
/**
* The URL to get the Speed Scores for.
*
* @var string $url Url to get the Speed Scores for.
*/
private $url;
/**
* Active Jetpack Boost modules.
*
* @var array $active_modules Active modules.
*/
private $active_modules;
/**
* When the Speed Scores request was created, in seconds since epoch.
*
* @var float $created Speed Scores request creation timestamp.
*/
private $created;
/**
* Current status of the Speed Score request.
*
* @var string $status Speed Scores request status.
*/
private $status;
/**
* Number of retries attempted.
*
* @var int $retry_count Number of times this Speed Score request has been retried.
*/
private $retry_count;
/**
* The error returned
*
* @var array $error Speed Scores error.
*/
private $error;
/**
* Where the Speed Scores request was made from.
*
* @var string $client A string passed to Speed_Score to identify where the request was made from.
*/
private $client;
/**
* Constructor.
*
* @param string $url The URL to get the Speed Scores for.
* @param array $active_modules Active modules.
* @param null $created When the Speed Scores request was created, in seconds since epoch.
* @param string $status Status of the Speed Scores request.
* @param null $error The Speed Scores error.
* @param string $client A string identifying where the request was made from.
*/
public function __construct( $url, $active_modules = array(), $created = null, $status = 'pending', $error = null, $client = null ) {
$this->set_cache_id( self::generate_cache_id_from_url( $url ) );
$this->url = $url;
$this->active_modules = $active_modules;
$this->created = $created === null ? microtime( true ) : $created;
$this->status = $status;
$this->error = $error;
$this->client = $client;
$this->retry_count = 0;
}
/**
* Generate the cache ID from the URL.
*
* @param string $url The URL to get the Speed Scores for.
*
* @return string
*/
public static function generate_cache_id_from_url( $url ) {
return hash( self::CACHE_KEY_HASH_ALGO, $url );
}
/**
* Get the list of active performance modules while this request was created.
*
* @return string
*/
public function get_active_performance_modules() {
// List of modules that affect the speed score.
$performance_modules = array(
'cloud_css',
'critical_css',
'image_cdn',
'minify_css',
'minify_js',
'render_blocking_js',
);
return array_intersect( $this->active_modules, $performance_modules );
}
/**
* Convert this object to a plain array for JSON serialization.
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return array(
'id' => $this->get_cache_id(),
'url' => $this->url,
'active_modules' => $this->get_active_performance_modules(),
'created' => $this->created,
'status' => $this->status,
'error' => $this->error,
'client' => $this->client,
'retry_count' => $this->retry_count,
);
}
/**
* This is intended to be the reverse of JsonSerializable->jsonSerialize.
*
* @param mixed $data The data to turn into an object.
*
* @return Speed_Score_Request
*/
public static function jsonUnserialize( $data ) {
$object = new Speed_Score_Request(
$data['url'],
$data['active_modules'],
$data['created'],
$data['status'],
$data['error'],
$data['client']
);
if ( ! empty( $data['id'] ) ) {
$object->set_cache_id( $data['id'] );
}
if ( ! empty( $data['retry_count'] ) ) {
$object->retry_count = intval( $data['retry_count'] );
}
return $object;
}
/**
* Return the cache prefix.
*
* @return string
*/
protected static function cache_prefix() {
return 'jetpack_boost_speed_scores_';
}
/**
* Send a Speed Scores request to the API.
*
* @return true|\WP_Error True on success, WP_Error on failure.
*/
public function execute() {
$response = Boost_API::post(
'speed-scores',
array(
'request_id' => $this->get_cache_id(),
'url' => Url::normalize( $this->url ),
'active_modules' => $this->active_modules,
'client' => $this->client,
)
);
if ( is_wp_error( $response ) ) {
$this->status = 'error';
$this->error = $response->get_error_message();
$this->store();
return $response;
}
return true;
}
/**
* Is this request pending?
*/
public function is_pending() {
return 'pending' === $this->status;
}
/**
* Did the request fail?
*/
public function is_error() {
return 'error' === $this->status;
}
/**
* Did the request succeed?
*/
public function is_success() {
return 'success' === $this->status;
}
/**
* Poll for updates to this Speed Scores request.
*
* @return true|\WP_Error True on success, WP_Error on failure.
*/
public function poll_update() {
$response = Boost_API::get(
sprintf(
'speed-scores/%s',
$this->get_cache_id()
)
);
if ( is_wp_error( $response ) ) {
// Special case: If the request is not found, restart it.
if ( 'not_found' === $response->get_error_code() ) {
return $this->restart();
}
return $response;
}
switch ( $response['status'] ) {
case 'pending':
// The initial job probably failed, dispatch again if so.
if ( $this->created <= strtotime( '-15 mins' ) ) {
return $this->restart();
}
break;
case 'error':
$this->status = 'error';
$this->error = $response['error'];
$this->store();
break;
case 'success':
$this->status = 'success';
$this->store();
$this->record_history( $response );
break;
default:
return new \WP_Error(
'invalid_response',
__(
'Invalid response from WPCOM API while polling for speed scores',
'jetpack-boost-speed-score'
),
$response
);
}
return true;
}
/**
* Restart this request; useful when WPCOM doesn't recognize the request or it times out.
*/
private function restart() {
// Enforce a maximum number of restarts.
if ( $this->retry_count > 2 ) {
$this->status = 'error';
$this->error = 'Maximum number of retries exceeded';
$this->store();
return new \WP_Error(
'error',
$this->error
);
}
$result = $this->execute();
if ( is_wp_error( $result ) ) {
return $result;
}
$this->created = time();
++$this->retry_count;
$this->store();
return true;
}
/**
* Save the speed score record to history.
*
* @param array $response Response from api.
*/
private function record_history( $response ) {
$history = new Speed_Score_History( $this->url );
$last_history = $history->latest();
$last_scores = $last_history ? $last_history['scores'] : null;
$last_theme = $last_history ? $last_history['theme'] : null;
$current_theme = wp_get_theme()->get( 'Name' );
// Only change if there is a difference from last score or the theme changed.
if ( $last_scores !== $response['scores'] || $current_theme !== $last_theme ) {
$history->push(
array(
'timestamp' => time(),
'scores' => $response['scores'],
'theme' => $current_theme,
)
);
}
}
}

View File

@ -0,0 +1,344 @@
<?php
/**
* Speed Score API endpoints.
*
* @package automattic/jetpack-boost-speed-score
*/
namespace Automattic\Jetpack\Boost_Speed_Score;
use Automattic\Jetpack\Boost_Core\Lib\Utils;
if ( ! defined( 'JETPACK_BOOST_REST_NAMESPACE' ) ) {
define( 'JETPACK_BOOST_REST_NAMESPACE', 'jetpack-boost/v1' );
}
// For use in situations where you want additional namespacing.
if ( ! defined( 'JETPACK_BOOST_REST_PREFIX' ) ) {
define( 'JETPACK_BOOST_REST_PREFIX', '' );
}
/**
* Class Speed_Score
*/
class Speed_Score {
const PACKAGE_VERSION = '0.3.7';
/**
* An instance of Automatic\Jetpack_Boost\Modules\Modules_Setup passed to the constructor
*
* @var Modules_Setup
*/
protected $modules;
/**
* A string representing the client making the request (e.g. 'boost-plugin', 'jetpack-dashboard', etc).
*
* @var string
*/
protected $client;
/**
* Constructor.
*
* @param Modules_Setup $modules - An instance of Automatic\Jetpack_Boost\Modules\Modules_Setup.
* @param string $client - A string representing the client making the request.
*/
public function __construct( $modules, $client ) {
$this->modules = $modules;
$this->client = $client;
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
add_action( 'jetpack_boost_deactivate', array( $this, 'clear_speed_score_request_cache' ) );
add_action( 'handle_environment_change', array( Speed_Score_History::class, 'mark_stale' ) );
add_action( 'jetpack_boost_deactivate', array( Speed_Score_History::class, 'mark_stale' ) );
}
/**
* Register Speed Score related REST routes.
*/
public function register_rest_routes() {
register_rest_route(
JETPACK_BOOST_REST_NAMESPACE,
JETPACK_BOOST_REST_PREFIX . '/speed-scores',
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( $this, 'fetch_speed_score' ),
'permission_callback' => array( $this, 'can_access_speed_scores' ),
)
);
register_rest_route(
JETPACK_BOOST_REST_NAMESPACE,
JETPACK_BOOST_REST_PREFIX . '/speed-scores/refresh',
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( $this, 'dispatch_speed_score_request' ),
'permission_callback' => array( $this, 'can_access_speed_scores' ),
)
);
register_rest_route(
JETPACK_BOOST_REST_NAMESPACE,
JETPACK_BOOST_REST_PREFIX . '/speed-scores-history',
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( $this, 'dispatch_speed_score_graph_history_request' ),
'permission_callback' => array( $this, 'can_access_speed_scores' ),
'args' => array(
'start' => array(
'required' => true,
'type' => 'number',
),
'end' => array(
'required' => true,
'type' => 'number',
),
),
)
);
}
/**
* Verify and normalize the URL argument for a request.
*
* @param \WP_REST_Request $request The request object.
*
* @return string|\WP_Error An error to return or the target url.
*/
private function process_url_arg( $request ) {
$params = $request->get_json_params();
if ( ! isset( $params['url'] ) ) {
return new \WP_Error(
'invalid_parameter',
__(
'The url parameter is required',
'jetpack-boost-speed-score'
),
array( 'status' => 400 )
);
}
return Utils::force_url_to_absolute( $params['url'] );
}
/**
* Handler for POST /speed-scores/refresh.
*
* @param \WP_REST_Request $request The request object.
*
* @return \WP_REST_Response|\WP_Error The response.
*/
public function dispatch_speed_score_request( $request ) {
$url = $this->process_url_arg( $request );
if ( is_wp_error( $url ) ) {
return $url;
}
// Create and store the Speed Score request.
$active_modules = array_keys( array_filter( $this->modules->get_status(), 'strlen' ) );
$score_request = new Speed_Score_Request( $url, $active_modules, null, 'pending', null, $this->client );
$score_request->store( 1800 ); // Keep the request for 30 minutes even if no one access the results.
// Send the request.
$score_request->execute();
$score_request_no_boost = $this->maybe_dispatch_no_boost_score_request( $url );
return $this->prepare_speed_score_response( $url, $score_request, $score_request_no_boost );
}
/**
* Handler for POST /speed-scores-history.
*
* @param \WP_REST_Request $request The request object.
*
* @return \WP_REST_Response|\WP_Error The response.
*/
public function dispatch_speed_score_graph_history_request( $request ) {
$score_history_request = new Speed_Score_Graph_History_Request( $request->get_param( 'start' ), $request->get_param( 'end' ), array() );
// Send the request.
return $score_history_request->execute();
}
/**
* Remove the string "jb-disable-module" from array of strings.
*
* This is intended to be used by the filter `jetpack_boost_excluded_query_parameters` to allow `jb-disable-module` url parameter during score requests.
*
* @param string[] $params List of parameters to be removed from url.
*
* @return string[] Revised list of parameters to remove from url.
*/
public function allow_jb_disable_module( $params ) {
$index = array_search( 'jb-disable-modules', $params, true );
unset( $params[ $index ] );
return $params;
}
/**
* Handler for POST /speed-scores.
*
* @param \WP_REST_Request $request The request object.
*
* @return \WP_REST_Response|\WP_Error The response.
*/
public function fetch_speed_score( $request ) {
$url = $this->process_url_arg( $request );
if ( is_wp_error( $url ) ) {
return $url;
}
// Poll update if there is an ongoing request for scores with boost disabled.
$url_no_boost = $this->get_boost_modules_disabled_url( $url );
$score_request_no_boost = $this->get_score_request_by_url( $url_no_boost );
if ( $score_request_no_boost && $score_request_no_boost->is_pending() ) {
$response = $score_request_no_boost->poll_update();
if ( is_wp_error( $response ) ) {
return $response;
}
}
// Poll update if there is an ongoing request for scores with boost enabled.
$score_request = $this->get_score_request_by_url( $url );
if ( $score_request && $score_request->is_pending() ) {
$response = $score_request->poll_update();
if ( is_wp_error( $response ) ) {
return $response;
}
}
// If this is a fresh install, there might not be any speed score history. In which case, we want to fetch the initial scores.
// While updating plugin from 1.2 -> 1.3, the history will be missing along with a non-pending score request due to data structure change.
$history = new Speed_Score_History( $url );
if ( null === $history->latest_scores() && ( empty( $score_request ) || ! $score_request->is_pending() ) ) {
return $this->dispatch_speed_score_request( $request );
}
return $this->prepare_speed_score_response( $url, $score_request, $score_request_no_boost );
}
/**
* If it is time to fetch the score without boost, fetch it.
*
* @param string $url Url of the site.
*
* @return Speed_Score_Request
*/
private function maybe_dispatch_no_boost_score_request( $url ) {
// Allow `jb-disable-module` URL param to fetch score without boost modules being active.
add_filter( 'jetpack_boost_excluded_query_parameters', array( $this, 'allow_jb_disable_module' ) );
$url_no_boost = $this->get_boost_modules_disabled_url( $url );
$history = new Speed_Score_History( $url_no_boost );
$score_request = $this->get_score_request_by_url( $url_no_boost );
if (
// If there isn't already a pending request.
( empty( $score_request ) || ! $score_request->is_pending() )
&& $this->modules->have_enabled_modules()
&& $history->is_stale()
) {
$score_request = new Speed_Score_Request( $url_no_boost, array(), null, 'pending', null, $this->client ); // Dispatch a new speed score request to measure score without boost.
$score_request->store( 3600 ); // Keep the request for 1 hour even if no one access the results. The value is persisted for 1 hour in wp.com from initial request.
// Send the request.
$score_request->execute();
}
remove_filter( 'jetpack_boost_excluded_query_parameters', array( $this, 'allow_jb_disable_module' ) );
return $score_request;
}
/**
* Get Speed_Score_Request instance by url.
*
* @param string $url Url to get the Speed_Score_Request instance for.
*
* @return Speed_Score_Request
*/
private function get_score_request_by_url( $url ) {
return Speed_Score_Request::get(
Speed_Score_Request::generate_cache_id_from_url( $url )
);
}
/**
* Add query parameters to the url that would disable all boost modules.
*
* @param string $url The original URL we are measuring for score.
*
* @return string
*/
private function get_boost_modules_disabled_url( $url ) {
return add_query_arg( 'jb-disable-modules', 'all', $url );
}
/**
* Can the user access speed scores?
*
* @return bool
*/
public function can_access_speed_scores() {
return current_user_can( 'manage_options' );
}
/**
* Clear speed score request cache on jetpack_boost_deactivate action.
*/
public function clear_speed_score_request_cache() {
Speed_Score_Request::clear_cache();
}
/**
* Prepare the speed score response.
*
* @param string $url URL of the speed is requested for.
* @param Speed_Score_Request $score_request Speed score request.
* @param Speed_Score_Request $score_request_no_boost Speed score request without boost enabled.
*
* @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response
*/
private function prepare_speed_score_response( $url, $score_request, $score_request_no_boost ) {
$history = new Speed_Score_History( $url );
$url_no_boost = $this->get_boost_modules_disabled_url( $url );
$history_no_boost = new Speed_Score_History( $url_no_boost );
$response = array();
if ( ( ! $score_request || $score_request->is_success() ) && ( ! $score_request_no_boost || $score_request_no_boost->is_success() ) ) {
$response['status'] = 'success';
$response['scores'] = array(
'current' => $history->latest_scores(),
'noBoost' => null,
);
// Only include noBoost scores if at least one module is enabled.
if ( $score_request && ! empty( $score_request->get_active_performance_modules() ) ) {
$response['scores']['noBoost'] = $history_no_boost->latest_scores();
}
$response['scores']['isStale'] = $history->is_stale();
} elseif ( ( $score_request && $score_request->is_error() ) || ( $score_request_no_boost && $score_request_no_boost->is_error() ) ) {
// If either request ended up in error, we can just return the one with error so front-end can take action. The relevent url is available on the serialized object.
if ( $score_request->is_error() ) {
// Serialized version of score request contains the status property and error description if any.
$response = $score_request->jsonSerialize();
} else {
$response = $score_request_no_boost->jsonSerialize();
}
} else {
// If no request ended up in error/success as previous conditions dictate, it means that either of them are in pending state.
$response['status'] = 'pending';
}
return rest_ensure_response( $response );
}
}

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.1] - 2024-03-14
### Changed
- Internal updates.
## [2.0.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
@ -192,6 +196,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Trying to add deterministic initialization.
[2.0.1]: https://github.com/Automattic/jetpack-config/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-config/compare/v1.15.4...v2.0.0
[1.15.4]: https://github.com/Automattic/jetpack-config/compare/v1.15.3...v1.15.4
[1.15.3]: https://github.com/Automattic/jetpack-config/compare/v1.15.2...v1.15.3

View File

@ -7,7 +7,7 @@
"php": ">=7.0"
},
"require-dev": {
"automattic/jetpack-changelogger": "^4.0.0"
"automattic/jetpack-changelogger": "^4.1.1"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -5,6 +5,48 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.5.0] - 2024-03-18
### Added
- Add the 'remote_provision' REST endpoint. [#36275]
- Add the 'remote_register' REST endpoint. [#36197]
## [2.4.1] - 2024-03-12
### Changed
- Internal updates.
## [2.4.0] - 2024-03-12
### Added
- Sync:Now Sync uses rest api endpoint for enabled sites [#36210]
### Changed
- Updated package dependencies. [#36325]
## [2.3.4] - 2024-03-04
### Changed
- Updated package dependencies. [#36095]
## [2.3.3] - 2024-03-01
### Fixed
- Webhook class: avoid PHP warning with PHP 8.2 [#35996]
## [2.3.2] - 2024-02-26
### Removed
- Remove legacy options that are not needed anymore. [#35873]
## [2.3.1] - 2024-02-13
### Changed
- Updated package dependencies. [#35608]
## [2.3.0] - 2024-02-05
### Added
- Add rate limiter to the package versions endpoint calls. [#35379]
### Changed
- Adjust 'get_site_id()' method to return null if there's no blog ID. [#35004]
- Adjust 'get_site_id()' method to return null if there's no blog ID. [#35006]
- Jetpack Connection: Add jetpack_package_versions to Sync [#35409]
- Updated package dependencies. [#35384]
## [2.2.0] - 2024-01-18
### Added
- Adding support for IDC when site URL is an IP address. [#34753]
@ -950,6 +992,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Separate the connection library into its own package.
[2.5.0]: https://github.com/Automattic/jetpack-connection/compare/v2.4.1...v2.5.0
[2.4.1]: https://github.com/Automattic/jetpack-connection/compare/v2.4.0...v2.4.1
[2.4.0]: https://github.com/Automattic/jetpack-connection/compare/v2.3.4...v2.4.0
[2.3.4]: https://github.com/Automattic/jetpack-connection/compare/v2.3.3...v2.3.4
[2.3.3]: https://github.com/Automattic/jetpack-connection/compare/v2.3.2...v2.3.3
[2.3.2]: https://github.com/Automattic/jetpack-connection/compare/v2.3.1...v2.3.2
[2.3.1]: https://github.com/Automattic/jetpack-connection/compare/v2.3.0...v2.3.1
[2.3.0]: https://github.com/Automattic/jetpack-connection/compare/v2.2.0...v2.3.0
[2.2.0]: https://github.com/Automattic/jetpack-connection/compare/v2.1.1...v2.2.0
[2.1.1]: https://github.com/Automattic/jetpack-connection/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/Automattic/jetpack-connection/compare/v2.0.3...v2.1.0

View File

@ -5,18 +5,18 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-a8c-mc-stats": "^2.0.0",
"automattic/jetpack-admin-ui": "^0.3.1",
"automattic/jetpack-constants": "^2.0.0",
"automattic/jetpack-roles": "^2.0.0",
"automattic/jetpack-status": "^2.0.2",
"automattic/jetpack-redirect": "^2.0.0"
"automattic/jetpack-a8c-mc-stats": "^2.0.1",
"automattic/jetpack-admin-ui": "^0.4.1",
"automattic/jetpack-constants": "^2.0.1",
"automattic/jetpack-roles": "^2.0.1",
"automattic/jetpack-status": "^2.1.2",
"automattic/jetpack-redirect": "^2.0.1"
},
"require-dev": {
"automattic/wordbless": "@dev",
"yoast/phpunit-polyfills": "1.1.0",
"brain/monkey": "2.6.1",
"automattic/jetpack-changelogger": "^4.0.5"
"automattic/jetpack-changelogger": "^4.1.2"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -57,7 +57,7 @@
"link-template": "https://github.com/Automattic/jetpack-connection/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "2.2.x-dev"
"dev-trunk": "2.5.x-dev"
}
},
"config": {

View File

@ -65,7 +65,6 @@ class Jetpack_Options {
'sync_health_status', // (bool|array) An array of data relating to Jetpack's sync health.
'safe_mode_confirmed', // (bool) True if someone confirms that this site was correctly put into safe mode automatically after an identity crisis is discovered.
'migrate_for_idc', // (bool) True if someone confirms that this site should migrate stats and subscribers from its previous URL
'dismissed_connection_banner', // (bool) True if the connection banner has been dismissed
'ab_connect_banner_green_bar', // (int) Version displayed of the A/B test for the green bar at the top of the connect banner.
'onboarding', // (string) Auth token to be used in the onboarding connection flow
'tos_agreed', // (bool) Whether or not the TOS for connection has been agreed upon.
@ -117,8 +116,6 @@ class Jetpack_Options {
'setup_wizard_questionnaire', // (array) (DEPRECATED) List of user choices from the setup wizard.
'setup_wizard_status', // (string) (DEPRECATED) Status of the setup wizard.
'licensing_error', // (string) Last error message occurred while attaching licenses that is yet to be surfaced to the user.
'recommendations_banner_dismissed', // (bool) Determines if the recommendations dashboard banner is dismissed or not.
'recommendations_banner_enabled', // (bool) Whether the recommendations are enabled or not.
'recommendations_data', // (array) The user choice and other data for the recommendations.
'recommendations_step', // (string) The current step of the recommendations.
'recommendations_conditional', // (array) An array of action-based recommendations.
@ -632,7 +629,6 @@ class Jetpack_Options {
'jetpack_protect_key',
'jetpack_protect_blocked_attempts',
'jetpack_protect_activating',
'jetpack_connection_banner_ab',
'jetpack_active_plan',
'jetpack_activation_source',
'jetpack_site_products',

View File

@ -269,7 +269,7 @@ class Jetpack_XMLRPC_Server {
* This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
* register this site so that a plan can be provisioned.
*
* @param array $request An array containing at minimum nonce and local_user keys.
* @param array|ArrayAccess $request An array containing at minimum nonce and local_user keys.
*
* @return \WP_Error|array
*/
@ -373,7 +373,7 @@ class Jetpack_XMLRPC_Server {
* This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
* register this site so that a plan can be provisioned.
*
* @param array $request An array containing at minimum a nonce key and a local_username key.
* @param array|ArrayAccess $request An array containing at minimum a nonce key and a local_username key.
*
* @return \WP_Error|array
*/

View File

@ -88,6 +88,7 @@ class Client {
'blog_id' => 0,
'auth_location' => Constants::get_constant( 'JETPACK_CLIENT__AUTH_LOCATION' ),
'method' => 'POST',
'format' => 'json',
'timeout' => 10,
'redirection' => 0,
'headers' => array(),
@ -151,11 +152,14 @@ class Client {
// Allow arrays to be used in passing data.
$body_to_hash = $body;
if ( is_array( $body ) ) {
if ( $args['format'] === 'jsonl' ) {
parse_str( $body, $body_to_hash );
}
if ( is_array( $body_to_hash ) ) {
// We cast this to a new variable, because the array form of $body needs to be
// maintained so it can be passed into the request later on in the code.
if ( array() !== $body ) {
$body_to_hash = wp_json_encode( self::_stringify_data( $body ) );
if ( array() !== $body_to_hash ) {
$body_to_hash = wp_json_encode( self::_stringify_data( $body_to_hash ) );
} else {
$body_to_hash = '';
}
@ -164,7 +168,6 @@ class Client {
if ( ! is_string( $body_to_hash ) ) {
return new \WP_Error( 'invalid_body', 'Body is malformed.' );
}
$body_hash = base64_encode( sha1( $body_to_hash, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}
@ -370,6 +373,7 @@ class Client {
array(
'headers' => 'array',
'method' => 'string',
'format' => 'string',
'timeout' => 'int',
'redirection' => 'int',
'stream' => 'boolean',

View File

@ -239,6 +239,8 @@ class Manager {
* from /xmlrpc.php so that we're replicating it as closely as possible.
*
* @todo Tighten $wp_xmlrpc_server_class a bit to make sure it doesn't do bad things.
*
* @return never
*/
public function alternate_xmlrpc() {
// Some browser-embedded clients send cookies. We don't want them.

View File

@ -30,11 +30,31 @@ class Package_Version_Tracker {
*/
const CACHED_FAILED_REQUEST_EXPIRATION = 1 * HOUR_IN_SECONDS;
/**
* Transient key for rate limiting the package version requests;
*/
const RATE_LIMITER_KEY = 'jetpack_update_remote_package_last_query';
/**
* Only allow one versions check (and request) per minute.
*/
const RATE_LIMITER_TIMEOUT = MINUTE_IN_SECONDS;
/**
* Uses the jetpack_package_versions filter to obtain the package versions from packages that need
* version tracking. If the package versions have changed, updates the option and notifies WPCOM.
*/
public function maybe_update_package_versions() {
// Do not run too early or all the modules may not be loaded.
if ( ! did_action( 'init' ) ) {
return;
}
// The version check is being rate limited.
if ( $this->is_rate_limiting() ) {
return;
}
/**
* Obtains the package versions.
*
@ -65,13 +85,43 @@ class Package_Version_Tracker {
}
/**
* Updates the package versions:
* Updates the package versions option.
*
* @param array $package_versions The package versions.
*/
protected function update_package_versions_option( $package_versions ) {
if ( ! $this->is_sync_enabled() ) {
$this->update_package_versions_via_remote_request( $package_versions );
return;
}
update_option( self::PACKAGE_VERSION_OPTION, $package_versions );
}
/**
* Whether Jetpack Sync is enabled.
*
* @return boolean true if Sync is present and enabled, false otherwise
*/
protected function is_sync_enabled() {
if ( class_exists( 'Automattic\Jetpack\Sync\Settings' ) && \Automattic\Jetpack\Sync\Settings::is_sync_enabled() ) {
return true;
}
return false;
}
/**
* Fallback for updating the package versions via a remote request when Sync is not present.
*
* Updates the package versions as follows:
* - Sends the updated package versions to wpcom.
* - Updates the 'jetpack_package_versions' option.
*
* @param array $package_versions The package versions.
*/
protected function update_package_versions_option( $package_versions ) {
protected function update_package_versions_via_remote_request( $package_versions ) {
$connection = new Manager();
if ( ! $connection->is_connected() ) {
return;
@ -108,4 +158,19 @@ class Package_Version_Tracker {
set_transient( self::CACHED_FAILED_REQUEST_KEY, time(), self::CACHED_FAILED_REQUEST_EXPIRATION );
}
}
/**
* Check if version check is being rate limited, and update the rate limiting transient if needed.
*
* @return bool
*/
private function is_rate_limiting() {
if ( get_transient( static::RATE_LIMITER_KEY ) ) {
return true;
}
set_transient( static::RATE_LIMITER_KEY, time(), static::RATE_LIMITER_TIMEOUT );
return false;
}
}

View File

@ -12,7 +12,7 @@ namespace Automattic\Jetpack\Connection;
*/
class Package_Version {
const PACKAGE_VERSION = '2.2.0';
const PACKAGE_VERSION = '2.5.0';
const PACKAGE_SLUG = 'connection';

View File

@ -84,6 +84,27 @@ class REST_Connector {
)
);
// Authorize a remote user.
register_rest_route(
'jetpack/v4',
'/remote_provision',
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'remote_provision' ),
'permission_callback' => array( $this, 'remote_provision_permission_check' ),
)
);
register_rest_route(
'jetpack/v4',
'/remote_register',
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'remote_register' ),
'permission_callback' => array( $this, 'remote_register_permission_check' ),
)
);
// Get current connection status of Jetpack.
register_rest_route(
'jetpack/v4',
@ -287,6 +308,72 @@ class REST_Connector {
return $result;
}
/**
* Initiate the site provisioning process.
*
* @since 2.5.0
*
* @param WP_REST_Request $request The request sent to the WP REST API.
*
* @return WP_Error|array
*/
public static function remote_provision( WP_REST_Request $request ) {
$xmlrpc_server = new Jetpack_XMLRPC_Server();
$result = $xmlrpc_server->remote_provision( $request );
if ( is_a( $result, 'IXR_Error' ) ) {
$result = new WP_Error( $result->code, $result->message );
}
return $result;
}
/**
* Register the site so that a plan can be provisioned.
*
* @since 2.5.0
*
* @param WP_REST_Request $request The request object.
*
* @return WP_Error|array
*/
public function remote_register( WP_REST_Request $request ) {
$xmlrpc_server = new Jetpack_XMLRPC_Server();
$result = $xmlrpc_server->remote_register( $request );
if ( is_a( $result, 'IXR_Error' ) ) {
$result = new WP_Error( $result->code, $result->message );
}
return $result;
}
/**
* Remote provision endpoint permission check.
*
* @return true|WP_Error
*/
public function remote_provision_permission_check() {
return Rest_Authentication::is_signed_with_blog_token()
? true
: new WP_Error( 'invalid_permission_remote_provision', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
/**
* Remote register endpoint permission check.
*
* @return true|WP_Error
*/
public function remote_register_permission_check() {
if ( $this->connection->has_connected_owner() ) {
return Rest_Authentication::is_signed_with_blog_token()
? true
: new WP_Error( 'already_registered', __( 'Blog is already registered', 'jetpack-connection' ), 400 );
}
return true;
}
/**
* Get connection status for this Jetpack site.
*

View File

@ -86,11 +86,11 @@ class Webhooks {
case 'authorize':
$this->handle_authorize();
$this->do_exit();
break;
break; // @phan-suppress-current-line PhanPluginUnreachableCode -- Safer to include it even though do_exit never returns.
case 'authorize_redirect':
$this->handle_authorize_redirect();
$this->do_exit();
break;
break; // @phan-suppress-current-line PhanPluginUnreachableCode -- Safer to include it even though do_exit never returns.
// Class Jetpack::admin_page_load() still handles other cases.
}
}
@ -159,6 +159,8 @@ class Webhooks {
/**
* The `exit` is wrapped into a method so we could mock it.
*
* @return never
*/
protected function do_exit() {
exit;

View File

@ -18,6 +18,12 @@ use Jetpack_Network;
* Authorize_Redirect Webhook handler class.
*/
class Authorize_Redirect {
/**
* The Connection Manager object.
*
* @var Manager
*/
private $connection;
/**
* Constructs the object
@ -32,6 +38,8 @@ class Authorize_Redirect {
* Handle the webhook
*
* This method implements what's in Jetpack::admin_page_load when the Jetpack plugin is not present
*
* @return never
*/
public function handle() {

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.1] - 2024-03-12
### Changed
- Internal updates.
## [2.0.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
@ -158,6 +162,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Packages: Finish the constants package
[2.0.1]: https://github.com/Automattic/jetpack-constants/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-constants/compare/v1.6.23...v2.0.0
[1.6.23]: https://github.com/Automattic/jetpack-constants/compare/v1.6.22...v1.6.23
[1.6.22]: https://github.com/Automattic/jetpack-constants/compare/v1.6.21...v1.6.22

View File

@ -9,7 +9,7 @@
"require-dev": {
"brain/monkey": "2.6.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.0"
"automattic/jetpack-changelogger": "^4.1.1"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.1.2] - 2024-03-18
### Changed
- Internal updates.
## [2.1.1] - 2024-03-14
### Changed
- Internal updates.
## [2.1.0] - 2024-02-22
### Changed
- Update the bot list with more bots [#35798]
## [2.0.1] - 2023-11-21
### Changed
- Added a note of non-usage of PHP8+ functions yet. [#34137]
@ -177,6 +189,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Moving jetpack_is_mobile into a package
[2.1.2]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.1...v2.1.2
[2.1.1]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/Automattic/jetpack-device-detection/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/Automattic/jetpack-device-detection/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.5.1...v2.0.0
[1.5.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.5.0...v1.5.1

View File

@ -8,7 +8,7 @@
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.2"
"automattic/jetpack-changelogger": "^4.1.2"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -35,7 +35,7 @@
"link-template": "https://github.com/Automattic/jetpack-device-detection/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "2.0.x-dev"
"dev-trunk": "2.1.x-dev"
}
}
}

View File

@ -5,6 +5,7 @@
* @package automattic/jetpack-device-detection
*
* We don't want to rename public members.
*
* @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
* @phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
* @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
@ -1564,6 +1565,20 @@ class User_Agent_Info {
'cloudflare-alwaysonline',
'cookieinformationscanner', // p1699315886066389-slack-C0438NHCLSY
'facebookexternalhit', // https://www.facebook.com/externalhit_uatext.php
'feedburner',
'yacybot', // http://yacy.net/bot.html
'trendictionbot', // http://www.trendiction.de/bot;
'elisabot',
'linkfluence', // http://linkfluence.com/
'semrushbot', // https://www.semrush.com/bot/
'archive.org_bot', // http://archive.org/details/archive.org_bot
'ezlynxbot', // https://www.ezoic.com/bot
'siteauditbot', // https://www.semrush.com/bot/
'snapchat', // https://developers.snap.com/robots
'applebot', // https://support.apple.com/en-ca/HT204683
'bne.es_bot', // https://www.bne.es/es/colecciones/archivo-web-espanola/aviso-webmasters
'google-safety;', // https://www.google.com/bot.html
'mojeekbot', // https://www.mojeek.com/bot.html
);
foreach ( $bot_agents as $bot_agent ) {

View File

@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.17.3] - 2024-03-14
### Changed
- Internal updates.
## [0.17.2] - 2024-03-12
### Changed
- Updated package dependencies. [#36325]
## [0.17.1] - 2024-03-04
### Changed
- Updated package dependencies.
## [0.17.0] - 2024-02-13
### Changed
- Updated package dependencies. [#35608]
### Deprecated
- Deprecating unused methods for IDC handling [#35444]
## [0.16.0] - 2024-02-07
### Deprecated
- Removing old and previously deprecated code. [#35048]
## [0.15.1] - 2024-02-05
### Changed
- Updated package dependencies. [#35384]
### Fixed
- Minor change in method response [#35336]
## [0.15.0] - 2024-01-18
### Added
- Adding support for IDC when site URL is an IP address. [#34753]
@ -473,6 +503,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated package dependencies.
- Use Connection/Urls for home_url and site_url functions migrated from Sync.
[0.17.3]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.2...v0.17.3
[0.17.2]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.1...v0.17.2
[0.17.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.0...v0.17.1
[0.17.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.16.0...v0.17.0
[0.16.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.15.1...v0.16.0
[0.15.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.15.0...v0.15.1
[0.15.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.14.1...v0.15.0
[0.14.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.14.0...v0.14.1
[0.14.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.13.0...v0.14.0

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '2770a423a89e22f7aed3');
<?php return array('dependencies' => array('react', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '607b018606bd0a465d3b');

View File

@ -5,14 +5,14 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-connection": "^2.2.0",
"automattic/jetpack-constants": "^2.0.0",
"automattic/jetpack-status": "^2.0.2",
"automattic/jetpack-logo": "^2.0.0",
"automattic/jetpack-assets": "^2.0.4"
"automattic/jetpack-connection": "^2.4.1",
"automattic/jetpack-constants": "^2.0.1",
"automattic/jetpack-status": "^2.1.2",
"automattic/jetpack-logo": "^2.0.1",
"automattic/jetpack-assets": "^2.1.4"
},
"require-dev": {
"automattic/jetpack-changelogger": "^4.0.5",
"automattic/jetpack-changelogger": "^4.1.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/wordbless": "@dev"
},
@ -57,7 +57,7 @@
"link-template": "https://github.com/Automattic/jetpack-identity-crisis/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "0.15.x-dev"
"dev-trunk": "0.17.x-dev"
}
},
"config": {

View File

@ -7,7 +7,6 @@
namespace Automattic\Jetpack;
use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Connection\Urls;
use Automattic\Jetpack\IdentityCrisis\Exception;
@ -27,7 +26,7 @@ class Identity_Crisis {
/**
* Package Version
*/
const PACKAGE_VERSION = '0.15.0';
const PACKAGE_VERSION = '0.17.3';
/**
* Persistent WPCOM blog ID that stays in the options after disconnect.
@ -142,9 +141,11 @@ class Identity_Crisis {
/**
* Gets the link to the support document used to explain Safe Mode to users.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public static function get_safe_mod_doc_url() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
return Redirect::get_url( 'jetpack-support-safe-mode' );
}
@ -239,25 +240,6 @@ class Identity_Crisis {
return add_query_arg( $query_args, $url );
}
/**
* Non-admins current screen check.
*
* @param object $current_screen Current screen.
*
* @return null
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function non_admins_current_screen_check( $current_screen ) {
_deprecated_function( __METHOD__, '0.5.0' );
self::$current_screen = $current_screen;
if ( isset( $current_screen->id ) && 'toplevel_page_jetpack' === $current_screen->id ) {
return null;
}
return null;
}
/**
* Renders the admin bar button.
*
@ -375,11 +357,13 @@ class Identity_Crisis {
/**
* Prepare URL for display.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @param string $url URL to display.
*
* @return string
*/
public static function prepare_url_for_display( $url ) {
_deprecated_function( __METHOD__, 'package-0.17.0' );
return untrailingslashit( self::normalize_url_protocol_agnostic( $url ) );
}
@ -558,28 +542,12 @@ class Identity_Crisis {
return $returned_values;
}
/**
* Returns the value of the jetpack_sync_idc_optin filter, or constant.
* If set to true, the site will be put into staging mode.
*
* @return bool
* @since 0.2.0
* @since-jetpack 4.3.2
* @deprecated 0.2.6 Use should_handle_idc()
* @see Automattic\Jetpack\Identity_Crisis::should_handle_idc
*/
public static function sync_idc_optin() {
_deprecated_function( __METHOD__, '0.2.6', 'Automattic\\Jetpack\\Identity_Crisis::should_handle_idc' );
return self::should_handle_idc();
}
/**
* Returns the value of the jetpack_should_handle_idc filter or constant.
* If set to true, the site will be put into staging mode.
*
* This method uses both the current jetpack_should_handle_idc filter and constant and the
* legacy jetpack_sync_idc_optin filter and constant to determine whether an IDC should be
* handled.
* This method uses both the current jetpack_should_handle_idc filter
* and constant to determine whether an IDC should be handled.
*
* @return bool
* @since 0.2.6
@ -587,16 +555,10 @@ class Identity_Crisis {
public static function should_handle_idc() {
if ( Constants::is_defined( 'JETPACK_SHOULD_HANDLE_IDC' ) ) {
$default = Constants::get_constant( 'JETPACK_SHOULD_HANDLE_IDC' );
} elseif ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
// Check the legacy constant. This constant should be considered deprecated as of version 0.2.6.
$default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
} else {
$default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
}
// Add a callback which uses the legacy filter 'jetpack_sync_idc_optin'.
add_filter( 'jetpack_should_handle_idc', array( __CLASS__, 'legacy_jetpack_sync_idc_optin_filter' ) );
/**
* Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
* URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
@ -609,162 +571,15 @@ class Identity_Crisis {
return (bool) apply_filters( 'jetpack_should_handle_idc', $default );
}
/**
* Returns the value for the deprecated filter, 'jetpack_sync_idc_optin'. That filter has been replaced with the
* 'jetpack_should_handle_idc' filter.
*
* @since 0.2.6
*
* @param bool $default Whether the site is opted in to IDC mitigation.
*/
public static function legacy_jetpack_sync_idc_optin_filter( $default ) {
/**
* Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
* URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
* JETPACK_SYNC_IDC_OPTIN constant if set.
*
* @param bool $default Whether the site is opted in to IDC mitigation.
*
* @since 0.2.0
* @since-jetpack 4.3.2
* @deprecated 0.2.6 Use jetpack_should_handle_idc
*/
return (bool) apply_filters_deprecated( 'jetpack_sync_idc_optin', array( $default ), '0.2.6', 'jetpack_should_handle_idc' );
}
/**
* Does the current admin page have help tabs?
*
* @return bool
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function admin_page_has_help_tabs() {
_deprecated_function( __METHOD__, '0.5.0' );
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$current_screen = get_current_screen();
$tabs = $current_screen->get_help_tabs();
return ! empty( $tabs );
}
/**
* Renders the non-admin IDC notice.
*
* @return void
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function display_non_admin_idc_notice() {
_deprecated_function( __METHOD__, '0.5.0' );
$classes = 'jp-idc-notice inline is-non-admin notice notice-warning';
if ( isset( self::$current_screen ) && 'toplevel_page_jetpack' !== self::$current_screen->id ) {
$classes .= ' is-dismissible';
}
if ( $this->admin_page_has_help_tabs() ) {
$classes .= ' has-help-tabs';
}
?>
<div class="<?php echo esc_attr( $classes ); ?>">
<?php $this->render_notice_header(); ?>
<div class="jp-idc-notice__content-header">
<h3 class="jp-idc-notice__content-header__lead">
<?php echo $this->get_non_admin_notice_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</h3>
<p class="jp-idc-notice__content-header__explanation">
<?php echo $this->get_non_admin_contact_admin_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
</div>
</div>
<?php
}
/**
* First "step" of the IDC mitigation. Will provide some messaging and two options/buttons.
* "Confirm Staging" - Dismiss the notice and continue on with our lives in staging mode.
* "Fix Jetpack Connection" - Will disconnect the site and start the mitigation...
*
* @return void
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function display_idc_notice() {
_deprecated_function( __METHOD__, '0.5.0' );
$classes = 'jp-idc-notice inline notice notice-warning';
if ( $this->admin_page_has_help_tabs() ) {
$classes .= ' has-help-tabs';
}
?>
<div class="<?php echo esc_attr( $classes ); ?>">
<?php $this->render_notice_header(); ?>
<?php $this->render_notice_first_step(); ?>
<?php $this->render_notice_second_step(); ?>
</div>
<?php
}
/**
* Enqueue CSS for the admin bar.
*
* @return void
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function enqueue_admin_bar_css() {
_deprecated_function( __METHOD__, '0.5.0' );
}
/**
* Enqueue scripts for the notice.
*
* @return void
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function enqueue_idc_notice_files() {
_deprecated_function( __METHOD__, '0.5.0' );
// Register and Enqueue jp-tracks-functions.
Tracking::register_tracks_functions_scripts( true );
}
/**
* Renders the notice header.
*
* @return void
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function render_notice_header() {
_deprecated_function( __METHOD__, '0.5.0' );
?>
<div class="jp-idc-notice__header">
<div class="jp-idc-notice__header__emblem">
<?php
$jetpack_logo = new Jetpack_Logo();
echo $jetpack_logo->get_jp_emblem(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
</div>
<p class="jp-idc-notice__header__text">
<?php esc_html_e( 'Jetpack Safe Mode', 'jetpack-idc' ); ?>
</p>
</div>
<div class="jp-idc-notice__separator"></div>
<?php
}
/**
* Is a container for the error notices.
* Will be shown/controlled by jQuery in idc-notice.js.
*
* @deprecated 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return void
*/
public function render_error_notice() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
?>
<div class="jp-idc-error__notice dops-notice is-error">
<svg class="gridicon gridicons-notice dops-notice__icon" height="24" width="24" viewBox="0 0 24 24">
@ -790,9 +605,11 @@ class Identity_Crisis {
/**
* Renders the first step notice.
*
* @deprecated 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return void
*/
public function render_notice_first_step() {
_deprecated_function( __METHOD__, ' 0.17.0' );
?>
<div class="jp-idc-notice__first-step">
<div class="jp-idc-notice__content-header">
@ -833,9 +650,12 @@ class Identity_Crisis {
/**
* Renders the second step notice.
*
* @deprecated 0.17.0 Use `@automattic/jetpack-idc` instead.
*
* @return void
*/
public function render_notice_second_step() {
_deprecated_function( __METHOD__, ' 0.17.0' );
?>
<div class="jp-idc-notice__second-step">
<div class="jp-idc-notice__content-header">
@ -877,9 +697,12 @@ class Identity_Crisis {
/**
* Returns the first step header lead.
*
* @deprecated 0.17.0 Use `@automattic/jetpack-idc` instead.
*
* @return string
*/
public function get_first_step_header_lead() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Safe mode docs URL and site URL. */
@ -905,9 +728,12 @@ class Identity_Crisis {
/**
* Returns the first step header explanation.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
*
* @return string
*/
public function get_first_step_header_explanation() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Safe mode docs URL. */
@ -931,9 +757,11 @@ class Identity_Crisis {
/**
* Returns the confirm safe mode explanation.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_confirm_safe_mode_action_explanation() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Site URL. */
@ -958,9 +786,11 @@ class Identity_Crisis {
/**
* Returns the confirm safe mode button text.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_confirm_safe_mode_button_text() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$string = esc_html__( 'Confirm Safe Mode', 'jetpack-idc' );
/**
@ -977,9 +807,11 @@ class Identity_Crisis {
/**
* Returns the first step fix connection action explanation.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_first_step_fix_connection_action_explanation() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Site URL. */
@ -1004,9 +836,11 @@ class Identity_Crisis {
/**
* Returns the first step fix connection button text.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_first_step_fix_connection_button_text() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$string = esc_html__( "Fix Jetpack's Connection", 'jetpack-idc' );
/**
@ -1023,9 +857,11 @@ class Identity_Crisis {
/**
* Returns the second step header lead.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_second_step_header_lead() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$string = sprintf(
/* translators: %s: Site URL. */
esc_html__( 'Is %1$s the new home of %2$s?', 'jetpack-idc' ),
@ -1047,9 +883,11 @@ class Identity_Crisis {
/**
* Returns the site action explanation.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_migrate_site_action_explanation() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Site URL. */
@ -1076,9 +914,11 @@ class Identity_Crisis {
/**
* Returns the migrate site button text.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_migrate_site_button_text() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$string = esc_html__( 'Migrate Stats &amp; Subscribers', 'jetpack-idc' );
/**
@ -1095,9 +935,11 @@ class Identity_Crisis {
/**
* Returns the start fresh explanation.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_start_fresh_action_explanation() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Site URL. */
@ -1124,9 +966,11 @@ class Identity_Crisis {
/**
* Returns the start fresh button text.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_start_fresh_button_text() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$string = esc_html__( 'Start Fresh &amp; Create New Connection', 'jetpack-idc' );
/**
@ -1143,9 +987,11 @@ class Identity_Crisis {
/**
* Returns the unsure prompt text.
*
* @deprecated since 0.17.0 Use `@automattic/jetpack-idc` instead.
* @return string
*/
public function get_unsure_prompt() {
_deprecated_function( __METHOD__, 'package-0.17.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Safe mode docs URL. */
@ -1166,57 +1012,6 @@ class Identity_Crisis {
return apply_filters( 'jetpack_idc_unsure_prompt', $html );
}
/**
* Returns the non-admin notice text.
*
* @return string
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function get_non_admin_notice_text() {
_deprecated_function( __METHOD__, '0.5.0' );
$html = wp_kses(
sprintf(
/* translators: %s: Safe mode docs URL. */
__( 'Jetpack has been placed into Safe Mode. Learn more about <a href="%1$s">Safe Mode</a>.', 'jetpack-idc' ),
esc_url( self::get_safe_mod_doc_url() )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default text that is displayed to non-admin on the Jetpack admin page.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_non_admin_notice_text', $html );
}
/**
* Returns the non-admin contact admin text.
*
* @return string
* @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
*/
public function get_non_admin_contact_admin_text() {
_deprecated_function( __METHOD__, '0.5.0' );
$string = esc_html__( 'An administrator of this site can take Jetpack out of Safe Mode.', 'jetpack-idc' );
/**
* Allows overriding of the default text that is displayed to non-admins prompting them to contact an admin.
*
* @param string $string The string to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_non_admin_contact_admin_text', $string );
}
/**
* Whether the site is undergoing identity crisis.
*
@ -1506,7 +1301,7 @@ class Identity_Crisis {
break;
}
}
return $response;
}
return $response;
}
}

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.2.2] - 2024-03-12
### Changed
- Internal updates.
## [0.2.1] - 2023-11-21
### Changed
- Added a note of non-usage of PHP8+ functions yet. [#34137]
@ -43,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add jetpack-ip package functionality [#28846]
- Initialized the package. [#28765]
[0.2.2]: https://github.com/automattic/jetpack-ip/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/automattic/jetpack-ip/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/automattic/jetpack-ip/compare/v0.1.6...v0.2.0
[0.1.6]: https://github.com/automattic/jetpack-ip/compare/v0.1.5...v0.1.6

View File

@ -9,7 +9,7 @@
"require-dev": {
"brain/monkey": "2.6.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.2"
"automattic/jetpack-changelogger": "^4.1.1"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -12,7 +12,7 @@ namespace Automattic\Jetpack\IP;
*/
class Utils {
const PACKAGE_VERSION = '0.2.1';
const PACKAGE_VERSION = '0.2.2';
/**
* Get the current user's IP address.

View File

@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.1.1] - 2024-03-14
### Changed
- Internal updates.
## [3.1.0] - 2024-03-12
### Changed
- Performance: only enqueue the JITM JavaScript on pages where it will be used. [#35997]
- Updated package dependencies. [#36325]
## [3.0.5] - 2024-03-04
### Changed
- Updated package dependencies. [#36095]
## [3.0.4] - 2024-02-13
### Changed
- Updated package dependencies. [#35608]
## [3.0.3] - 2024-02-05
### Changed
- Updated package dependencies. [#35384]
## [3.0.2] - 2024-01-04
### Changed
- Updated package dependencies. [#34815]
@ -662,6 +683,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update Jetpack to use new JITM package
[3.1.1]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.0...v3.1.1
[3.1.0]: https://github.com/Automattic/jetpack-jitm/compare/v3.0.5...v3.1.0
[3.0.5]: https://github.com/Automattic/jetpack-jitm/compare/v3.0.4...v3.0.5
[3.0.4]: https://github.com/Automattic/jetpack-jitm/compare/v3.0.3...v3.0.4
[3.0.3]: https://github.com/Automattic/jetpack-jitm/compare/v3.0.2...v3.0.3
[3.0.2]: https://github.com/Automattic/jetpack-jitm/compare/v3.0.1...v3.0.2
[3.0.1]: https://github.com/Automattic/jetpack-jitm/compare/v3.0.0...v3.0.1
[3.0.0]: https://github.com/Automattic/jetpack-jitm/compare/v2.5.3...v3.0.0

View File

@ -1 +1 @@
<?php return array('dependencies' => array('jquery', 'wp-polyfill'), 'version' => 'be1957276194cda30e35');
<?php return array('dependencies' => array('jquery', 'wp-polyfill'), 'version' => '42733b29f872f13c5451');

View File

@ -5,18 +5,18 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-a8c-mc-stats": "^2.0.0",
"automattic/jetpack-assets": "^2.0.4",
"automattic/jetpack-connection": "^2.1.1",
"automattic/jetpack-device-detection": "^2.0.1",
"automattic/jetpack-logo": "^2.0.0",
"automattic/jetpack-redirect": "^2.0.0",
"automattic/jetpack-status": "^2.0.2"
"automattic/jetpack-a8c-mc-stats": "^2.0.1",
"automattic/jetpack-assets": "^2.1.4",
"automattic/jetpack-connection": "^2.4.1",
"automattic/jetpack-device-detection": "^2.1.1",
"automattic/jetpack-logo": "^2.0.1",
"automattic/jetpack-redirect": "^2.0.1",
"automattic/jetpack-status": "^2.1.2"
},
"require-dev": {
"brain/monkey": "2.6.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.5"
"automattic/jetpack-changelogger": "^4.1.1"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -57,7 +57,7 @@
"link-template": "https://github.com/Automattic/jetpack-jitm/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "3.0.x-dev"
"dev-trunk": "3.1.x-dev"
}
}
}

View File

@ -20,7 +20,7 @@ use Automattic\Jetpack\Status;
*/
class JITM {
const PACKAGE_VERSION = '3.0.2';
const PACKAGE_VERSION = '3.1.1';
/**
* The configuration method that is called from the jetpack-config package.
@ -130,11 +130,42 @@ class JITM {
}
}
/**
* Check if the current page is a Jetpack or WooCommerce admin page.
* Noting that this is a very basic check, and pages from other plugins may also match.
*
* @since 3.1.0
*
* @return bool True if the current page is a Jetpack or WooCommerce admin page, else false.
*/
public function is_a8c_admin_page() {
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$current_screen = get_current_screen();
// We never want to show JITMs on the block editor.
if (
method_exists( $current_screen, 'is_block_editor' )
&& $current_screen->is_block_editor()
) {
return false;
}
return (
$current_screen
&& $current_screen->id
&& (bool) preg_match( '/jetpack|woo|shop|product/', $current_screen->id )
);
}
/**
* Function to enqueue jitm css and js
*/
public function jitm_enqueue_files() {
if ( $this->is_gutenberg_page() ) {
// Only load those files on the Jetpack or Woo admin pages.
if ( ! $this->is_a8c_admin_page() ) {
return;
}
@ -145,9 +176,9 @@ class JITM {
array(
'in_footer' => true,
'dependencies' => array( 'jquery' ),
'enqueue' => true,
)
);
Assets::enqueue_script( 'jetpack-jitm' );
wp_localize_script(
'jetpack-jitm',
'jitm_config',
@ -167,8 +198,11 @@ class JITM {
*
* @since 1.1.0
* @since-jetpack 8.0.0
*
* @deprecated 3.1.0
*/
public function is_gutenberg_page() {
_deprecated_function( __METHOD__, '3.1.0' );
$current_screen = get_current_screen();
return ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() );
}
@ -192,8 +226,8 @@ class JITM {
return;
}
// do not display on Gutenberg pages.
if ( $this->is_gutenberg_page() ) {
// Only add this to Jetpack or Woo admin pages.
if ( ! $this->is_a8c_admin_page() ) {
return;
}

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.2] - 2024-03-14
### Changed
- Internal updates.
## [2.0.1] - 2023-11-21
## [2.0.0] - 2023-11-20
@ -256,6 +260,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Licensing: Add support for Jetpack licenses
[2.0.2]: https://github.com/Automattic/jetpack-licensing/compare/v2.0.1...v2.0.2
[2.0.1]: https://github.com/Automattic/jetpack-licensing/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.8.4...v2.0.0
[1.8.4]: https://github.com/Automattic/jetpack-licensing/compare/v1.8.3...v1.8.4

View File

@ -5,12 +5,12 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-connection": "^2.0.1"
"automattic/jetpack-connection": "^2.4.1"
},
"require-dev": {
"automattic/wordbless": "@dev",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.1"
"automattic/jetpack-changelogger": "^4.1.1"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.2] - 2024-03-18
### Changed
- Internal updates.
## [2.0.1] - 2024-03-12
### Changed
- Internal updates.
## [2.0.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
@ -166,6 +174,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Packages: Add a basic Jetpack Logo package
[2.0.2]: https://github.com/Automattic/jetpack-logo/compare/v2.0.1...v2.0.2
[2.0.1]: https://github.com/Automattic/jetpack-logo/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-logo/compare/v1.6.3...v2.0.0
[1.6.3]: https://github.com/Automattic/jetpack-logo/compare/v1.6.2...v1.6.3
[1.6.2]: https://github.com/Automattic/jetpack-logo/compare/v1.6.1...v1.6.2

View File

@ -8,7 +8,7 @@
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.0"
"automattic/jetpack-changelogger": "^4.1.2"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -5,6 +5,155 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [4.17.0] - 2024-03-14
### Changed
- Rewrite My Jetpack hooks to Typescript [#36288]
## [4.16.0] - 2024-03-12
### Added
- Add a red bubble notification that shows if the site is disconnected [#36263]
- Add README to data directory [#36301]
- Add whitelist to show errors only for certain queries [#36261]
### Changed
- Migrate Stats counts out of redux and into tanstack queries [#36195]
- Replace window state calls with util function [#36271]
- Rewrite My Jetpack utils to typescript [#36296]
- Show small stats card in table if large stats card isn't showing [#36136]
- Updated package dependencies. [#36325]
- Update query hooks for my-jetpack data" [#36257]
- Update useMyJetpackConnection hook to TypeScript [#36300]
### Removed
- Removing redux store [#36256]
## [4.15.0] - 2024-03-07
### Added
- Refactor My Jetpack's backup related redux state to react query. [#35982]
### Changed
- Migrate dismiss welcome banner to tanstack [#36199]
- Migrate global notices to context [#36201]
- Migrate My Jetpack's licenses query and state to react-query [#36029]
- Moved lifecycle stats function out of redux [#36205]
- Updating purchase related redux data to tanstack/react [#35994]
### Removed
- Connected Product offer is not being used, removing it to clean up a bit [#36203]
### Fixed
- fix a few My Jetpack bugs when main plugin is not installed [#36139]
- Intersitital tables were not visibly loading when pressed [#36236]
## [4.14.0] - 2024-03-04
### Changed
- Migrate My Jetpack zendesk state to react-query [#36028]
- Updated package dependencies.
### Fixed
- Add registration nonce to connect screen in My Jetpack [#36133]
## [4.13.0] - 2024-03-01
### Added
- Add site lifecycle status guess to My Jetpack [#35815]
### Changed
- Changed product plan checks on My Jetpack cards [#36046]
- Display different Boost card tooltip content based on score letter grade. [#35863]
- Improve consistency and fix bugs in product start and checkout flows [#35908]
- Instanciate the Boost Score API (new Speed_Score()) in My Jetpack. [#36080]
- My Jetpack: add Tracks events to connection section [#35804]
- Refactor react-query to reduce code repetition [#35990]
### Fixed
- fixed the purchase query for the boost card [#36004]
## [4.12.1] - 2024-02-27
### Added
- My Jetpack: Add an info popover in the Boost product card. [#35731]
## [4.12.0] - 2024-02-26
### Changed
- My Jetpack: decouple Jetpack AI insterstitial component [#35836]
- Remove translation of product names [#35830]
- Updating purchases state to use data query instead of redux [#35697]
### Removed
- Remove kebab menu on My Jetpack cards [#35829]
## [4.11.0] - 2024-02-22
### Added
- Adding accesible text for external links on connection page and footer [#35733]
### Changed
- change status and action of My Jetpack cards when plugin is missing [#35718]
- ESlint: disabled redundant role rule [#35800]
- My Jetpack: add product slugs to click events on interstitials [#35740]
- My Jetpack: let tier data pass on quantity data to checkout process for proper checkout URL crafting [#35817]
- Updated package dependencies. [#35793]
### Fixed
- Backup Card: made stats readable by screen readers [#35786]
- Connection Screen: make VoiceOver announce lists as such [#35736]
- Do not initialize My Jetpack when in Offline mode. [#35807]
- Fix wrong prop type passed to ConnectedProductCard [#35789]
## [4.10.0] - 2024-02-19
### Added
- Add Boost Speed Score into My Jetpack Boost product card [#35606]
- Add connection indicator for screen readers [#35714]
### Fixed
- Improved accessibility of Dismiss button in Connection Banner [#35694]
- My Jeptack Connection: Make footer logos a list for better screen readers interpretation. [#35667]
- My Jetpack: add label for screen readers to connect page close button [#35712]
## [4.9.2] - 2024-02-13
### Changed
- My Jetpack: various improvements to the Stats card. [#35355]
- Updated package dependencies. [#35608]
## [4.9.1] - 2024-02-12
### Added
- Add My Jetpack link to standalone plugins missing it [#35523]
## [4.9.0] - 2024-02-07
### Changed
- Add pricing info for AI and CRM on My Jetpack [#35457]
- Update the description of some cards to better describe the product on My Jetpack page [#35428]
### Fixed
- Fixes issue on My Jetpack interstitials where some prices are 1 cent off [#35492]
## [4.8.0] - 2024-02-05
### Added
- Add tracking info to the Jetpack Manage Banner CTA [#35378]
- My Jetpack: support redirect_to parameter on the product interstitial. [#35263]
### Changed
- Update CTA copy on the connection banner to make it clear which type of connection we are going to request [#35401]
- Updated package dependencies.
- Update product cards on My Jetpack to always display the status indidicator. [#35377]
### Fixed
- Fix issue where most products are not installing their standalone product upon purchase [#35399]
## [4.7.0] - 2024-01-29
### Changed
- Update the UpsellBanner to use the Card component from WP components. [#35223]
### Removed
- UpsellBanner component moved to js-packages/components [#35228]
## [4.6.2] - 2024-01-22
### Added
- My Jetpack: add contact us event for Jetpack AI [#35136]
## [4.6.1] - 2024-01-22
### Changed
- Display Jetpack Protect product card for all users. [#35142]
- Ensure that interstitial tables go straight to checkout just like insterstitial cards [#35049]
## [4.6.0] - 2024-01-18
### Added
- Add hosting provider check. [#34864]
@ -1198,6 +1347,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Created package
[4.17.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.16.0...4.17.0
[4.16.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.15.0...4.16.0
[4.15.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.14.0...4.15.0
[4.14.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.13.0...4.14.0
[4.13.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.12.1...4.13.0
[4.12.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.12.0...4.12.1
[4.12.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.11.0...4.12.0
[4.11.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.10.0...4.11.0
[4.10.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.9.2...4.10.0
[4.9.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.9.1...4.9.2
[4.9.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.9.0...4.9.1
[4.9.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.8.0...4.9.0
[4.8.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.7.0...4.8.0
[4.7.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.6.2...4.7.0
[4.6.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.6.1...4.6.2
[4.6.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.6.0...4.6.1
[4.6.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.5.0...4.6.0
[4.5.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.4.0...4.5.0
[4.4.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.3.0...4.4.0

View File

@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => '8b33ddffd4b0e82ce03c');
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => '5782f9bf704638c25046');

View File

@ -12,6 +12,16 @@
http://jedwatson.github.io/classnames
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @remix-run/router v1.2.1
*

View File

@ -5,21 +5,23 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-admin-ui": "^0.3.1",
"automattic/jetpack-assets": "^2.0.4",
"automattic/jetpack-connection": "^2.2.0",
"automattic/jetpack-jitm": "^3.0.2",
"automattic/jetpack-licensing": "^2.0.1",
"automattic/jetpack-plugins-installer": "^0.3.1",
"automattic/jetpack-redirect": "^2.0.0",
"automattic/jetpack-constants": "^2.0.0",
"automattic/jetpack-plans": "^0.4.1"
"automattic/jetpack-admin-ui": "^0.4.1",
"automattic/jetpack-assets": "^2.1.4",
"automattic/jetpack-boost-speed-score": "^0.3.6",
"automattic/jetpack-connection": "^2.4.1",
"automattic/jetpack-jitm": "^3.1.1",
"automattic/jetpack-licensing": "^2.0.2",
"automattic/jetpack-plugins-installer": "^0.3.2",
"automattic/jetpack-redirect": "^2.0.1",
"automattic/jetpack-constants": "^2.0.1",
"automattic/jetpack-plans": "^0.4.3",
"automattic/jetpack-status": "^2.1.2"
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.0.5",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/wordbless": "@dev",
"automattic/jetpack-videopress": "^0.22.2"
"automattic/jetpack-videopress": "^0.23.10"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -67,7 +69,7 @@
"link-template": "https://github.com/Automattic/jetpack-my-jetpack/compare/${old}...${new}"
},
"branch-alias": {
"dev-trunk": "4.6.x-dev"
"dev-trunk": "4.17.x-dev"
},
"version-constants": {
"::PACKAGE_VERSION": "src/class-initializer.php"

View File

@ -0,0 +1,245 @@
interface Window {
myJetpackInitialState?: {
siteSuffix: string;
latestBoostSpeedScores: {
scores: {
desktop: number;
mobile: number;
};
theme: string;
timestamp: number;
};
IDCContainerID: string;
adminUrl: string;
blogID: string;
fileSystemWriteAccess: 'yes' | 'no';
isStatsModuleActive: string;
isUserFromKnownHost: string;
jetpackManage: {
isAgencyAccount: boolean;
isEnabled: boolean;
};
loadAddLicenseScreen: string;
myJetpackCheckoutUri: string;
myJetpackFlags: {
showFullJetpackStatsCard: boolean;
videoPressStats: boolean;
};
lifecycleStats: {
isSiteConnected: boolean;
isUserConnected: boolean;
jetpackPlugins: Array< string >;
modules: Array< string >;
purchases: Array< string >;
};
myJetpackUrl: string;
myJetpackVersion: string;
plugins: {
[ key: string ]: {
Name: string;
PluginURI: string;
Version: string;
Title: string;
Description: string;
Author: string;
AuthorName: string;
AuthorURI: string;
DomainPath: string;
textDomain: string;
RequiresPHP: string;
RequiresWP: string;
UpdateURI: string;
Network: boolean;
active: boolean;
};
};
products: {
items: {
[ key: string ]: {
class: string;
description: string;
disclaimers: Array< string[] >;
features: string[];
has_paid_plan_for_product: boolean;
features_by_tier: Array< string >;
has_required_plan: boolean;
has_required_tier: Array< string >;
is_bundle: boolean;
is_plugin_active: boolean;
is_upgradable_by_bundle: string[];
long_description: string;
manage_url: string;
name: string;
plugin_slug: string;
post_activation_url: string;
post_checkout_url?: string;
pricing_for_ui?: {
available: boolean;
wpcom_product_slug: string;
product_term: string;
currency_code: string;
full_price: number;
discount_price: number;
coupon_discount: number;
is_introductory_offer: boolean;
introductory_offer?: {
cost_per_interval: number;
interval_count: number;
interval_unit: string;
should_prorate_when_offer_ends: boolean;
transition_after_renewal_count: number;
usage_limit?: number;
};
};
purchase_url?: string;
requires_user_connection: boolean;
slug: string;
standalone_plugin_info: {
has_standalone_plugin: boolean;
is_standalone_installed: boolean;
is_standalone_active: boolean;
};
status: string;
supported_products: string[];
tiers: string[];
title: string;
wpcom_product_slug: string;
};
};
};
purchases: {
items: Array< {
ID: string;
user_id: string;
blog_id: string;
product_id: string;
subscribed_date: string;
renew: string;
auto_renew: string;
renew_date: string;
inactive_date: string | null;
active: string;
meta: string | object;
ownership_id: string;
most_recent_renew_date: string;
amount: number;
expiry_date: string;
expiry_message: string;
expiry_sub_message: string;
expiry_status: string;
partner_name: string | null;
partner_slug: string | null;
partner_key_id: string | null;
subscription_status: string;
product_name: string;
product_slug: string;
product_type: string;
blog_created_date: string;
blogname: string;
domain: string;
description: string;
attached_to_purchase_id: string | null;
included_domain: string;
included_domain_purchase_amount: number;
currency_code: string;
currency_symbol: string;
renewal_price_tier_slug: string | null;
renewal_price_tier_usage_quantity: number | null;
current_price_tier_slug: string | null;
current_price_tier_usage_quantity: number | null;
price_tier_list: Array< object >;
price_text: string;
bill_period_label: string;
bill_period_days: number;
regular_price_text: string;
regular_price_integer: number;
product_display_price: string;
price_integer: number;
is_cancelable: boolean;
can_explicit_renew: boolean;
can_disable_auto_renew: boolean;
can_reenable_auto_renewal: boolean;
iap_purchase_management_link: string | null;
is_iap_purchase: boolean;
is_locked: boolean;
is_refundable: boolean;
refund_period_in_days: number;
is_renewable: boolean;
is_renewal: boolean;
has_private_registration: boolean;
refund_amount: number;
refund_integer: number;
refund_currency_symbol: string;
refund_text: string;
refund_options: object | null;
total_refund_amount: number;
total_refund_integer: number;
total_refund_currency: string;
total_refund_text: string;
check_dns: boolean;
} >;
};
topJetpackMenuItemUrl: string;
userIsAdmin: string;
userIsNewToJetpack: string;
welcomeBanner: {
hasBeenDismissed: boolean;
};
};
JP_CONNECTION_INITIAL_STATE: {
apiRoot: string;
apiNonce: string;
registrationNonce: string;
connectionStatus: {
isActive: boolean;
isStaging: boolean;
isRegistered: boolean;
isUserConnected: boolean;
hasConnectedOwner: boolean;
offlineMode: {
isActive: boolean;
constant: boolean;
url: boolean;
filter: boolean;
wpLocalConstant: boolean;
};
isPublic: boolean;
};
userConnectionData: {
currentUser: {
isConnected: boolean;
isMaster: boolean;
username: string;
id: number;
blogId: number;
wpcomUser: {
avatar: boolean;
};
gravatar: string;
permissions: {
admin_page?: boolean;
connect: boolean;
connect_user: boolean;
disconnect: boolean;
edit_posts?: boolean;
manage_modules?: boolean;
manage_options?: boolean;
manage_plugins?: boolean;
network_admin?: boolean;
network_sites_page?: boolean;
publish_posts?: boolean;
view_stats?: boolean;
};
};
connectionOwner: null;
};
connectedPlugins: object;
wpVersion: string;
siteSuffix: string;
connectionErrors: Array< string | object >;
};
myJetpackRest?: {
apiRoot: string;
apiNonce: string;
};
}

View File

@ -9,6 +9,9 @@ namespace Automattic\Jetpack\My_Jetpack;
use Automattic\Jetpack\Admin_UI\Admin_Menu;
use Automattic\Jetpack\Assets;
use Automattic\Jetpack\Boost_Speed_Score\Jetpack_Boost_Modules;
use Automattic\Jetpack\Boost_Speed_Score\Speed_Score;
use Automattic\Jetpack\Boost_Speed_Score\Speed_Score_History;
use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
@ -34,7 +37,7 @@ class Initializer {
*
* @var string
*/
const PACKAGE_VERSION = '4.6.0';
const PACKAGE_VERSION = '4.17.0';
/**
* HTML container ID for the IDC screen on My Jetpack page.
@ -52,6 +55,17 @@ class Initializer {
'jetpack-search',
);
const MY_JETPACK_SITE_INFO_TRANSIENT_KEY = 'my-jetpack-site-info';
const MISSING_SITE_CONNECTION_NOTIFICATION_KEY = 'missing-site-connection';
/**
* Holds info/data about the site (from the /sites/%d endpoint)
*
* @var stdClass Object
*/
public static $site_info;
/**
* Initialize My Jetpack
*
@ -72,6 +86,10 @@ class Initializer {
Licensing::instance()->initialize();
}
// Initialize Boost Speed Score
$boost_modules = Jetpack_Boost_Modules::init();
new Speed_Score( $boost_modules, 'jetpack-my-jetpack' );
// Add custom WP REST API endoints.
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
@ -85,6 +103,8 @@ class Initializer {
);
add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
// This is later than the admin-ui package, which runs on 1000
add_action( 'admin_init', array( __CLASS__, 'maybe_show_red_bubble' ), 1001 );
// Sets up JITMS.
JITM::configure();
@ -142,6 +162,7 @@ class Initializer {
* @return void
*/
public static function admin_init() {
self::$site_info = self::get_site_info();
add_filter( 'identity_crisis_container_id', array( static::class, 'get_idc_container_id' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
// Product statuses are constantly changing, so we never want to cache the page.
@ -177,40 +198,53 @@ class Initializer {
'textdomain' => 'jetpack-my-jetpack',
)
);
$modules = new Modules();
$modules = new Modules();
$connection = new Connection_Manager();
$speed_score_history = new Speed_Score_History( wp_parse_url( get_site_url(), PHP_URL_HOST ) );
wp_localize_script(
'my_jetpack_main_app',
'myJetpackInitialState',
array(
'products' => array(
'products' => array(
'items' => Products::get_products(),
),
'purchases' => array(
'purchases' => array(
'items' => array(),
),
'plugins' => Plugins_Installer::get_plugins(),
'myJetpackUrl' => admin_url( 'admin.php?page=my-jetpack' ),
'myJetpackCheckoutUri' => 'admin.php?page=my-jetpack',
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
'siteSuffix' => ( new Status() )->get_site_suffix(),
'blogID' => Connection_Manager::get_site_id( true ),
'myJetpackVersion' => self::PACKAGE_VERSION,
'myJetpackFlags' => self::get_my_jetpack_flags(),
'fileSystemWriteAccess' => self::has_file_system_write_access(),
'loadAddLicenseScreen' => self::is_licensing_ui_enabled(),
'adminUrl' => esc_url( admin_url() ),
'IDCContainerID' => static::get_idc_container_id(),
'userIsAdmin' => current_user_can( 'manage_options' ),
'userIsNewToJetpack' => self::is_jetpack_user_new(),
'isStatsModuleActive' => $modules->is_active( 'stats' ),
'isUserFromKnownHost' => self::is_user_from_known_host(),
'welcomeBanner' => array(
'plugins' => Plugins_Installer::get_plugins(),
'myJetpackUrl' => admin_url( 'admin.php?page=my-jetpack' ),
'myJetpackCheckoutUri' => admin_url( 'admin.php?page=my-jetpack' ),
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
'siteSuffix' => ( new Status() )->get_site_suffix(),
'blogID' => Connection_Manager::get_site_id( true ),
'myJetpackVersion' => self::PACKAGE_VERSION,
'myJetpackFlags' => self::get_my_jetpack_flags(),
'fileSystemWriteAccess' => self::has_file_system_write_access(),
'loadAddLicenseScreen' => self::is_licensing_ui_enabled(),
'adminUrl' => esc_url( admin_url() ),
'IDCContainerID' => static::get_idc_container_id(),
'userIsAdmin' => current_user_can( 'manage_options' ),
'userIsNewToJetpack' => self::is_jetpack_user_new(),
'lifecycleStats' => array(
'jetpackPlugins' => self::get_installed_jetpack_plugins(),
'isSiteConnected' => $connection->is_connected(),
'isUserConnected' => $connection->is_user_connected(),
'purchases' => self::get_purchases(),
'modules' => self::get_active_modules(),
),
'redBubbleAlerts' => self::get_red_bubble_alerts(),
'isStatsModuleActive' => $modules->is_active( 'stats' ),
'isUserFromKnownHost' => self::is_user_from_known_host(),
'isCommercial' => self::is_commercial_site(),
'isAtomic' => ( new Status_Host() )->is_woa_site(),
'welcomeBanner' => array(
'hasBeenDismissed' => \Jetpack_Options::get_option( 'dismissed_welcome_banner', false ),
),
'jetpackManage' => array(
'jetpackManage' => array(
'isEnabled' => Jetpack_Manage::could_use_jp_manage(),
'isAgencyAccount' => Jetpack_Manage::is_agency_account(),
),
'latestBoostSpeedScores' => $speed_score_history->latest(),
)
);
@ -232,6 +266,63 @@ class Initializer {
}
}
/**
* Get product slugs of the active purchases
*
* @return array
*/
public static function get_purchases() {
$purchases = Wpcom_Products::get_site_current_purchases();
if ( is_wp_error( $purchases ) ) {
return array();
}
return array_map(
function ( $purchase ) {
return $purchase->product_slug;
},
$purchases
);
}
/**
* Get installed Jetpack plugins
*
* @return array
*/
public static function get_installed_jetpack_plugins() {
$plugin_slugs = array_keys( Plugins_Installer::get_plugins() );
$plugin_slugs = array_map(
static function ( $slug ) {
$parts = explode( '/', $slug );
if ( empty( $parts ) ) {
return '';
}
// Return the last segment of the filepath without the PHP extension
return str_replace( '.php', '', $parts[ count( $parts ) - 1 ] );
},
$plugin_slugs
);
return array_values( array_intersect( self::JETPACK_PLUGIN_SLUGS, $plugin_slugs ) );
}
/**
* Get active modules (except ones enabled by default)
*
* @return array
*/
public static function get_active_modules() {
$modules = new Modules();
$active_modules = $modules->get_active();
// if the Jetpack plugin is active, filter out the modules that are active by default
if ( class_exists( 'Jetpack' ) && ! empty( $active_modules ) ) {
$active_modules = array_diff( $active_modules, Jetpack::get_default_modules() );
}
return $active_modules;
}
/**
* Determine if the current user is "new" to Jetpack
* This is used to vary some messaging in My Jetpack
@ -305,8 +396,8 @@ class Initializer {
*/
public static function get_my_jetpack_flags() {
$flags = array(
'videoPressStats' => Jetpack_Constants::is_true( 'JETPACK_MY_JETPACK_VIDEOPRESS_STATS_ENABLED' ),
'showJetpackStatsCard' => class_exists( 'Jetpack' ),
'videoPressStats' => Jetpack_Constants::is_true( 'JETPACK_MY_JETPACK_VIDEOPRESS_STATS_ENABLED' ),
'showFullJetpackStatsCard' => class_exists( 'Jetpack' ),
);
return $flags;
@ -376,6 +467,11 @@ class Initializer {
$should = false;
}
// All options presented in My Jetpack require a connection to WordPress.com.
if ( ( new Status() )->is_offline_mode() ) {
$should = false;
}
/**
* Allows filtering whether My Jetpack should be initialized.
*
@ -406,6 +502,56 @@ class Initializer {
return rest_ensure_response( $body, 200 );
}
/**
* Populates the self::$site_info var with site data from the /sites/%d endpoint
*
* @return Object|WP_Error
*/
public static function get_site_info() {
static $site_info = null;
if ( $site_info !== null ) {
return $site_info;
}
// Check for a cached value before doing lookup
$stored_site_info = get_transient( self::MY_JETPACK_SITE_INFO_TRANSIENT_KEY );
if ( $stored_site_info !== false ) {
return $stored_site_info;
}
$response = self::get_site();
if ( is_wp_error( $response ) ) {
return $response;
}
$site_info = $response->data;
set_transient( self::MY_JETPACK_SITE_INFO_TRANSIENT_KEY, $site_info, DAY_IN_SECONDS );
return $site_info;
}
/**
* Returns whether a site has been determined "commercial" or not.
*
* @return bool
*/
public static function is_commercial_site() {
if ( is_wp_error( self::$site_info ) ) {
return null;
}
return empty( self::$site_info->options->is_commercial ) ? false : self::$site_info->options->is_commercial;
}
/**
* Check if site is registered (has been connected before).
*
* @return bool
*/
public static function is_registered() {
return (bool) \Jetpack_Options::get_option( 'id' );
}
/**
* Dismiss the welcome banner.
*
@ -465,4 +611,59 @@ class Initializer {
public static function get_idc_container_id() {
return static::IDC_CONTAINER_ID;
}
/**
* Conditionally append the red bubble notification to the "Jetpack" menu item if there are alerts to show
*
* @return void
*/
public static function maybe_show_red_bubble() {
global $menu;
// filters for the items in this file
add_filter( 'my_jetpack_red_bubble_notification_slugs', array( __CLASS__, 'alert_if_missing_site_connection' ) );
$red_bubble_alerts = self::get_red_bubble_alerts();
// The Jetpack menu item should be on index 3
if (
! empty( $red_bubble_alerts ) &&
is_countable( $red_bubble_alerts ) &&
isset( $menu[3] ) &&
$menu[3][0] === 'Jetpack'
) {
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$menu[3][0] .= sprintf( ' <span class="awaiting-mod">%d</span>', count( $red_bubble_alerts ) );
}
}
/**
* Collect all possible alerts that we might use a red bubble notification for
*
* @return array
*/
public static function get_red_bubble_alerts() {
static $red_bubble_alerts = array();
// using a static cache since we call this function more than once in the class
if ( ! empty( $red_bubble_alerts ) ) {
return $red_bubble_alerts;
}
// go find the alerts
$red_bubble_alerts = apply_filters( 'my_jetpack_red_bubble_notification_slugs', $red_bubble_alerts );
return $red_bubble_alerts;
}
/**
* Add an alert slug if the site is missing a site connection
*
* @param array $red_bubble_slugs - slugs that describe the reasons the red bubble is showing.
* @return array
*/
public static function alert_if_missing_site_connection( array $red_bubble_slugs ) {
if ( ! ( new Connection_Manager() )->is_connected() ) {
$red_bubble_slugs[] = self::MISSING_SITE_CONNECTION_NOTIFICATION_KEY;
}
return $red_bubble_slugs;
}
}

View File

@ -174,8 +174,17 @@ class Products {
'backup',
'boost',
'crm',
'videopress', // we use videopress here to add the plugin action to the Jetpack plugin itself
'videopress',
'social',
'protect',
'crm',
'search',
);
// Add plugin action links for the core Jetpack plugin.
Product::extend_core_plugin_action_links();
// Add plugin action links to standalone products.
foreach ( $products as $product ) {
$class_name = self::get_product_class( $product );
$class_name::extend_plugin_action_links();

View File

@ -237,17 +237,6 @@ class REST_Products {
);
}
/**
* If the product is not hybrid, there is no need to deal with a standalone plugin.
*/
if ( ! is_subclass_of( $product['class'], Hybrid_Product::class ) ) {
return new \WP_Error(
'not_hybrid',
__( 'This product does not have a standalone plugin to install', 'jetpack-my-jetpack' ),
array( 'status' => 400 )
);
}
$install_product_result = call_user_func( array( $product['class'], 'install_and_activate_standalone' ) );
if ( is_wp_error( $install_product_result ) ) {
$install_product_result->add_data( array( 'status' => 400 ) );

View File

@ -44,21 +44,21 @@ class Anti_Spam extends Product {
public static $requires_user_connection = false;
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'Akismet Anti-spam', 'jetpack-my-jetpack' );
return 'Akismet Anti-spam';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack Akismet Anti-spam', 'jetpack-my-jetpack' );
return 'Jetpack Akismet Anti-spam';
}
/**

View File

@ -52,21 +52,21 @@ class Backup extends Hybrid_Product {
public static $has_standalone_plugin = true;
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'VaultPress Backup', 'jetpack-my-jetpack' );
return 'VaultPress Backup';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack VaultPress Backup', 'jetpack-my-jetpack' );
return 'Jetpack VaultPress Backup';
}
/**
@ -129,6 +129,13 @@ class Backup extends Hybrid_Product {
return 'jetpack_backup_t1_yearly';
}
/**
* Get the URL where the user should be redirected after checkout
*/
public static function get_post_checkout_url() {
return self::get_manage_url();
}
/**
* Get the product princing details
*
@ -209,10 +216,12 @@ class Backup extends Hybrid_Product {
* @return ?string
*/
public static function get_manage_url() {
if ( static::is_jetpack_plugin_active() ) {
return Redirect::get_url( 'my-jetpack-manage-backup' );
} elseif ( static::is_plugin_active() ) {
// check standalone first
if ( static::is_standalone_plugin_active() ) {
return admin_url( 'admin.php?page=jetpack-backup' );
// otherwise, check for the main Jetpack plugin
} elseif ( static::is_jetpack_plugin_active() ) {
return Redirect::get_url( 'my-jetpack-manage-backup' );
}
}
@ -224,15 +233,4 @@ class Backup extends Hybrid_Product {
public static function is_active() {
return parent::is_active() && static::has_required_plan();
}
/**
* Get the URL where the user should be redirected after checkout
*/
public static function get_post_checkout_url() {
if ( static::is_jetpack_plugin_active() ) {
return 'admin.php?page=jetpack#/recommendations';
} elseif ( static::is_plugin_active() ) {
return 'admin.php?page=jetpack-backup';
}
}
}

View File

@ -51,21 +51,21 @@ class Boost extends Product {
public static $requires_user_connection = false;
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'Boost', 'jetpack-my-jetpack' );
return 'Boost';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack Boost', 'jetpack-my-jetpack' );
return 'Jetpack Boost';
}
/**
@ -74,7 +74,7 @@ class Boost extends Product {
* @return string
*/
public static function get_description() {
return __( 'The easiest speed optimization plugin for WordPress', 'jetpack-my-jetpack' );
return __( 'Speed up your site in seconds', 'jetpack-my-jetpack' );
}
/**
@ -237,6 +237,15 @@ class Boost extends Product {
);
}
/**
* Get the URL the user is taken after purchasing the product through the checkout
*
* @return ?string
*/
public static function get_post_checkout_url() {
return self::get_manage_url();
}
/**
* Get the product princing details
*
@ -260,6 +269,27 @@ class Boost extends Product {
);
}
/**
* Checks whether the current plan (or purchases) of the site already supports the product
*
* @return boolean
*/
public static function has_paid_plan_for_product() {
$purchases_data = Wpcom_Products::get_site_current_purchases();
if ( is_wp_error( $purchases_data ) ) {
return false;
}
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
foreach ( $purchases_data as $purchase ) {
// Boost is available as standalone bundle and as part of the Complete plan.
if ( strpos( $purchase->product_slug, 'jetpack_boost' ) !== false || str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
return true;
}
}
}
return false;
}
/**
* Get the URL where the user manages the product
*

View File

@ -50,21 +50,21 @@ class Creator extends Product {
public static $requires_user_connection = false;
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'Creator', 'jetpack-my-jetpack' );
return 'Creator';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack Creator', 'jetpack-my-jetpack' );
return 'Jetpack Creator';
}
/**

View File

@ -8,6 +8,7 @@
namespace Automattic\Jetpack\My_Jetpack\Products;
use Automattic\Jetpack\My_Jetpack\Product;
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
/**
* Class responsible for handling the CRM product
@ -26,7 +27,10 @@ class Crm extends Product {
*
* @var string
*/
public static $plugin_filename = 'zero-bs-crm/ZeroBSCRM.php';
public static $plugin_filename = array(
'zero-bs-crm/ZeroBSCRM.php',
'crm/ZeroBSCRM.php',
);
/**
* The slug of the plugin associated with this product. If not defined, it will default to the Jetpack plugin
@ -43,21 +47,21 @@ class Crm extends Product {
public static $requires_user_connection = false;
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'CRM', 'jetpack-my-jetpack' );
return 'CRM';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack CRM', 'jetpack-my-jetpack' );
return 'Jetpack CRM';
}
/**
@ -66,7 +70,7 @@ class Crm extends Product {
* @return string
*/
public static function get_description() {
return __( 'Connect with your people', 'jetpack-my-jetpack' );
return __( 'Nurture your contacts to grow your business', 'jetpack-my-jetpack' );
}
/**
@ -98,9 +102,17 @@ class Crm extends Product {
* @return array Pricing details
*/
public static function get_pricing_for_ui() {
// We are hard coding pricing info for CRM because it is not available to us through the CRM API.
return array(
'available' => true,
'is_free' => true,
'available' => true,
'is_free' => false,
'full_price' => 132,
'discount_price' => 132,
'is_introductory_offer' => false,
'product_term' => 'year',
'introductory_offer' => null,
// CRM is only sold in USD
'currency_code' => 'USD',
);
}
@ -121,4 +133,27 @@ class Crm extends Product {
public static function get_manage_url() {
return admin_url( 'admin.php?page=zerobscrm-dash' );
}
/**
* Checks whether the current plan (or purchases) of the site already supports the product
* CRM is available as part of Jetpack Complete
*
* @return boolean
*/
public static function has_required_plan() {
$purchases_data = Wpcom_Products::get_site_current_purchases();
if ( is_wp_error( $purchases_data ) ) {
return false;
}
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
foreach ( $purchases_data as $purchase ) {
if ( str_starts_with( $purchase->product_slug, 'jetpack_complete' ) ) {
return true;
}
}
}
return false;
}
}

View File

@ -38,21 +38,21 @@ class Extras extends Product {
public static $requires_user_connection = false;
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'Extras', 'jetpack-my-jetpack' );
return 'Extras';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack Extras', 'jetpack-my-jetpack' );
return 'Jetpack Extras';
}
/**

View File

@ -15,10 +15,6 @@ use WP_Error;
* Class responsible for handling the hybrid products
*
* Hybrid products are those that may work both as a stand-alone plugin or with the Jetpack plugin.
*
* In case Jetpack plugin is active, it will not attempt to install its stand-alone plugin.
*
* But if Jetpack plugin is not active, then it will prompt to install and activate its stand-alone plugin.
*/
abstract class Hybrid_Product extends Product {
@ -29,6 +25,15 @@ abstract class Hybrid_Product extends Product {
*/
public static $has_standalone_plugin = true;
/**
* For Hybrid products, we can use either the standalone or Jetpack plugin
*
* @return bool
*/
public static function is_plugin_installed() {
return parent::is_plugin_installed() || parent::is_jetpack_plugin_installed();
}
/**
* Checks whether the Product is active
*
@ -47,15 +52,6 @@ abstract class Hybrid_Product extends Product {
return parent::is_plugin_active();
}
/**
* Checks whether the plugin is installed
*
* @return boolean
*/
public static function is_plugin_installed() {
return parent::is_plugin_installed() || static::is_jetpack_plugin_installed();
}
/**
* Checks whether the Jetpack module is active only if a module_name is defined
*
@ -122,12 +118,11 @@ abstract class Hybrid_Product extends Product {
}
}
if ( ! empty( static::$module_name ) ) {
if ( ! static::has_required_plan() ) {
// translators: %s is the product name. e.g. Jetpack Search.
return new WP_Error( 'not_supported', sprintf( __( 'Your plan does not support %s.', 'jetpack-my-jetpack' ), static::get_title() ) );
}
// Only activate the module if the plan supports it
// We don't want to throw an error for a missing plan here since we try activation before purchase
if ( static::has_required_plan() && ! empty( static::$module_name ) ) {
$module_activation = ( new Modules() )->activate( static::$module_name, false, false );
if ( ! $module_activation ) {
return new WP_Error( 'module_activation_failed', __( 'Error activating Jetpack module', 'jetpack-my-jetpack' ) );
}
@ -143,33 +138,8 @@ abstract class Hybrid_Product extends Product {
*
* @return boolean|WP_Error
*/
final public static function install_and_activate_standalone() {
/**
* Check for the presence of the standalone plugin, ignoring Jetpack presence.
*
* If the standalone plugin is not installed and the user can install plugins, proceed with the installation.
*/
if ( ! parent::is_plugin_installed() ) {
/**
* Check for permissions
*/
if ( ! current_user_can( 'install_plugins' ) ) {
return new WP_Error( 'not_allowed', __( 'You are not allowed to install plugins on this site.', 'jetpack-my-jetpack' ) );
}
/**
* Install the plugin
*/
$installed = Plugins_Installer::install_plugin( static::get_plugin_slug() );
if ( is_wp_error( $installed ) ) {
return $installed;
}
}
/**
* Activate the installed plugin
*/
$result = static::activate_plugin();
public static function install_and_activate_standalone() {
$result = parent::install_and_activate_standalone();
if ( is_wp_error( $result ) ) {
return $result;
@ -179,7 +149,7 @@ abstract class Hybrid_Product extends Product {
* Activate the module as well, if the user has a plan
* or the product does not require a plan to work
*/
if ( static::has_required_plan() ) {
if ( static::has_required_plan() && isset( static::$module_name ) ) {
$module_activation = ( new Modules() )->activate( static::$module_name, false, false );
if ( ! $module_activation ) {

View File

@ -1,6 +1,6 @@
<?php
/**
* Boost product
* AI product
*
* @package my-jetpack
*/
@ -58,21 +58,21 @@ class Jetpack_Ai extends Product {
}
/**
* Get the internationalized product name
* Get the product name
*
* @return string
*/
public static function get_name() {
return __( 'Jetpack AI', 'jetpack-my-jetpack' );
return 'AI';
}
/**
* Get the internationalized product title
* Get the product title
*
* @return string
*/
public static function get_title() {
return __( 'Jetpack AI', 'jetpack-my-jetpack' );
return 'Jetpack AI';
}
/**
@ -103,7 +103,7 @@ class Jetpack_Ai extends Product {
* @return int
*/
public static function get_next_usage_tier() {
if ( ! self::is_site_connected() ) {
if ( ! self::is_site_connected() || ! self::has_required_plan() ) {
return 100;
}
@ -205,12 +205,6 @@ class Jetpack_Ai extends Product {
* @return array Pricing details
*/
public static function get_pricing_for_ui_by_usage_tier( $tier ) {
// Bail early if the site is not connected.
if ( ! self::is_site_connected() ) {
return array();
}
$product = Wpcom_Products::get_product( static::get_wpcom_product_slug() );
if ( empty( $product ) ) {
@ -316,24 +310,7 @@ class Jetpack_Ai extends Product {
* @return boolean
*/
public static function has_required_plan() {
$purchases_data = Wpcom_Products::get_site_current_purchases();
if ( is_wp_error( $purchases_data ) ) {
return false;
}
if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) {
foreach ( $purchases_data as $purchase ) {
if ( str_starts_with( $purchase->product_slug, static::get_wpcom_product_slug() ) ) {
return true;
}
if ( str_starts_with( $purchase->product_slug, static::get_wpcom_monthly_product_slug() ) ) {
return true;
}
if ( str_starts_with( $purchase->product_slug, static::get_wpcom_bi_yearly_product_slug() ) ) {
return true;
}
}
}
return false;
return static::does_site_have_feature( 'ai-assistant' );
}
/**

View File

@ -86,7 +86,7 @@ abstract class Module_Product extends Product {
*/
public static function get_status() {
$status = parent::get_status();
if ( 'active' === $status && ! static::is_module_active() ) {
if ( 'inactive' === $status && ! static::is_module_active() ) {
$status = 'module_disabled';
}
return $status;

Some files were not shown because too many files have changed in this diff Show More