updated plugin Jetpack Protect version 2.2.0

This commit is contained in:
2024-06-27 12:10:57 +00:00
committed by Gitium
parent ec9d8a5834
commit 938cef2946
218 changed files with 7469 additions and 1864 deletions

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.4.2] - 2024-04-22
### Changed
- Internal updates.
## [0.4.1] - 2024-03-12
### Changed
- Internal updates.
@ -144,6 +148,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fixing menu visibility issues.
[0.4.2]: https://github.com/Automattic/jetpack-admin-ui/compare/0.4.1...0.4.2
[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

View File

@ -8,8 +8,8 @@
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/jetpack-logo": "^2.0.1",
"automattic/jetpack-changelogger": "^4.2.2",
"automattic/jetpack-logo": "^2.0.2",
"automattic/wordbless": "dev-master"
},
"suggest": {

View File

@ -13,7 +13,7 @@ namespace Automattic\Jetpack\Admin_UI;
*/
class Admin_Menu {
const PACKAGE_VERSION = '0.4.1';
const PACKAGE_VERSION = '0.4.2';
/**
* Whether this class has been initialized

View File

@ -5,6 +5,37 @@ 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.11] - 2024-05-20
### Changed
- Internal updates.
## [2.1.10] - 2024-05-16
### Added
- Assets: Adding comments to explain why we use variables within translation functions [#37397]
### Changed
- Updated package dependencies. [#37379]
## [2.1.9] - 2024-05-06
### Changed
- Updated package dependencies. [#37147]
## [2.1.8] - 2024-04-22
### Changed
- Internal updates.
## [2.1.7] - 2024-04-08
### Changed
- Updated package dependencies. [#36760]
## [2.1.6] - 2024-03-27
### Changed
- Updated package dependencies. [#36585]
## [2.1.5] - 2024-03-18
### Changed
- Internal updates.
## [2.1.4] - 2024-03-12
### Changed
- Internal updates.
@ -419,6 +450,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Statically access asset tools
[2.1.11]: https://github.com/Automattic/jetpack-assets/compare/v2.1.10...v2.1.11
[2.1.10]: https://github.com/Automattic/jetpack-assets/compare/v2.1.9...v2.1.10
[2.1.9]: https://github.com/Automattic/jetpack-assets/compare/v2.1.8...v2.1.9
[2.1.8]: https://github.com/Automattic/jetpack-assets/compare/v2.1.7...v2.1.8
[2.1.7]: https://github.com/Automattic/jetpack-assets/compare/v2.1.6...v2.1.7
[2.1.6]: https://github.com/Automattic/jetpack-assets/compare/v2.1.5...v2.1.6
[2.1.5]: https://github.com/Automattic/jetpack-assets/compare/v2.1.4...v2.1.5
[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

View File

@ -5,12 +5,12 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"automattic/jetpack-constants": "^2.0.1"
"automattic/jetpack-constants": "^2.0.2"
},
"require-dev": {
"brain/monkey": "2.6.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/jetpack-changelogger": "^4.2.4",
"wikimedia/testing-access-wrapper": "^1.0 || ^2.0 || ^3.0"
},
"suggest": {

View File

@ -651,7 +651,7 @@ class Assets {
*/
public static function filter_gettext( $translation, $text, $domain ) {
if ( $translation === $text ) {
// phpcs:ignore WordPress.WP.I18n
// phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
$newtext = __( $text, self::$domain_map[ $domain ][0] );
if ( $newtext !== $text ) {
return $newtext;
@ -673,7 +673,7 @@ class Assets {
*/
public static function filter_ngettext( $translation, $single, $plural, $number, $domain ) {
if ( $translation === $single || $translation === $plural ) {
// phpcs:ignore WordPress.WP.I18n
// phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
$translation = _n( $single, $plural, $number, self::$domain_map[ $domain ][0] );
}
return $translation;
@ -691,7 +691,7 @@ class Assets {
*/
public static function filter_gettext_with_context( $translation, $text, $context, $domain ) {
if ( $translation === $text ) {
// phpcs:ignore WordPress.WP.I18n
// phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
$translation = _x( $text, $context, self::$domain_map[ $domain ][0] );
}
return $translation;
@ -711,7 +711,7 @@ class Assets {
*/
public static function filter_ngettext_with_context( $translation, $single, $plural, $number, $context, $domain ) {
if ( $translation === $single || $translation === $plural ) {
// phpcs:ignore WordPress.WP.I18n
// phpcs:ignore WordPress.WP.I18n -- This is a filter hook to map the text domains from our Composer packages to the domain for a containing plugin. See https://wp.me/p2gHKz-oRh#problem-6-text-domains-in-composer-packages
$translation = _nx( $single, $plural, $number, $context, self::$domain_map[ $domain ][0] );
}
return $translation;

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).
## [0.2.6] - 2024-04-08
### Changed
- Internal updates.
## [0.2.5] - 2024-03-25
### Fixed
- Backup: change some error messages to not trigger security scanners [#36496]
## [0.2.4] - 2024-03-18
### Changed
- Internal updates.
@ -29,6 +37,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.6]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.2.4...v0.2.5
[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

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\V0003;
namespace Automattic\Jetpack\Backup\V0004;
use Exception;
use WP_Error;

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\V0003;
namespace Automattic\Jetpack\Backup\V0004;
/**
* 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\V0003;
namespace Automattic\Jetpack\Backup\V0004;
use Exception;
use Throwable;
@ -430,7 +430,7 @@ class Throw_On_Errors {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for file_put_contents() is unset' );
throw new Exception( 'Filename for f_p_c() is unset' );
}
if ( $data === null ) {
throw new Exception( 'Data to write is null' );
@ -438,7 +438,8 @@ class Throw_On_Errors {
$data_length = strlen( $data );
$label = "file_put_contents( '$filename', $data_length bytes of data )";
// Weird label is intentional, otherwise security scanners find this label suspicious.
$label = "f_p_c( '$filename', $data_length bytes of data )";
$number_of_bytes_written = static::throw_on_warnings(
function () use ( $filename, $data ) {
@ -474,12 +475,13 @@ class Throw_On_Errors {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for file_get_contents() is unset' );
throw new Exception( 'Filename for f_g_c() is unset' );
}
$label = "file_get_contents( '$filename' )";
// Weird label is intentional, otherwise security scanners find this label suspicious.
$label = "f_g_c( '$filename' )";
$file_get_contents_result = static::throw_on_warnings(
$fgc_result = static::throw_on_warnings(
function () use ( $filename ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
return file_get_contents( $filename );
@ -487,10 +489,10 @@ class Throw_On_Errors {
$label
);
if ( false === $file_get_contents_result ) {
if ( false === $fgc_result ) {
throw new Exception( "Unable to $label" );
}
return $file_get_contents_result;
return $fgc_result;
}
}

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).
## [0.2.7] - 2024-05-06
### Added
- Add missing package dependencies. [#37141]
## [0.2.6] - 2024-03-25
### Changed
- Internal updates.
## [0.2.5] - 2024-03-14
### Changed
- Internal updates.
@ -49,6 +57,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Introduce new package. [#31163]
[0.2.7]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.6...v0.2.7
[0.2.6]: https://github.com/Automattic/jetpack-boost-core/compare/v0.2.5...v0.2.6
[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

View File

@ -4,11 +4,12 @@
"type": "jetpack-library",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0"
"php": ">=7.0",
"automattic/jetpack-connection": "^2.7.6"
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/jetpack-changelogger": "^4.2.3",
"automattic/wordbless": "dev-master"
},
"autoload": {

View File

@ -5,6 +5,28 @@ 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.11] - 2024-04-22
### Changed
- Internal updates.
## [0.3.10] - 2024-04-08
### Changed
- Bump project version. [#36072]
## [0.3.9] - 2024-03-27
### Added
- Use `jetpack_boost_critical_css_environment_changed` hook instead of `handle_environment_change` [#36519]
### Changed
- Speed Score: More accurately detect which modules are active when a speed score is requested. [#36534]
### Fixed
- Updated Jetpack_Boost_Modules placeholder class to match Boost interface [#36598]
## [0.3.8] - 2024-03-25
### Changed
- Internal updates.
## [0.3.7] - 2024-03-18
### Changed
- Internal updates.
@ -62,6 +84,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 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.11]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.10...v0.3.11
[0.3.10]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.9...v0.3.10
[0.3.9]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.8...v0.3.9
[0.3.8]: https://github.com/Automattic/jetpack-boost-speed-score/compare/v0.3.7...v0.3.8
[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

View File

@ -5,7 +5,7 @@
"license": "GPL-2.0-or-later",
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.2",
"automattic/jetpack-changelogger": "^4.2.2",
"brain/monkey": "^2.6"
},
"autoload-dev": {
@ -15,7 +15,7 @@
},
"require": {
"php": ">=7.0",
"automattic/jetpack-boost-core": "^0.2.5"
"automattic/jetpack-boost-core": "^0.2.6"
},
"autoload": {
"classmap": [

View File

@ -37,11 +37,11 @@ class Jetpack_Boost_Modules {
return self::$instance;
}
/**
* Returns status of all active boost modules
* Returns status of all active boost modules that are also ready
*
* @return array - An empty array. The user will never have active modules when using the Boost Score API
*/
public function get_status() {
public function get_ready_active_optimization_modules() {
return array();
}

View File

@ -22,14 +22,14 @@ class Speed_Score_Graph_History_Request extends Cacheable {
/**
* The timestamp start windown in ms.
*
* @var number $start timestamp start windown in ms.
* @var int|float $start timestamp start windown in ms.
*/
private $start;
/**
* The timestamp end windown in ms.
*
* @var number $end timestamp end windown in ms.
* @var int|float $end timestamp end windown in ms.
*/
private $end;
@ -57,9 +57,9 @@ class Speed_Score_Graph_History_Request extends Cacheable {
/**
* Constructor.
*
* @param number $start timestamp start windown in ms.
* @param number $end timestamp end windown in ms.
* @param array $error Speed Scores error.
* @param int|float $start timestamp start windown in ms.
* @param int|float $end timestamp end windown in ms.
* @param array $error Speed Scores error.
*/
public function __construct( $start, $end, $error ) {
$this->start = $start;

View File

@ -30,7 +30,7 @@ class Speed_Score_Request extends Cacheable {
/**
* Active Jetpack Boost modules.
*
* @var array $active_modules Active modules.
* @var string[] $active_modules Active modules.
*/
private $active_modules;
@ -105,21 +105,10 @@ class Speed_Score_Request extends Cacheable {
/**
* Get the list of active performance modules while this request was created.
*
* @return string
* @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 );
public function get_active_modules() {
return $this->active_modules;
}
/**
@ -130,7 +119,7 @@ class Speed_Score_Request extends Cacheable {
return array(
'id' => $this->get_cache_id(),
'url' => $this->url,
'active_modules' => $this->get_active_performance_modules(),
'active_modules' => $this->get_active_modules(),
'created' => $this->created,
'status' => $this->status,
'error' => $this->error,

View File

@ -23,12 +23,12 @@ if ( ! defined( 'JETPACK_BOOST_REST_PREFIX' ) ) {
*/
class Speed_Score {
const PACKAGE_VERSION = '0.3.7';
const PACKAGE_VERSION = '0.3.11';
/**
* An instance of Automatic\Jetpack_Boost\Modules\Modules_Setup passed to the constructor
* Array of module slugs that are currently active and can impact speed score.
*
* @var Modules_Setup
* @var string[]
*/
protected $modules;
@ -42,16 +42,35 @@ class Speed_Score {
/**
* 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.
* @param string[] $modules - Array of module slugs that are currently active and can impact speed score.
* @param string $client - A string representing the client making the request.
*/
public function __construct( $modules, $client ) {
/*
* Plugins using the old version of the package may pass an object instead of an array. Converting the
* object to an array keeps it backward compatible. We will lose the module slugs in case of an object,
* but it is better than a fatal error.
*/
if ( ! is_array( $modules ) ) {
$modules = array();
}
$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' ) );
/**
* Mark the speed score history as stale when the environment changes.
*
* @since 0.3.9 - This hook replaced `handle_environment_change` action.
*/
add_action( 'jetpack_boost_critical_css_environment_changed', array( Speed_Score_History::class, 'mark_stale' ) );
/**
* The `handle_environment_change` action is replaced by `jetpack_boost_critical_css_environment_changed` in Jetpack Boost.
* Keeping the `handle_environment_change` action for backward compatibility.
*/
add_action( 'handle_environment_change', array( Speed_Score_History::class, 'mark_stale' ) );
add_action( 'jetpack_boost_deactivate', array( Speed_Score_History::class, 'mark_stale' ) );
}
@ -139,8 +158,7 @@ class Speed_Score {
}
// 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 = new Speed_Score_Request( $url, $this->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.
@ -241,7 +259,7 @@ class Speed_Score {
if (
// If there isn't already a pending request.
( empty( $score_request ) || ! $score_request->is_pending() )
&& $this->modules->have_enabled_modules()
&& ! empty( $this->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.
@ -320,7 +338,7 @@ class Speed_Score {
);
// Only include noBoost scores if at least one module is enabled.
if ( $score_request && ! empty( $score_request->get_active_performance_modules() ) ) {
if ( $score_request && ! empty( $score_request->get_active_modules() ) ) {
$response['scores']['noBoost'] = $history_no_boost->latest_scores();
}

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-05-06
### Changed
- Internal updates.
## [2.0.1] - 2024-03-14
### Changed
- Internal updates.
@ -196,6 +200,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Trying to add deterministic initialization.
[2.0.2]: https://github.com/Automattic/jetpack-config/compare/v2.0.1...v2.0.2
[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

View File

@ -7,7 +7,21 @@
"php": ">=7.0"
},
"require-dev": {
"automattic/jetpack-changelogger": "^4.1.1"
"automattic/jetpack-changelogger": "^4.2.3",
"automattic/jetpack-connection": "@dev",
"automattic/jetpack-identity-crisis": "@dev",
"automattic/jetpack-import": "@dev",
"automattic/jetpack-jitm": "@dev",
"automattic/jetpack-post-list": "@dev",
"automattic/jetpack-publicize": "@dev",
"automattic/jetpack-search": "@dev",
"automattic/jetpack-stats": "@dev",
"automattic/jetpack-stats-admin": "@dev",
"automattic/jetpack-sync": "@dev",
"automattic/jetpack-videopress": "@dev",
"automattic/jetpack-waf": "@dev",
"automattic/jetpack-wordads": "@dev",
"automattic/jetpack-yoast-promo": "@dev"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -28,6 +42,29 @@
},
"branch-alias": {
"dev-trunk": "2.0.x-dev"
},
"dependencies": {
"test-only": [
"packages/connection",
"packages/identity-crisis",
"packages/import",
"packages/jitm",
"packages/post-list",
"packages/publicize",
"packages/search",
"packages/stats",
"packages/stats-admin",
"packages/sync",
"packages/videopress",
"packages/waf",
"packages/wordads",
"packages/yoast-promo"
]
}
},
"config": {
"allow-plugins": {
"automattic/jetpack-autoloader": true
}
}
}

View File

@ -234,6 +234,7 @@ class Config {
JITMS_JITM::configure();
} else {
// Provides compatibility with jetpack-jitm <v1.6.
// @phan-suppress-next-line PhanUndeclaredClassMethod
JITM::configure();
}

View File

@ -5,6 +5,94 @@ 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.8.4] - 2024-05-22
### Deprecated
- Jetpack Connection Manager: Deprecate `request_params` arg in setup_xmlrpc_handlers method. [#37445]
### Fixed
- SSO: Ensure the dist files are generated properly, without overwriting each other. [#37489]
## [2.8.3] - 2024-05-20
### Changed
- Internal updates.
## [2.8.2] - 2024-05-16
### Added
- Connection: Ensuring direct file access is disabled in class-jetpack-ixr-client.php [#37398]
### Changed
- Updated package dependencies. [#37379]
## [2.8.1] - 2024-05-14
### Changed
- SSO: do not rely on the Jetpack class anymore. [#37153]
## [2.8.0] - 2024-05-13
### Added
- SSO: Ensuring tooltips are accessible [#37302]
### Changed
- SSO: Improve user invite error logging [#37144]
## [2.7.7] - 2024-05-09
### Fixed
- SSO: Fix tooltip display on view all users page [#37257]
## [2.7.6] - 2024-05-06
### Added
- Bring in authentication methods needed for SSO feature. [#36924]
### Changed
- SSO: rely on Connection methods instead of relying on methods from the Jetpack plugin. [#36989]
- Updated package dependencies. [#37147]
## [2.7.5] - 2024-04-30
### Changed
- Internal updates.
## [2.7.4] - 2024-04-26
### Changed
- General: use wp_admin_notice function introduced in WP 6.4 to display notices. [#37051]
## [2.7.3] - 2024-04-25
### Changed
- General: Remove code that was added to remain compatible with versions of WordPress lower than 6.4. [#37049]
### Fixed
- Disconnect connection owner on removal. [#36888]
- Improve phpdoc comments in Client class, and remove some unnecessary boolean checks. [#37056]
## [2.7.2] - 2024-04-22
### Added
- SSO: Add SSO feature to the package. [#36587]
### Fixed
- Jetpack Connection: Prevent unnecessary jetpack_connection_active_plugins option updates. [#36896]
## [2.7.1] - 2024-04-08
### Changed
- Updated package dependencies. [#36760]
## [2.7.0] - 2024-03-27
### Added
- Add 'test_connection' endpoint to check for blog token validity. [#36471]
- Add the 'get_heartbeat_data' REST endpoint. [#36553]
### Changed
- Updated package dependencies. [#36585]
## [2.6.2] - 2024-03-25
### Changed
- Internal updates.
## [2.6.1] - 2024-03-22
### Changed
- yUpdate Phan config. [#36353]
## [2.6.0] - 2024-03-20
### Added
- Add the 'remote_connect' REST endpoint. [#36329]
## [2.5.0] - 2024-03-18
### Added
- Add the 'remote_provision' REST endpoint. [#36275]
@ -992,6 +1080,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Separate the connection library into its own package.
[2.8.4]: https://github.com/Automattic/jetpack-connection/compare/v2.8.3...v2.8.4
[2.8.3]: https://github.com/Automattic/jetpack-connection/compare/v2.8.2...v2.8.3
[2.8.2]: https://github.com/Automattic/jetpack-connection/compare/v2.8.1...v2.8.2
[2.8.1]: https://github.com/Automattic/jetpack-connection/compare/v2.8.0...v2.8.1
[2.8.0]: https://github.com/Automattic/jetpack-connection/compare/v2.7.7...v2.8.0
[2.7.7]: https://github.com/Automattic/jetpack-connection/compare/v2.7.6...v2.7.7
[2.7.6]: https://github.com/Automattic/jetpack-connection/compare/v2.7.5...v2.7.6
[2.7.5]: https://github.com/Automattic/jetpack-connection/compare/v2.7.4...v2.7.5
[2.7.4]: https://github.com/Automattic/jetpack-connection/compare/v2.7.3...v2.7.4
[2.7.3]: https://github.com/Automattic/jetpack-connection/compare/v2.7.2...v2.7.3
[2.7.2]: https://github.com/Automattic/jetpack-connection/compare/v2.7.1...v2.7.2
[2.7.1]: https://github.com/Automattic/jetpack-connection/compare/v2.7.0...v2.7.1
[2.7.0]: https://github.com/Automattic/jetpack-connection/compare/v2.6.2...v2.7.0
[2.6.2]: https://github.com/Automattic/jetpack-connection/compare/v2.6.1...v2.6.2
[2.6.1]: https://github.com/Automattic/jetpack-connection/compare/v2.6.0...v2.6.1
[2.6.0]: https://github.com/Automattic/jetpack-connection/compare/v2.5.0...v2.6.0
[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

View File

@ -6,17 +6,18 @@
"require": {
"php": ">=7.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"
"automattic/jetpack-admin-ui": "^0.4.2",
"automattic/jetpack-assets": "^2.1.11",
"automattic/jetpack-constants": "^2.0.2",
"automattic/jetpack-roles": "^2.0.2",
"automattic/jetpack-status": "^3.2.0",
"automattic/jetpack-redirect": "^2.0.2"
},
"require-dev": {
"automattic/wordbless": "@dev",
"yoast/phpunit-polyfills": "1.1.0",
"brain/monkey": "2.6.1",
"automattic/jetpack-changelogger": "^4.1.2"
"automattic/jetpack-changelogger": "^4.2.4"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -57,7 +58,13 @@
"link-template": "https://github.com/Automattic/jetpack-connection/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "2.5.x-dev"
"dev-trunk": "2.8.x-dev"
},
"dependencies": {
"test-only": [
"packages/licensing",
"packages/sync"
]
}
},
"config": {

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => 'd0b88193b5b4008c3108');

View File

@ -0,0 +1 @@
.jetpack-sso-admin-create-user-invite-message{width:550px}.jetpack-sso-admin-create-user-invite-message-link-sso{text-decoration:none}#createuser .form-field textarea{width:25em}#createuser .form-field [type=checkbox]{width:1rem}#custom_email_message_description{color:#646970;font-size:12px;max-width:25rem}

View File

@ -0,0 +1 @@
document.addEventListener("DOMContentLoaded",(function(){const e=document.getElementById("send_user_notification"),d=document.getElementById("user_external_contractor"),t=document.getElementById("invite_user_wpcom"),n=document.getElementById("custom_email_message_block");t&&e&&n&&(t.addEventListener("change",(function(){e.disabled=t.checked,t.checked?(e.checked=!1,d&&(d.disabled=!1),n.style.display="table"):(d&&(d.disabled=!0,d.checked=!1),n.style.display="none")})),t.checked&&(e.disabled=!0,e.checked=!1,n.style.display="table"),t.checked||(d&&(d.disabled=!0),n.style.display="none"))}));

View File

@ -0,0 +1 @@
.jetpack-sso-admin-create-user-invite-message{width:550px}.jetpack-sso-admin-create-user-invite-message-link-sso{text-decoration:none}#createuser .form-field textarea{width:25em}#createuser .form-field [type=checkbox]{width:1rem}#custom_email_message_description{color:#646970;font-size:12px;max-width:25rem}

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => '049775f3b0a647a127f9');

View File

@ -0,0 +1 @@
#loginform{padding-bottom:92px;position:relative!important}.jetpack-sso-repositioned #loginform{padding-bottom:26px}#loginform #jetpack-sso-wrap,#loginform #jetpack-sso-wrap *{box-sizing:border-box}#jetpack-sso-wrap__action,#jetpack-sso-wrap__user{display:none}.jetpack-sso-form-display #jetpack-sso-wrap__action,.jetpack-sso-form-display #jetpack-sso-wrap__user{display:block}#jetpack-sso-wrap{bottom:20px;margin-left:-24px;margin-right:-24px;padding:0 24px;position:absolute;width:100%}.jetpack-sso-repositioned #jetpack-sso-wrap{bottom:auto;margin-left:0;margin-right:0;margin-top:16px;padding:0;position:relative}.jetpack-sso-form-display #jetpack-sso-wrap{bottom:auto;margin-left:0;margin-right:0;margin-top:0;padding:0;position:relative}#loginform #jetpack-sso-wrap p{color:#777;margin-bottom:16px}#jetpack-sso-wrap a{display:block;text-align:center;text-decoration:none;width:100%}#jetpack-sso-wrap .jetpack-sso-toggle.wpcom{display:none}.jetpack-sso-form-display #jetpack-sso-wrap .jetpack-sso-toggle.wpcom{display:block}.jetpack-sso-form-display #jetpack-sso-wrap .jetpack-sso-toggle.default,.jetpack-sso-form-display #loginform>div,.jetpack-sso-form-display #loginform>p{display:none}.jetpack-sso-form-display #loginform #jetpack-sso-wrap{display:block}.jetpack-sso-form-display #loginform{padding:26px 24px}.jetpack-sso-or{margin-bottom:16px;position:relative;text-align:center}.jetpack-sso-or:before{background:#dcdcde;content:"";height:1px;left:0;position:absolute;top:50%;width:100%}.jetpack-sso-or span{background:#fff;color:#777;padding:0 8px;position:relative;text-transform:uppercase}#jetpack-sso-wrap .button{align-items:center;display:flex;height:36px;justify-content:center;margin-bottom:16px;width:100%}#jetpack-sso-wrap .button .genericon-wordpress{font-size:24px;margin-right:4px}#jetpack-sso-wrap__user img{border-radius:50%;display:block;margin:0 auto 16px}#jetpack-sso-wrap__user h2{font-size:21px;font-weight:300;margin-bottom:16px;text-align:center}#jetpack-sso-wrap__user h2 span{font-weight:700}.jetpack-sso-wrap__reauth{margin-bottom:16px}.jetpack-sso-form-display #nav{display:none}.jetpack-sso-form-display #backtoblog{margin:24px 0 0}.jetpack-sso-clear:after{clear:both;content:"";display:table}

View File

@ -0,0 +1 @@
document.addEventListener("DOMContentLoaded",(()=>{const e=document.querySelector("body"),t=document.querySelector(".jetpack-sso-toggle"),d=document.getElementById("user_login"),o=document.getElementById("user_pass"),s=document.getElementById("jetpack-sso-wrap"),n=document.getElementById("loginform"),c=document.createElement("div");c.className="jetpack-sso-clear",n.appendChild(c),c.appendChild(document.querySelector("p.forgetmenot")),c.appendChild(document.querySelector("p.submit")),n.appendChild(s),e.classList.add("jetpack-sso-repositioned"),t.addEventListener("click",(t=>{t.preventDefault(),e.classList.toggle("jetpack-sso-form-display"),e.classList.contains("jetpack-sso-form-display")||(d.focus(),o.disabled=!1)}))}));

View File

@ -0,0 +1 @@
#loginform{padding-bottom:92px;position:relative!important}.jetpack-sso-repositioned #loginform{padding-bottom:26px}#loginform #jetpack-sso-wrap,#loginform #jetpack-sso-wrap *{box-sizing:border-box}#jetpack-sso-wrap__action,#jetpack-sso-wrap__user{display:none}.jetpack-sso-form-display #jetpack-sso-wrap__action,.jetpack-sso-form-display #jetpack-sso-wrap__user{display:block}#jetpack-sso-wrap{bottom:20px;margin-left:-24px;margin-right:-24px;padding:0 24px;position:absolute;width:100%}.jetpack-sso-repositioned #jetpack-sso-wrap{bottom:auto;margin-left:0;margin-right:0;margin-top:16px;padding:0;position:relative}.jetpack-sso-form-display #jetpack-sso-wrap{bottom:auto;margin-left:0;margin-right:0;margin-top:0;padding:0;position:relative}#loginform #jetpack-sso-wrap p{color:#777;margin-bottom:16px}#jetpack-sso-wrap a{display:block;text-align:center;text-decoration:none;width:100%}#jetpack-sso-wrap .jetpack-sso-toggle.wpcom{display:none}.jetpack-sso-form-display #jetpack-sso-wrap .jetpack-sso-toggle.wpcom{display:block}.jetpack-sso-form-display #jetpack-sso-wrap .jetpack-sso-toggle.default,.jetpack-sso-form-display #loginform>div,.jetpack-sso-form-display #loginform>p{display:none}.jetpack-sso-form-display #loginform #jetpack-sso-wrap{display:block}.jetpack-sso-form-display #loginform{padding:26px 24px}.jetpack-sso-or{margin-bottom:16px;position:relative;text-align:center}.jetpack-sso-or:before{background:#dcdcde;content:"";height:1px;position:absolute;right:0;top:50%;width:100%}.jetpack-sso-or span{background:#fff;color:#777;padding:0 8px;position:relative;text-transform:uppercase}#jetpack-sso-wrap .button{align-items:center;display:flex;height:36px;justify-content:center;margin-bottom:16px;width:100%}#jetpack-sso-wrap .button .genericon-wordpress{font-size:24px;margin-left:4px}#jetpack-sso-wrap__user img{border-radius:50%;display:block;margin:0 auto 16px}#jetpack-sso-wrap__user h2{font-size:21px;font-weight:300;margin-bottom:16px;text-align:center}#jetpack-sso-wrap__user h2 span{font-weight:700}.jetpack-sso-wrap__reauth{margin-bottom:16px}.jetpack-sso-form-display #nav{display:none}.jetpack-sso-form-display #backtoblog{margin:24px 0 0}.jetpack-sso-clear:after{clear:both;content:"";display:table}

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => '04d208524c748ec232f3');

View File

@ -0,0 +1 @@
document.addEventListener("DOMContentLoaded",(function(){function t(){this.querySelector(".jetpack-sso-invitation-tooltip").style.display="block"}function e(t){document.activeElement!==t.target&&(this.querySelector(".jetpack-sso-invitation-tooltip").style.display="none")}document.querySelectorAll(".jetpack-sso-invitation-tooltip-icon:not(.sso-disconnected-user)").forEach((function(t){t.innerHTML+=" [?]";const e=document.createElement("span");e.classList.add("jetpack-sso-invitation-tooltip","jetpack-sso-th-tooltip");const n=window.Jetpack_SSOTooltip.tooltipString;function o(){t.appendChild(e),e.style.display="block"}function i(){document.activeElement!==t&&t.removeChild(e)}e.innerHTML+=n,t.addEventListener("mouseenter",o),t.addEventListener("focus",o),t.addEventListener("mouseleave",i),t.addEventListener("blur",i)})),document.querySelectorAll(".jetpack-sso-invitation-tooltip-icon:not(.jetpack-sso-status-column)").forEach((function(n){n.addEventListener("mouseenter",t),n.addEventListener("focus",t),n.addEventListener("mouseleave",e),n.addEventListener("blur",e)}))}));

View File

@ -12,6 +12,13 @@
use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Manager;
/**
* Disable direct access.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( IXR_Client::class ) ) {
require_once ABSPATH . WPINC . '/class-IXR.php';
}
@ -146,10 +153,10 @@ class Jetpack_IXR_Client extends IXR_Client {
$code = $match[1];
$message = $match[2];
$status = $fault_code;
return new \WP_Error( $code, $message, $status );
return new WP_Error( $code, $message, $status );
}
return new \WP_Error( "IXR_{$fault_code}", $fault_string );
return new WP_Error( "IXR_{$fault_code}", $fault_string );
}
/**

View File

@ -93,9 +93,9 @@ class Jetpack_Signature {
// Convert the $_POST to the body, if the body was empty. This is how arrays are hashed
// and encoded on the Jetpack side.
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Used to generate a cryptographic signature of the post data. Not actually using any of it here.
if ( empty( $body ) && is_array( $_POST ) && $_POST !== array() ) {
$body = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
$body = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- We need all of $_POST in order to generate a cryptographic signature of the post data.
}
}
} elseif ( isset( $_SERVER['REQUEST_METHOD'] ) && 'PUT' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.

View File

@ -440,8 +440,8 @@ class Jetpack_XMLRPC_Server {
* Given an array containing a local user identifier and a nonce, will attempt to fetch and set
* an access token for the given user.
*
* @param array $request An array containing local_user and nonce keys at minimum.
* @param \IXR_Client $ixr_client The client object, optional.
* @param array|ArrayAccess $request An array containing local_user and nonce keys at minimum.
* @param \IXR_Client $ixr_client The client object, optional.
* @return mixed
*/
public function remote_connect( $request, $ixr_client = false ) {

View File

@ -0,0 +1,281 @@
<?php
/**
* Authorize_Json_Api handler class.
* Used to handle connections via JSON API.
* Ported from the Jetpack class.
*
* @since 2.7.6 Ported from the Jetpack class.
*
* @package automattic/jetpack-connection
*/
namespace Automattic\Jetpack\Connection;
use Automattic\Jetpack\Redirect;
use Automattic\Jetpack\Status\Host;
use Jetpack_Options;
/**
* Authorize_Json_Api handler class.
*/
class Authorize_Json_Api {
/**
* Verified data for JSON authorization request
*
* @since 2.7.6
*
* @var array
*/
public $json_api_authorization_request = array();
/**
* Verifies the request by checking the signature
*
* @since jetpack-4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
* passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
* @since 2.7.6 Ported from Jetpack to the Connection package.
*
* @param null|array $environment Value to override $_REQUEST.
*
* @return void
*/
public function verify_json_api_authorization_request( $environment = null ) {
$environment = $environment === null
? $_REQUEST // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verification handled later in function and request data are 1) used to verify a cryptographic signature of the request data and 2) sanitized later in function.
: $environment;
if ( ! isset( $environment['token'] ) ) {
wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack-connection' ) );
}
list( $env_token,, $env_user_id ) = explode( ':', $environment['token'] );
$token = ( new Tokens() )->get_access_token( (int) $env_user_id, $env_token );
if ( ! $token || empty( $token->secret ) ) {
wp_die( esc_html__( 'You must connect your Jetpack plugin to WordPress.com to use this feature.', 'jetpack-connection' ) );
}
$die_error = __( 'Someone may be trying to trick you into giving them access to your site. Or it could be you just encountered a bug :). Either way, please close this window.', 'jetpack-connection' );
// Host has encoded the request URL, probably as a result of a bad http => https redirect.
if (
preg_match( '/https?%3A%2F%2F/i', esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ) > 0 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no site changes, we're erroring out.
) {
/**
* Jetpack authorisation request Error.
*
* @since jetpack-7.5.0
*/
do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
$die_error = sprintf(
/* translators: %s is a URL */
__( 'Your site is incorrectly double-encoding redirects from http to https. This is preventing Jetpack from authenticating your connection. Please visit our <a href="%s">support page</a> for details about how to resolve this.', 'jetpack-connection' ),
esc_url( Redirect::get_url( 'jetpack-support-double-encoding' ) )
);
}
$jetpack_signature = new \Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
$signature = $jetpack_signature->sign_request(
$environment['token'],
$environment['timestamp'],
$environment['nonce'],
'',
'GET',
$environment['jetpack_json_api_original_query'],
null,
true
);
} else {
$signature = $jetpack_signature->sign_current_request(
array(
'body' => null,
'method' => 'GET',
)
);
}
if ( ! $signature ) {
wp_die(
wp_kses(
$die_error,
array(
'a' => array(
'href' => array(),
),
)
)
);
} elseif ( is_wp_error( $signature ) ) {
wp_die(
wp_kses(
$die_error,
array(
'a' => array(
'href' => array(),
),
)
)
);
} elseif ( ! hash_equals( $signature, $environment['signature'] ) ) {
if ( is_ssl() ) {
// If we signed an HTTP request on the Jetpack Servers, but got redirected to HTTPS by the local blog, check the HTTP signature as well.
$signature = $jetpack_signature->sign_current_request(
array(
'scheme' => 'http',
'body' => null,
'method' => 'GET',
)
);
if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
wp_die(
wp_kses(
$die_error,
array(
'a' => array(
'href' => array(),
),
)
)
);
}
} else {
wp_die(
wp_kses(
$die_error,
array(
'a' => array(
'href' => array(),
),
)
)
);
}
}
$timestamp = (int) $environment['timestamp'];
$nonce = stripslashes( (string) $environment['nonce'] );
if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
// De-nonce the nonce, at least for 5 minutes.
// We have to reuse this nonce at least once (used the first time when the initial request is made, used a second time when the login form is POSTed).
$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
if ( $old_nonce_time < time() - 300 ) {
wp_die( esc_html__( 'The authorization process expired. Please go back and try again.', 'jetpack-connection' ) );
}
}
$data = json_decode(
base64_decode( stripslashes( $environment['data'] ) ) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
);
$data_filters = array(
'state' => 'opaque',
'client_id' => 'int',
'client_title' => 'string',
'client_image' => 'url',
);
foreach ( $data_filters as $key => $sanitation ) {
if ( ! isset( $data->$key ) ) {
wp_die(
wp_kses(
$die_error,
array(
'a' => array(
'href' => array(),
),
)
)
);
}
switch ( $sanitation ) {
case 'int':
$this->json_api_authorization_request[ $key ] = (int) $data->$key;
break;
case 'opaque':
$this->json_api_authorization_request[ $key ] = (string) $data->$key;
break;
case 'string':
$this->json_api_authorization_request[ $key ] = wp_kses( (string) $data->$key, array() );
break;
case 'url':
$this->json_api_authorization_request[ $key ] = esc_url_raw( (string) $data->$key );
break;
}
}
if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
wp_die(
wp_kses(
$die_error,
array(
'a' => array(
'href' => array(),
),
)
)
);
}
}
/**
* Add the Access Code details to the public-api.wordpress.com redirect.
*
* @since 2.7.6 Ported from Jetpack to the Connection package.
*
* @param string $redirect_to URL.
* @param string $original_redirect_to URL.
* @param \WP_User $user WP_User for the redirect.
*
* @return string
*/
public function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
return add_query_arg(
urlencode_deep(
array(
'jetpack-code' => get_user_meta(
$user->ID,
'jetpack_json_api_' . $this->json_api_authorization_request['client_id'],
true
),
'jetpack-user-id' => (int) $user->ID,
'jetpack-state' => $this->json_api_authorization_request['state'],
)
),
$redirect_to
);
}
/**
* If someone logs in to approve API access, store the Access Code in usermeta.
*
* @since 2.7.6 Ported from Jetpack to the Connection package.
*
* @param string $user_login Unused.
* @param \WP_User $user User logged in.
*
* @return void
*/
public function store_json_api_authorization_token( $user_login, $user ) {
add_filter( 'login_redirect', array( $this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_public_api_domain' ) );
$token = wp_generate_password( 32, false );
update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
}
/**
* HTML for the JSON API authorization notice.
*
* @since 2.7.6 Ported from Jetpack to the Connection package.
*
* @return string
*/
public function login_message_json_api_authorization() {
return '<p class="message">' . sprintf(
/* translators: Name/image of the client requesting authorization */
esc_html__( '%s wants to access your sites data. Log in to authorize that access.', 'jetpack-connection' ),
'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
}
}

View File

@ -8,6 +8,10 @@
namespace Automattic\Jetpack\Connection;
use Automattic\Jetpack\Constants;
use WP_Error;
// `wp_remote_request` returns an array with a particular format.
'@phan-type _WP_Remote_Response_Array = array{headers:\WpOrg\Requests\Utility\CaseInsensitiveDictionary,body:string,response:array{code:int,message:string},cookies:\WP_HTTP_Cookie[],filename:?string,http_response:WP_HTTP_Requests_Response}';
/**
* The Client class that is used to connect to WordPress.com Jetpack API.
@ -18,9 +22,10 @@ class Client {
/**
* Makes an authorized remote request using Jetpack_Signature
*
* @param array $args the arguments for the remote request.
* @param array|String $body the request body.
* @param array $args the arguments for the remote request.
* @param array|string|null $body the request body.
* @return array|WP_Error WP HTTP response on success
* @phan-return _WP_Remote_Response_Array|WP_Error
*/
public static function remote_request( $args, $body = null ) {
if ( isset( $args['url'] ) ) {
@ -35,7 +40,7 @@ class Client {
}
$result = self::build_signed_request( $args, $body );
if ( ! $result || is_wp_error( $result ) ) {
if ( is_wp_error( $result ) ) {
return $result;
}
@ -64,12 +69,12 @@ class Client {
/**
* Adds authorization signature to a remote request using Jetpack_Signature
*
* @param array $args the arguments for the remote request.
* @param array|String $body the request body.
* @return WP_Error|array {
* @param array $args the arguments for the remote request.
* @param array|string|null $body the request body.
* @return WP_Error|array{url:string,request:array,auth:array} {
* An array containing URL and request items.
*
* @type String $url The request URL.
* @type string $url The request URL.
* @type array $request Request arguments.
* @type array $auth Authorization data.
* }
@ -107,7 +112,7 @@ class Client {
$token = ( new Tokens() )->get_access_token( $args['user_id'] );
if ( ! $token ) {
return new \WP_Error( 'missing_token' );
return new WP_Error( 'missing_token' );
}
$method = strtoupper( $args['method'] );
@ -122,8 +127,8 @@ class Client {
$request = compact( 'method', 'body', 'timeout', 'redirection', 'stream', 'filename', 'sslverify' );
@list( $token_key, $secret ) = explode( '.', $token->secret ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
if ( empty( $token ) || empty( $secret ) ) {
return new \WP_Error( 'malformed_token' );
if ( ! $secret ) {
return new WP_Error( 'malformed_token' );
}
$token_key = sprintf(
@ -141,7 +146,7 @@ class Client {
if ( function_exists( 'wp_generate_password' ) ) {
$nonce = wp_generate_password( 10, false );
} else {
$nonce = substr( sha1( wp_rand( 0, 1000000 ) ), 0, 10 );
$nonce = substr( sha1( (string) wp_rand( 0, 1000000 ) ), 0, 10 );
}
// Kind of annoying. Maybe refactor Jetpack_Signature to handle body-hashing.
@ -166,7 +171,7 @@ class Client {
}
if ( ! is_string( $body_to_hash ) ) {
return new \WP_Error( 'invalid_body', 'Body is malformed.' );
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
}
@ -195,7 +200,7 @@ class Client {
$signature = $jetpack_signature->sign_request( $token_key, $timestamp, $nonce, $body_hash, $method, $url, $body, false );
if ( ! $signature || is_wp_error( $signature ) ) {
if ( is_wp_error( $signature ) ) {
return $signature;
}
@ -232,10 +237,11 @@ class Client {
*
* @internal
*
* @param String $url the request URL.
* @param string $url the request URL.
* @param array $args request arguments.
* @param Boolean $set_fallback whether to allow flagging this request to use a fallback certficate override.
* @param boolean $set_fallback whether to allow flagging this request to use a fallback certficate override.
* @return array|WP_Error WP HTTP response on success
* @phan-return _WP_Remote_Response_Array|WP_Error
*/
public static function _wp_remote_request( $url, $args, $set_fallback = false ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
$fallback = \Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
@ -315,8 +321,9 @@ class Client {
/**
* Sets the time difference for correct signature computation.
*
* @param HTTP_Response $response the response object.
* @param Boolean $force_set whether to force setting the time difference.
* @param array|WP_Error $response Response array from `wp_remote_request`, or WP_Error on error.
* @param bool $force_set whether to force setting the time difference.
* @phan-param _WP_Remote_Response_Array|WP_Error $response
*/
public static function set_time_diff( &$response, $force_set = false ) {
$code = wp_remote_retrieve_response_code( $response );
@ -356,7 +363,7 @@ class Client {
* @param array $args Arguments to {@see WP_Http}. Default is `array()`.
* @param string $base_api_path REST API root. Default is `wpcom`.
*
* @return array|WP_Error $response Response data, else {@see WP_Error} on failure.
* @return array Validated arguments.
*/
public static function validate_args_for_wpcom_json_api_request(
$path,
@ -407,13 +414,14 @@ class Client {
/**
* Queries the WordPress.com REST API with a user token.
*
* @param string $path REST API path.
* @param string $version REST API version. Default is `2`.
* @param array $args Arguments to {@see WP_Http}. Default is `array()`.
* @param string $body Body passed to {@see WP_Http}. Default is `null`.
* @param string $base_api_path REST API root. Default is `wpcom`.
* @param string $path REST API path.
* @param string $version REST API version. Default is `2`.
* @param array $args Arguments to {@see WP_Http}. Default is `array()`.
* @param null|string|array $body Body passed to {@see WP_Http}. Default is `null`.
* @param string $base_api_path REST API root. Default is `wpcom`.
*
* @return array|WP_Error $response Response data, else {@see WP_Error} on failure.
* @phan-return _WP_Remote_Response_Array|WP_Error
*/
public static function wpcom_json_api_request_as_user(
$path,
@ -439,12 +447,13 @@ class Client {
/**
* Query the WordPress.com REST API using the blog token
*
* @param String $path The API endpoint relative path.
* @param String $version The API version.
* @param array $args Request arguments.
* @param String $body Request body.
* @param String $base_api_path (optional) the API base path override, defaults to 'rest'.
* @param string $path The API endpoint relative path.
* @param string $version The API version.
* @param array $args Request arguments.
* @param array|string|null $body Request body.
* @param string $base_api_path (optional) the API base path override, defaults to 'rest'.
* @return array|WP_Error $response Data.
* @phan-return _WP_Remote_Response_Array|WP_Error
*/
public static function wpcom_json_api_request_as_blog(
$path,
@ -471,7 +480,7 @@ class Client {
* make sure that body hashes are made ith the string version, which is what will be seen after a
* server pulls up the data in the $_POST array.
*
* @param array|Mixed $data the data that needs to be stringified.
* @param mixed $data the data that needs to be stringified.
*
* @return array|string
*/

View File

@ -702,11 +702,15 @@ class Error_Handler {
return;
}
?>
<div class="notice notice-error is-dismissible jetpack-message jp-connect" style="display:block !important;">
<p><?php echo esc_html( $message ); ?></p>
</div>
<?php
wp_admin_notice(
esc_html( $message ),
array(
'type' => 'error',
'dismissible' => true,
'additional_classes' => array( 'jetpack-message', 'jp-connect' ),
'attributes' => array( 'style' => 'display:block !important;' ),
)
);
}
/**

View File

@ -7,8 +7,13 @@
namespace Automattic\Jetpack;
use Automattic\Jetpack\Connection\Rest_Authentication;
use Automattic\Jetpack\Connection\REST_Connector;
use Jetpack_Options;
use WP_CLI;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;
/**
* Heartbeat sends a batch of stats to wp.com once a day
@ -73,6 +78,8 @@ class Heartbeat {
if ( defined( 'WP_CLI' ) && WP_CLI ) {
WP_CLI::add_command( 'jetpack-heartbeat', array( $this, 'cli_callback' ) );
}
add_action( 'rest_api_init', array( $this, 'initialize_rest_api' ) );
}
/**
@ -249,4 +256,55 @@ class Heartbeat {
WP_CLI::line( sprintf( __( 'Last heartbeat sent at: %s', 'jetpack-connection' ), $last_date ) );
}
}
/**
* Initialize the heartbeat REST API.
*
* @return void
*/
public function initialize_rest_api() {
register_rest_route(
'jetpack/v4',
'/heartbeat/data',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'rest_heartbeat_data' ),
'permission_callback' => array( $this, 'rest_heartbeat_data_permission_check' ),
'args' => array(
'prefix' => array(
'description' => __( 'Prefix to add before the stats identifiers.', 'jetpack-connection' ),
'type' => 'string',
),
),
)
);
}
/**
* Endpoint to retrieve the heartbeat data.
*
* @param WP_REST_Request $request The request data.
*
* @since 2.7.0
*
* @return array
*/
public function rest_heartbeat_data( WP_REST_Request $request ) {
return static::generate_stats_array( $request->get_param( 'prefix' ) );
}
/**
* Check permissions for the `get_heartbeat_data` endpoint.
*
* @return true|WP_Error
*/
public function rest_heartbeat_data_permission_check() {
if ( current_user_can( 'jetpack_connect' ) ) {
return true;
}
return Rest_Authentication::is_signed_with_blog_token()
? true
: new WP_Error( 'invalid_permission_heartbeat_data', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
}

View File

@ -16,8 +16,10 @@ use Automattic\Jetpack\Status;
use Automattic\Jetpack\Status\Host;
use Automattic\Jetpack\Terms_Of_Service;
use Automattic\Jetpack\Tracking;
use IXR_Error;
use Jetpack_IXR_Client;
use Jetpack_Options;
use Jetpack_XMLRPC_Server;
use WP_Error;
use WP_User;
@ -71,6 +73,13 @@ class Manager {
*/
private static $extra_register_params = array();
/**
* We store ID's of users already disconnected to prevent multiple disconnect requests.
*
* @var array
*/
private static $disconnected_users = array();
/**
* Initialize the object.
* Make sure to call the "Configure" first.
@ -102,7 +111,7 @@ class Manager {
);
$manager->setup_xmlrpc_handlers(
$_GET, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
null,
$manager->has_connected_owner(),
$manager->verify_xml_rpc_signature()
);
@ -127,6 +136,10 @@ class Manager {
Webhooks::init( $manager );
// Unlink user before deleting the user from WP.com.
add_action( 'deleted_user', array( $manager, 'disconnect_user_force' ), 9, 1 );
add_action( 'remove_user_from_blog', array( $manager, 'disconnect_user_force' ), 9, 1 );
// Set up package version hook.
add_filter( 'jetpack_package_versions', __NAMESPACE__ . '\Package_Version::send_package_version_to_tracker' );
@ -148,32 +161,31 @@ class Manager {
* Sets up the XMLRPC request handlers.
*
* @since 1.25.0 Deprecate $is_active param.
* @since 2.8.4 Deprecate $request_params param.
*
* @param array $request_params incoming request parameters.
* @param bool $has_connected_owner Whether the site has a connected owner.
* @param bool $is_signed whether the signature check has been successful.
* @param \Jetpack_XMLRPC_Server $xmlrpc_server (optional) an instance of the server to use instead of instantiating a new one.
* @param array|null $deprecated Deprecated. Not used.
* @param bool $has_connected_owner Whether the site has a connected owner.
* @param bool $is_signed whether the signature check has been successful.
* @param Jetpack_XMLRPC_Server $xmlrpc_server (optional) an instance of the server to use instead of instantiating a new one.
*/
public function setup_xmlrpc_handlers(
$request_params,
$deprecated,
$has_connected_owner,
$is_signed,
\Jetpack_XMLRPC_Server $xmlrpc_server = null
Jetpack_XMLRPC_Server $xmlrpc_server = null
) {
add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ), 1000, 2 );
if (
! isset( $request_params['for'] )
|| 'jetpack' !== $request_params['for']
) {
if ( $deprecated !== null ) {
_deprecated_argument( __METHOD__, '2.8.4' );
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We are using the 'for' request param to early return unless it's 'jetpack'.
if ( ! isset( $_GET['for'] ) || 'jetpack' !== $_GET['for'] ) {
return false;
}
// Alternate XML-RPC, via ?for=jetpack&jetpack=comms.
if (
isset( $request_params['jetpack'] )
&& 'comms' === $request_params['jetpack']
) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This just determines whether to handle the request as an XML-RPC request. The actual XML-RPC endpoints do the appropriate nonce checking where applicable. Plus we make sure to clear all cookies via require_jetpack_authentication called later in method.
if ( isset( $_GET['jetpack'] ) && 'comms' === $_GET['jetpack'] ) {
if ( ! Constants::is_defined( 'XMLRPC_REQUEST' ) ) {
// Use the real constant here for WordPress' sake.
define( 'XMLRPC_REQUEST', true );
@ -193,7 +205,7 @@ class Manager {
if ( $xmlrpc_server ) {
$this->xmlrpc_server = $xmlrpc_server;
} else {
$this->xmlrpc_server = new \Jetpack_XMLRPC_Server();
$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
}
$this->require_jetpack_authentication();
@ -442,9 +454,9 @@ class Manager {
}
$jetpack_signature = new \Jetpack_Signature( $token->secret, (int) \Jetpack_Options::get_option( 'time_diff' ) );
// phpcs:disable WordPress.Security.NonceVerification.Missing
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Used to verify a cryptographic signature of the post data. Also a nonce is verified later in the function.
if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
$post_data = $_POST;
$post_data = $_POST; // We need all of $_POST in order to verify a cryptographic signature of the post data.
$file_hashes = array();
foreach ( $post_data as $post_data_key => $post_data_value ) {
if ( ! str_starts_with( $post_data_key, '_jetpack_file_hmac_' ) ) {
@ -863,6 +875,22 @@ class Manager {
exit();
}
/**
* Force user disconnect.
*
* @param int $user_id Local (external) user ID.
*
* @return bool
*/
public function disconnect_user_force( $user_id ) {
if ( ! (int) $user_id ) {
// Missing user ID.
return false;
}
return $this->disconnect_user( $user_id, true, true );
}
/**
* Unlinks the current user from the linked WordPress.com user.
*
@ -884,6 +912,11 @@ class Manager {
return false;
}
if ( in_array( $user_id, self::$disconnected_users, true ) ) {
// The user is already disconnected.
return false;
}
// Attempt to disconnect the user from WordPress.com.
$is_disconnected_from_wpcom = $this->unlink_user_from_wpcom( $user_id );
@ -913,6 +946,8 @@ class Manager {
}
}
self::$disconnected_users[] = $user_id;
return $is_disconnected_from_wpcom && $is_disconnected_locally;
}
@ -1842,11 +1877,16 @@ class Manager {
/**
* Builds a URL to the Jetpack connection auth page.
*
* @param WP_User $user (optional) defaults to the current logged in user.
* @param String $redirect (optional) a redirect URL to use instead of the default.
* @since 2.7.6 Added optional $from and $raw parameters.
*
* @param WP_User $user (optional) defaults to the current logged in user.
* @param string $redirect (optional) a redirect URL to use instead of the default.
* @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
* @param bool $raw If true, URL will not be escaped.
*
* @return string Connect URL.
*/
public function get_authorization_url( $user = null, $redirect = null ) {
public function get_authorization_url( $user = null, $redirect = null, $from = false, $raw = false ) {
if ( empty( $user ) ) {
$user = wp_get_current_user();
}
@ -1939,8 +1979,28 @@ class Manager {
$url = add_query_arg( $body, $api_url );
/** This filter is documented in plugins/jetpack/class-jetpack.php */
return apply_filters( 'jetpack_build_authorize_url', $url );
if ( is_network_admin() ) {
$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
}
if ( $from ) {
$url = add_query_arg( 'from', $from, $url );
}
if ( $raw ) {
$url = esc_url_raw( $url );
}
/**
* Filter the URL used when connecting a user to a WordPress.com account.
*
* @since 2.0.0
* @since 2.7.6 Added $raw parameter.
*
* @param string $url Connection URL.
* @param bool $raw If true, URL will not be escaped.
*/
return apply_filters( 'jetpack_build_authorize_url', $url, $raw );
}
/**
@ -2287,7 +2347,7 @@ class Manager {
* Handles a getOptions XMLRPC method call.
*
* @param array $args method call arguments.
* @return an amended XMLRPC server options array.
* @return array|IXR_Error An amended XMLRPC server options array.
*/
public function jetpack_get_options( $args ) {
global $wp_xmlrpc_server;

View File

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

View File

@ -24,6 +24,11 @@ class Plugin_Storage {
*/
const PLUGINS_DISABLED_OPTION_NAME = 'jetpack_connection_disabled_plugins';
/**
* Transient name used as flag to indicate that the active connected plugins list needs refreshing.
*/
const ACTIVE_PLUGINS_REFRESH_FLAG = 'jetpack_connection_active_plugins_refresh';
/**
* Whether this class was configured for the first time or not.
*
@ -31,13 +36,6 @@ class Plugin_Storage {
*/
private static $configured = false;
/**
* Refresh list of connected plugins upon intialization.
*
* @var boolean
*/
private static $refresh_connected_plugins = false;
/**
* Connected plugins.
*
@ -65,11 +63,6 @@ class Plugin_Storage {
public static function upsert( $slug, array $args = array() ) {
self::$plugins[ $slug ] = $args;
// if plugin is not in the list of active plugins, refresh the list.
if ( ! array_key_exists( $slug, (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) ) ) {
self::$refresh_connected_plugins = true;
}
return true;
}
@ -167,6 +160,44 @@ class Plugin_Storage {
return;
}
self::$configured = true;
add_action( 'update_option_active_plugins', array( __CLASS__, 'set_flag_to_refresh_active_connected_plugins' ) );
self::maybe_update_active_connected_plugins();
}
/**
* Set a flag to indicate that the active connected plugins list needs to be updated.
* This will happen when the `active_plugins` option is updated.
*
* @see configure
*/
public static function set_flag_to_refresh_active_connected_plugins() {
set_transient( self::ACTIVE_PLUGINS_REFRESH_FLAG, time() );
}
/**
* Determine if we need to update the active connected plugins list.
*/
public static function maybe_update_active_connected_plugins() {
$maybe_error = self::ensure_configured();
if ( $maybe_error instanceof WP_Error ) {
return;
}
// Only attempt to update the option if the corresponding flag is set.
if ( ! get_transient( self::ACTIVE_PLUGINS_REFRESH_FLAG ) ) {
return;
}
// Only attempt to update the option on POST requests.
// This will prevent the option from being updated multiple times due to concurrent requests.
if ( ! ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) ) {
return;
}
delete_transient( self::ACTIVE_PLUGINS_REFRESH_FLAG );
if ( is_multisite() ) {
self::$current_blog_id = get_current_blog_id();
}
@ -175,11 +206,9 @@ class Plugin_Storage {
// self::$plugins is populated in Config::ensure_options_connection().
$number_of_plugins_differ = count( self::$plugins ) !== count( (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) );
if ( $number_of_plugins_differ || true === self::$refresh_connected_plugins ) {
if ( $number_of_plugins_differ ) {
self::update_active_plugins_option();
}
self::$configured = true;
}
/**
@ -188,7 +217,7 @@ class Plugin_Storage {
* @return void
*/
public static function update_active_plugins_option() {
// Note: Since this options is synced to wpcom, if you change its structure, you have to update the sanitizer at wpcom side.
// Note: Since this option is synced to wpcom, if you change its structure, you have to update the sanitizer at wpcom side.
update_option( self::ACTIVE_PLUGINS_OPTION_NAME, self::$plugins );
if ( ! class_exists( 'Automattic\Jetpack\Sync\Settings' ) || ! \Automattic\Jetpack\Sync\Settings::is_sync_enabled() ) {

View File

@ -7,6 +7,8 @@
namespace Automattic\Jetpack\Connection;
use WP_Error;
/**
* The Jetpack Connection Rest Authentication class.
*/
@ -118,7 +120,7 @@ class Rest_Authentication {
}
if ( ! isset( $_SERVER['REQUEST_METHOD'] ) ) {
$this->rest_authentication_status = new \WP_Error(
$this->rest_authentication_status = new WP_Error(
'rest_invalid_request',
__( 'The request method is missing.', 'jetpack-connection' ),
array( 'status' => 400 )
@ -131,7 +133,7 @@ class Rest_Authentication {
// can be passed to the WP REST API via the '?_method=' parameter if
// needed.
if ( 'GET' !== $_SERVER['REQUEST_METHOD'] && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
$this->rest_authentication_status = new \WP_Error(
$this->rest_authentication_status = new WP_Error(
'rest_invalid_request',
__( 'This request method is not supported.', 'jetpack-connection' ),
array( 'status' => 400 )
@ -139,7 +141,7 @@ class Rest_Authentication {
return null;
}
if ( 'POST' !== $_SERVER['REQUEST_METHOD'] && ! empty( file_get_contents( 'php://input' ) ) ) {
$this->rest_authentication_status = new \WP_Error(
$this->rest_authentication_status = new WP_Error(
'rest_invalid_request',
__( 'This request method does not support body parameters.', 'jetpack-connection' ),
array( 'status' => 400 )
@ -173,7 +175,7 @@ class Rest_Authentication {
}
// Something else went wrong. Probably a signature error.
$this->rest_authentication_status = new \WP_Error(
$this->rest_authentication_status = new WP_Error(
'rest_invalid_signature',
__( 'The request is not signed correctly.', 'jetpack-connection' ),
array( 'status' => 400 )

View File

@ -7,6 +7,7 @@
namespace Automattic\Jetpack\Connection;
use Automattic\Jetpack\Connection\Webhooks\Authorize_Redirect;
use Automattic\Jetpack\Constants;
use Automattic\Jetpack\Redirect;
use Automattic\Jetpack\Status;
@ -105,6 +106,28 @@ class REST_Connector {
)
);
// Connect a remote user.
register_rest_route(
'jetpack/v4',
'/remote_connect',
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'remote_connect' ),
'permission_callback' => array( $this, 'remote_connect_permission_check' ),
)
);
// The endpoint verifies blog connection and blog token validity.
register_rest_route(
'jetpack/v4',
'/connection/check',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'connection_check' ),
'permission_callback' => array( $this, 'connection_check_permission_check' ),
)
);
// Get current connection status of Jetpack.
register_rest_route(
'jetpack/v4',
@ -295,7 +318,7 @@ class REST_Connector {
*
* @param WP_REST_Request $request The request sent to the WP REST API.
*
* @return array|wp-error
* @return array|WP_Error
*/
public static function remote_authorize( $request ) {
$xmlrpc_server = new Jetpack_XMLRPC_Server();
@ -328,6 +351,26 @@ class REST_Connector {
return $result;
}
/**
* Connect a remote user.
*
* @since 2.6.0
*
* @param WP_REST_Request $request The request sent to the WP REST API.
*
* @return WP_Error|array
*/
public static function remote_connect( WP_REST_Request $request ) {
$xmlrpc_server = new Jetpack_XMLRPC_Server();
$result = $xmlrpc_server->remote_connect( $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.
*
@ -359,6 +402,17 @@ class REST_Connector {
: new WP_Error( 'invalid_permission_remote_provision', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
/**
* Remote connect endpoint permission check.
*
* @return true|WP_Error
*/
public function remote_connect_permission_check() {
return Rest_Authentication::is_signed_with_blog_token()
? true
: new WP_Error( 'invalid_permission_remote_connect', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
/**
* Remote register endpoint permission check.
*
@ -761,11 +815,7 @@ class REST_Connector {
$redirect_uri = $request->get_param( 'redirect_uri' ) ? admin_url( $request->get_param( 'redirect_uri' ) ) : null;
if ( class_exists( 'Jetpack' ) ) {
$authorize_url = \Jetpack::build_authorize_url( $redirect_uri );
} else {
$authorize_url = $this->connection->get_authorization_url( null, $redirect_uri );
}
$authorize_url = ( new Authorize_Redirect( $this->connection ) )->build_authorize_url( $redirect_uri );
/**
* Filters the response of jetpack/v4/connection/register endpoint
@ -885,7 +935,7 @@ class REST_Connector {
*
* @since 1.29.0
*
* @return bool|WP_Error.
* @return bool|WP_Error
*/
public static function update_user_token_permission_check() {
return Rest_Authentication::is_signed_with_blog_token()
@ -934,4 +984,41 @@ class REST_Connector {
return new WP_Error( 'invalid_user_permission_set_connection_owner', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
/**
* The endpoint verifies blog connection and blog token validity.
*
* @since 2.7.0
*
* @return mixed|null
*/
public function connection_check() {
/**
* Filters the successful response of the REST API test_connection method
*
* @param string $response The response string.
*/
$status = apply_filters( 'jetpack_rest_connection_check_response', 'success' );
return rest_ensure_response(
array(
'status' => $status,
)
);
}
/**
* Remote connect endpoint permission check.
*
* @return true|WP_Error
*/
public function connection_check_permission_check() {
if ( current_user_can( 'jetpack_connect' ) ) {
return true;
}
return Rest_Authentication::is_signed_with_blog_token()
? true
: new WP_Error( 'invalid_permission_connection_check', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
}

View File

@ -158,6 +158,7 @@ class Secrets {
*/
do_action( 'jetpack_verify_secrets_begin', $action, $user );
/** Closure to run the 'fail' action and return an error. */
$return_error = function ( WP_Error $error ) use ( $action, $user ) {
/**
* Verifying of the previously generated secret has failed.

View File

@ -223,7 +223,7 @@ class Server_Sandbox {
*
* Attached to the `admin_bar_menu` action.
*
* @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
* @param \WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
*/
public function admin_bar_add_sandbox_item( $wp_admin_bar ) {
if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {

View File

@ -38,8 +38,8 @@ class Tracking {
/**
* Creates the Tracking object.
*
* @param String $product_name the slug of the product that we are tracking.
* @param Automattic\Jetpack\Connection\Manager $connection the connection manager object.
* @param String $product_name the slug of the product that we are tracking.
* @param \Automattic\Jetpack\Connection\Manager $connection the connection manager object.
*/
public function __construct( $product_name = 'jetpack', $connection = null ) {
$this->product_name = $product_name;
@ -159,7 +159,7 @@ class Tracking {
*
* @param string $event_type Type of the event.
* @param array $data Data to send with the event.
* @param mixed $user Username, user_id, or WP_user object.
* @param mixed $user Username, user_id, or WP_User object.
* @param bool $use_product_prefix Whether to use the object's product name as a prefix to the event type. If
* set to false, the prefix will be 'jetpack_'.
*/
@ -189,7 +189,7 @@ class Tracking {
/**
* Record an event in Tracks - this is the preferred way to record events from PHP.
*
* @param mixed $user username, user_id, or WP_user object.
* @param mixed $user username, user_id, or WP_User object.
* @param string $event_name The name of the event.
* @param array $properties Custom properties to send with the event.
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
@ -221,8 +221,8 @@ class Tracking {
/**
* Determines whether tracking should be enabled.
*
* @param Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
* @param Automattic\Jetpack\Status $status A Status object.
* @param \Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
* @param \Automattic\Jetpack\Status $status A Status object.
*
* @return boolean True if tracking should be enabled, else false.
*/
@ -238,10 +238,10 @@ class Tracking {
* Procedurally build a Tracks Event Object.
* NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
*
* @param WP_user $user WP_user object.
* @param string $event_name The name of the event.
* @param array $properties Custom properties to send with the event.
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
* @param \WP_User $user WP_User object.
* @param string $event_name The name of the event.
* @param array $properties Custom properties to send with the event.
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
*
* @return \Jetpack_Tracks_Event|\WP_Error
*/

View File

@ -82,8 +82,8 @@ class Urls {
/**
* Return URL with a normalized protocol.
*
* @param callable $callable Function to retrieve URL option.
* @param string $new_value URL Protocol to set URLs to.
* @param string $callable Function name that was used to retrieve URL option.
* @param string $new_value URL Protocol to set URLs to.
* @return string Normalized URL.
*/
public static function get_protocol_normalized_url( $callable, $new_value ) {

View File

@ -83,4 +83,53 @@ class Utils {
)
);
}
/**
* Generate a new user from a SSO attempt.
*
* @param object $user_data WordPress.com user information.
*/
public static function generate_user( $user_data ) {
$username = $user_data->login;
/**
* Determines how many times the SSO module can attempt to randomly generate a user.
*
* @module sso
*
* @since jetpack-4.3.2
*
* @param int 5 By default, SSO will attempt to random generate a user up to 5 times.
*/
$num_tries = (int) apply_filters( 'jetpack_sso_allowed_username_generate_retries', 5 );
$exists = username_exists( $username );
$tries = 0;
while ( $exists && $tries++ < $num_tries ) {
$username = $user_data->login . '_' . $user_data->ID . '_' . wp_rand();
$exists = username_exists( $username );
}
if ( $exists ) {
return false;
}
$user = (object) array();
$user->user_pass = wp_generate_password( 20 );
$user->user_login = wp_slash( $username );
$user->user_email = wp_slash( $user_data->email );
$user->display_name = $user_data->display_name;
$user->first_name = $user_data->first_name;
$user->last_name = $user_data->last_name;
$user->url = $user_data->url;
$user->description = $user_data->description;
if ( isset( $user_data->role ) && $user_data->role ) {
$user->role = $user_data->role;
}
$created_user_id = wp_insert_user( $user );
update_user_meta( $created_user_id, 'wpcom_user_id', $user_data->ID );
return get_userdata( $created_user_id );
}
}

View File

@ -107,7 +107,7 @@ class Webhooks {
}
do_action( 'jetpack_client_authorize_processing' );
$data = stripslashes_deep( $_GET );
$data = stripslashes_deep( $_GET ); // We need all request data under the context of an authorization request.
$data['auth_type'] = 'client';
$roles = new Roles();
$role = $roles->translate_current_user_to_role();

View File

@ -7,6 +7,8 @@
namespace Automattic\Jetpack\Connection;
use IXR_Error;
/**
* Registers the XML-RPC methods for Connections.
*/
@ -69,10 +71,10 @@ class XMLRPC_Connector {
$code = -10520;
}
if ( ! class_exists( \IXR_Error::class ) ) {
if ( ! class_exists( IXR_Error::class ) ) {
require_once ABSPATH . WPINC . '/class-IXR.php';
}
return new \IXR_Error(
return new IXR_Error(
$code,
sprintf( 'Jetpack: [%s] %s', $data->get_error_code(), $data->get_error_message() )
);

View File

@ -0,0 +1,184 @@
<?php
/**
* Force Jetpack 2FA Functionality
*
* Ported from original repo at https://github.com/automattic/jetpack-force-2fa
*
* @package automattic/jetpack-connection
*/
namespace Automattic\Jetpack\Connection\SSO;
use Automattic\Jetpack\Connection\SSO;
use Automattic\Jetpack\Modules;
use WP_Error;
/**
* Force users to use two factor authentication.
*/
class Force_2FA {
/**
* The role to force 2FA for.
*
* Defaults to manage_options via the plugins_loaded function.
* Can be modified with the jetpack_force_2fa_cap filter.
*
* @var string
*/
private $role;
/**
* Constructor.
*/
public function __construct() {
add_action( 'after_setup_theme', array( $this, 'plugins_loaded' ) );
}
/**
* Load the plugin via the plugins_loaded hook.
*/
public function plugins_loaded() {
/**
* Filter the role to force 2FA for.
* Defaults to manage_options.
*
* @param string $role The role to force 2FA for.
* @return string
* @since jetpack-12.7
* @module SSO
*/
$this->role = apply_filters( 'jetpack_force_2fa_cap', 'manage_options' );
// Bail if Jetpack SSO is not active
if ( ! ( new Modules() )->is_active( 'sso' ) ) {
add_action( 'admin_notices', array( $this, 'admin_notice' ) );
return;
}
$this->force_2fa();
}
/**
* Display an admin notice if Jetpack SSO is not active.
*/
public function admin_notice() {
/**
* Filter if an admin notice is deplayed when Force 2FA is required, but SSO is not enabled.
* Defaults to true.
*
* @param bool $display_notice Whether to display the notice.
* @return bool
* @since jetpack-12.7
* @module SSO
*/
if ( apply_filters( 'jetpack_force_2fa_dependency_notice', true ) && current_user_can( $this->role ) ) {
wp_admin_notice(
esc_html__( 'Jetpack Force 2FA requires Jetpacks SSO feature.', 'jetpack-connection' ),
array(
'type' => 'warning',
)
);
}
}
/**
* Force 2FA when using Jetpack SSO and force Jetpack SSO.
*
* @return void
*/
private function force_2fa() {
// Allows WP.com login to a local account if it matches the local account.
add_filter( 'jetpack_sso_match_by_email', '__return_true', 9999 );
// multisite
if ( is_multisite() ) {
// Hide the login form
add_filter( 'jetpack_remove_login_form', '__return_true', 9999 );
add_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_true', 9999 );
add_filter( 'jetpack_sso_display_disclaimer', '__return_false', 9999 );
add_filter(
'wp_authenticate_user',
function () {
return new WP_Error( 'wpcom-required', $this->get_login_error_message() ); },
9999
);
add_filter( 'jetpack_sso_require_two_step', '__return_true' );
add_filter( 'allow_password_reset', '__return_false' );
} else {
// Not multisite.
// Completely disable the standard login form for admins.
add_filter(
'wp_authenticate_user',
function ( $user ) {
if ( is_wp_error( $user ) ) {
return $user;
}
if ( $user->has_cap( $this->role ) ) {
return new WP_Error( 'wpcom-required', $this->get_login_error_message(), $user->user_login );
}
return $user;
},
9999
);
add_filter(
'allow_password_reset',
function ( $allow, $user_id ) {
if ( user_can( $user_id, $this->role ) ) {
return false;
}
return $allow; },
9999,
2
);
add_action( 'jetpack_sso_pre_handle_login', array( $this, 'jetpack_set_two_step' ) );
}
}
/**
* Specifically set the two step filter for Jetpack SSO.
*
* @param Object $user_data The user data from WordPress.com.
*
* @return void
*/
public function jetpack_set_two_step( $user_data ) {
$user = SSO::get_user_by_wpcom_id( $user_data->ID );
// Borrowed from Jetpack. Ignores the match_by_email setting.
if ( empty( $user ) ) {
$user = get_user_by( 'email', $user_data->email );
}
if ( $user && $user->has_cap( $this->role ) ) {
add_filter( 'jetpack_sso_require_two_step', '__return_true' );
}
}
/**
* Get the login error message.
*
* @return string
*/
private function get_login_error_message() {
/**
* Filter the login error message.
* Defaults to a message that explains the user must use a WordPress.com account with 2FA enabled.
*
* @param string $message The login error message.
* @return string
* @since jetpack-12.7
* @module SSO
*/
return apply_filters(
'jetpack_force_2fa_login_error_message',
sprintf( 'For added security, please log in using your WordPress.com account.<br /><br />Note: Your account must have <a href="%1$s" target="_blank">Two Step Authentication</a> enabled, which can be configured from <a href="%2$s" target="_blank">Security Settings</a>.', 'https://support.wordpress.com/security/two-step-authentication/', 'https://wordpress.com/me/security/two-step' )
);
}
}

View File

@ -0,0 +1,387 @@
<?php
/**
* A collection of helper functions used in the SSO module.
*
* @package automattic/jetpack-connection
*/
namespace Automattic\Jetpack\Connection\SSO;
use Automattic\Jetpack\Constants;
use Jetpack_IXR_Client;
/**
* A collection of helper functions used in the SSO module.
*
* @since jetpack-4.1.0
*/
class Helpers {
/**
* Determine if the login form should be hidden or not
*
* @return bool
**/
public static function should_hide_login_form() {
/**
* Remove the default log in form, only leave the WordPress.com log in button.
*
* @module sso
*
* @since jetpack-3.1.0
*
* @param bool get_option( 'jetpack_sso_remove_login_form', false ) Should the default log in form be removed. Default to false.
*/
return (bool) apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) );
}
/**
* Returns a boolean value for whether logging in by matching the WordPress.com user email to a
* Jetpack site user's email is allowed.
*
* @return bool
*/
public static function match_by_email() {
$match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? \WPCC_MATCH_BY_EMAIL : (bool) get_option( 'jetpack_sso_match_by_email', true );
/**
* Link the local account to an account on WordPress.com using the same email address.
*
* @module sso
*
* @since jetpack-2.6.0
*
* @param bool $match_by_email Should we link the local account to an account on WordPress.com using the same email address. Default to false.
*/
return (bool) apply_filters( 'jetpack_sso_match_by_email', $match_by_email );
}
/**
* Returns a boolean for whether users are allowed to register on the Jetpack site with SSO,
* even though the site disallows normal registrations.
*
* @param object|null $user_data WordPress.com user information.
* @return bool|string
*/
public static function new_user_override( $user_data = null ) {
$new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? \WPCC_NEW_USER_OVERRIDE : false;
/**
* Allow users to register on your site with a WordPress.com account, even though you disallow normal registrations.
* If you return a string that corresponds to a user role, the user will be given that role.
*
* @module sso
*
* @since jetpack-2.6.0
* @since jetpack-4.6 $user_data object is now passed to the jetpack_sso_new_user_override filter
*
* @param bool|string $new_user_override Allow users to register on your site with a WordPress.com account. Default to false.
* @param object|null $user_data An object containing the user data returned from WordPress.com.
*/
$role = apply_filters( 'jetpack_sso_new_user_override', $new_user_override, $user_data );
if ( $role ) {
if ( is_string( $role ) && get_role( $role ) ) {
return $role;
} else {
return get_option( 'default_role' );
}
}
return false;
}
/**
* Returns a boolean value for whether two-step authentication is required for SSO.
*
* @since jetpack-4.1.0
*
* @return bool
*/
public static function is_two_step_required() {
/**
* Is it required to have 2-step authentication enabled on WordPress.com to use SSO?
*
* @module sso
*
* @since jetpack-2.8.0
*
* @param bool get_option( 'jetpack_sso_require_two_step' ) Does SSO require 2-step authentication?
*/
return (bool) apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step', false ) );
}
/**
* Returns a boolean for whether a user that is attempting to log in will be automatically
* redirected to WordPress.com to begin the SSO flow.
*
* @return bool
*/
public static function bypass_login_forward_wpcom() {
/**
* Redirect the site's log in form to WordPress.com's log in form.
*
* @module sso
*
* @since jetpack-3.1.0
*
* @param bool false Should the site's log in form be automatically forwarded to WordPress.com's log in form.
*/
return (bool) apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false );
}
/**
* Returns a boolean for whether the SSO login form should be displayed as the default
* when both the default and SSO login form allowed.
*
* @since jetpack-4.1.0
*
* @return bool
*/
public static function show_sso_login() {
if ( self::should_hide_login_form() ) {
return true;
}
/**
* Display the SSO login form as the default when both the default and SSO login forms are enabled.
*
* @module sso
*
* @since jetpack-4.1.0
*
* @param bool true Should the SSO login form be displayed by default when the default login form is also enabled?
*/
return (bool) apply_filters( 'jetpack_sso_default_to_sso_login', true );
}
/**
* Returns a boolean for whether the two step required checkbox, displayed on the Jetpack admin page, should be disabled.
*
* @since jetpack-4.1.0
*
* @return bool
*/
public static function is_require_two_step_checkbox_disabled() {
return (bool) has_filter( 'jetpack_sso_require_two_step' );
}
/**
* Returns a boolean for whether the match by email checkbox, displayed on the Jetpack admin page, should be disabled.
*
* @since jetpack-4.1.0
*
* @return bool
*/
public static function is_match_by_email_checkbox_disabled() {
return defined( 'WPCC_MATCH_BY_EMAIL' ) || has_filter( 'jetpack_sso_match_by_email' );
}
/**
* Returns an array of hosts that SSO will redirect to.
*
* Instead of accessing JETPACK__API_BASE within the method directly, we set it as the
* default for $api_base due to restrictions with testing constants in our tests.
*
* @since jetpack-4.3.0
* @since jetpack-4.6.0 Added public-api.wordpress.com as an allowed redirect
*
* @param array $hosts Allowed redirect hosts.
* @param string $api_base Base API URL.
*
* @return array
*/
public static function allowed_redirect_hosts( $hosts, $api_base = '' ) {
if ( empty( $api_base ) ) {
$api_base = Constants::get_constant( 'JETPACK__API_BASE' );
}
if ( empty( $hosts ) ) {
$hosts = array();
}
$hosts[] = 'wordpress.com';
$hosts[] = 'jetpack.wordpress.com';
$hosts[] = 'public-api.wordpress.com';
$hosts[] = 'jetpack.com';
if ( ! str_contains( $api_base, 'jetpack.wordpress.com/jetpack' ) ) {
$base_url_parts = wp_parse_url( esc_url_raw( $api_base ) );
if ( $base_url_parts && ! empty( $base_url_parts['host'] ) ) {
$hosts[] = $base_url_parts['host'];
}
}
return array_unique( $hosts );
}
/**
* Determines how long the auth cookie is valid for when a user logs in with SSO.
*
* @return int result of the jetpack_sso_auth_cookie_expiration filter.
*/
public static function extend_auth_cookie_expiration_for_sso() {
/**
* Determines how long the auth cookie is valid for when a user logs in with SSO.
*
* @module sso
*
* @since jetpack-4.4.0
* @since jetpack-6.1.0 Fixed a typo. Filter was previously jetpack_sso_auth_cookie_expirtation.
*
* @param int YEAR_IN_SECONDS
*/
return (int) apply_filters( 'jetpack_sso_auth_cookie_expiration', YEAR_IN_SECONDS );
}
/**
* Determines if the SSO form should be displayed for the current action.
*
* @since jetpack-4.6.0
*
* @param string $action SSO action being performed.
*
* @return bool Is SSO allowed for the current action?
*/
public static function display_sso_form_for_action( $action ) {
/**
* Allows plugins the ability to overwrite actions where the SSO form is allowed to be used.
*
* @module sso
*
* @since jetpack-4.6.0
*
* @param array $allowed_actions_for_sso
*/
$allowed_actions_for_sso = (array) apply_filters(
'jetpack_sso_allowed_actions',
array(
'login',
'jetpack-sso',
'jetpack_json_api_authorization',
)
);
return in_array( $action, $allowed_actions_for_sso, true );
}
/**
* This method returns an environment array that is meant to simulate `$_REQUEST` when the initial
* JSON API auth request was made.
*
* @since jetpack-4.6.0
*
* @return array|bool
*/
public static function get_json_api_auth_environment() {
if ( empty( $_COOKIE['jetpack_sso_original_request'] ) ) {
return false;
}
$original_request = esc_url_raw( wp_unslash( $_COOKIE['jetpack_sso_original_request'] ) );
$parsed_url = wp_parse_url( $original_request );
if ( empty( $parsed_url ) || empty( $parsed_url['query'] ) ) {
return false;
}
$args = array();
wp_parse_str( $parsed_url['query'], $args );
if ( empty( $args ) || empty( $args['action'] ) ) {
return false;
}
if ( 'jetpack_json_api_authorization' !== $args['action'] ) {
return false;
}
return array_merge(
$args,
array( 'jetpack_json_api_original_query' => $original_request )
);
}
/**
* Check if the site has a custom login page URL, and return it.
* If default login page URL is used (`wp-login.php`), `null` will be returned.
*
* @return string|null
*/
public static function get_custom_login_url() {
$login_url = wp_login_url();
if ( str_ends_with( $login_url, 'wp-login.php' ) ) {
// No custom URL found.
return null;
}
$site_url = trailingslashit( site_url() );
if ( ! str_starts_with( $login_url, $site_url ) ) {
// Something went wrong, we can't properly extract the custom URL.
return null;
}
// Extracting the "path" part of the URL, because we don't need the `site_url` part.
return str_ireplace( $site_url, '', $login_url );
}
/**
* Clear the cookies that store the profile information for the last
* WPCOM user to connect.
*/
public static function clear_wpcom_profile_cookies() {
if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) {
setcookie(
'jetpack_sso_wpcom_name_' . COOKIEHASH,
' ',
time() - YEAR_IN_SECONDS,
COOKIEPATH,
COOKIE_DOMAIN,
is_ssl(),
true
);
}
if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) {
setcookie(
'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
' ',
time() - YEAR_IN_SECONDS,
COOKIEPATH,
COOKIE_DOMAIN,
is_ssl(),
true
);
}
}
/**
* Remove an SSO connection for a user.
*
* @param int $user_id The local user id.
*/
public static function delete_connection_for_user( $user_id ) {
$wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true );
if ( ! $wpcom_user_id ) {
return;
}
$xml = new Jetpack_IXR_Client(
array(
'wpcom_user_id' => $user_id,
)
);
$xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
if ( $xml->isError() ) {
return false;
}
// Clean up local data stored for SSO.
delete_user_meta( $user_id, 'wpcom_user_id' );
delete_user_meta( $user_id, 'wpcom_user_data' );
self::clear_wpcom_profile_cookies();
return $xml->getResponse();
}
}

View File

@ -0,0 +1,241 @@
<?php
/**
* A collection of helper functions used in the SSO module.
*
* @package automattic/jetpack-connection
*/
namespace Automattic\Jetpack\Connection\SSO;
use Automattic\Jetpack\Redirect;
use WP_Error;
use WP_User;
/**
* A collection of helper functions used in the SSO module.
*
* @since jetpack-4.4.0
*/
class Notices {
/**
* Error message displayed on the login form when two step is required and
* the user's account on WordPress.com does not have two step enabled.
*
* @since jetpack-2.7
* @param string $message Error message.
* @return string
**/
public static function error_msg_enable_two_step( $message ) {
$error = sprintf(
wp_kses(
/* translators: URL to settings page */
__(
'Two-Step Authentication is required to access this site. Please visit your <a href="%1$s" rel="noopener noreferrer" target="_blank">Security Settings</a> to configure <a href="%2$s" rel="noopener noreferrer" target="_blank">Two-step Authentication</a> for your account.',
'jetpack-connection'
),
array( 'a' => array( 'href' => array() ) )
),
Redirect::get_url( 'calypso-me-security-two-step' ),
Redirect::get_url( 'wpcom-support-security-two-step-authentication' )
);
$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
return $message;
}
/**
* Error message displayed when the user tries to SSO, but match by email
* is off and they already have an account with their email address on
* this site.
*
* @param string $message Error message.
* @return string
*/
public static function error_msg_email_already_exists( $message ) {
$error = sprintf(
wp_kses(
/* translators: login URL */
__(
'You already have an account on this site. Please <a href="%1$s">sign in</a> with your username and password and then connect to WordPress.com.',
'jetpack-connection'
),
array( 'a' => array( 'href' => array() ) )
),
esc_url_raw( add_query_arg( 'jetpack-sso-show-default-form', '1', wp_login_url() ) )
);
$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
return $message;
}
/**
* Error message that is displayed when the current site is in an identity crisis and SSO can not be used.
*
* @since jetpack-4.3.2
*
* @param string $message Error Message.
*
* @return string
*/
public static function error_msg_identity_crisis( $message ) {
$error = esc_html__( 'Logging in with WordPress.com is not currently available because this site is experiencing connection problems.', 'jetpack-connection' );
$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
return $message;
}
/**
* Error message that is displayed when we are not able to verify the SSO nonce due to an XML error or
* failed validation. In either case, we prompt the user to try again or log in with username and password.
*
* @since jetpack-4.3.2
*
* @param string $message Error message.
*
* @return string
*/
public static function error_invalid_response_data( $message ) {
$error = esc_html__(
'There was an error logging you in via WordPress.com, please try again or try logging in with your username and password.',
'jetpack-connection'
);
$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
return $message;
}
/**
* Error message that is displayed when we were not able to automatically create an account for a user
* after a user has logged in via SSO. By default, this message is triggered after trying to create an account 5 times.
*
* @since jetpack-4.3.2
*
* @param string $message Error message.
*
* @return string
*/
public static function error_unable_to_create_user( $message ) {
$error = esc_html__(
'There was an error creating a user for you. Please contact the administrator of your site.',
'jetpack-connection'
);
$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
return $message;
}
/**
* When the default login form is hidden, this method is called on the 'authenticate' filter with a priority of 30.
* This method disables the ability to submit the default login form.
*
* @param WP_User|WP_Error $user Either the user attempting to login or an existing authentication failure.
*
* @return WP_Error
*/
public static function disable_default_login_form( $user ) {
if ( is_wp_error( $user ) ) {
return $user;
}
/**
* Since we're returning an error that will be shown as a red notice, let's remove the
* informational "blue" notice.
*/
remove_filter( 'login_message', array( static::class, 'msg_login_by_jetpack' ) );
return new WP_Error( 'jetpack_sso_required', self::get_sso_required_message() );
}
/**
* Message displayed when the site admin has disabled the default WordPress
* login form in Settings > General > Secure Sign On
*
* @since jetpack-2.7
* @param string $message Error message.
*
* @return string
**/
public static function msg_login_by_jetpack( $message ) {
$message .= sprintf( '<p class="message">%s</p>', self::get_sso_required_message() );
return $message;
}
/**
* Get the message for SSO required.
*
* @return string
*/
public static function get_sso_required_message() {
$msg = esc_html__(
'A WordPress.com account is required to access this site. Click the button below to sign in or create a free WordPress.com account.',
'jetpack-connection'
);
/**
* Filter the message displayed when the default WordPress login form is disabled.
*
* @module sso
*
* @since jetpack-2.8.0
*
* @param string $msg Disclaimer when default WordPress login form is disabled.
*/
return apply_filters( 'jetpack_sso_disclaimer_message', $msg );
}
/**
* Message displayed when the user can not be found after approving the SSO process on WordPress.com
*
* @param string $message Error message.
*
* @return string
*/
public static function cant_find_user( $message ) {
$error = __(
"We couldn't find your account. If you already have an account, make sure you have connected to WordPress.com.",
'jetpack-connection'
);
/**
* Filters the "couldn't find your account" notice after an attempted SSO.
*
* @module sso
*
* @since jetpack-10.5.0
*
* @param string $error Error text.
*/
$error = apply_filters( 'jetpack_sso_unknown_user_notice', $error );
$message .= sprintf( '<p class="message" id="login_error">%s</p>', esc_html( $error ) );
return $message;
}
/**
* Error message that is displayed when the current site is in an identity crisis and SSO can not be used.
*
* @since jetpack-4.4.0
*
* @param string $message Error message.
*
* @return string
*/
public static function sso_not_allowed_in_staging( $message ) {
$error = __(
'Logging in with WordPress.com is disabled for sites that are in staging mode.',
'jetpack-connection'
);
/**
* Filters the disallowed notice for staging sites attempting SSO.
*
* @module sso
*
* @since jetpack-10.5.0
*
* @param string $error Error text.
*/
$error = apply_filters( 'jetpack_sso_disallowed_staging_notice', $error );
$message .= sprintf( '<p class="message">%s</p>', esc_html( $error ) );
return $message;
}
}

View File

@ -0,0 +1,21 @@
.jetpack-sso-admin-create-user-invite-message {
width: 550px;
}
.jetpack-sso-admin-create-user-invite-message-link-sso {
text-decoration: none;
}
#createuser .form-field textarea {
width: 25em;
}
#createuser .form-field [type=checkbox] {
width: 1rem;
}
#custom_email_message_description {
max-width: 25rem;
color: #646970;
font-size: 12px;
}

View File

@ -0,0 +1,45 @@
document.addEventListener( 'DOMContentLoaded', function () {
const sendUserNotificationCheckbox = document.getElementById( 'send_user_notification' );
const userExternalContractorCheckbox = document.getElementById( 'user_external_contractor' );
const inviteUserWpcomCheckbox = document.getElementById( 'invite_user_wpcom' );
const customEmailMessageBlock = document.getElementById( 'custom_email_message_block' );
if ( inviteUserWpcomCheckbox && sendUserNotificationCheckbox && customEmailMessageBlock ) {
// Toggle Send User Notification checkbox enabled/disabled based on Invite User checkbox
// Enable External Contractor checkbox if Invite User checkbox is checked
// Show/hide the external email message field.
inviteUserWpcomCheckbox.addEventListener( 'change', function () {
sendUserNotificationCheckbox.disabled = inviteUserWpcomCheckbox.checked;
if ( inviteUserWpcomCheckbox.checked ) {
sendUserNotificationCheckbox.checked = false;
if ( userExternalContractorCheckbox ) {
userExternalContractorCheckbox.disabled = false;
}
customEmailMessageBlock.style.display = 'table';
} else {
if ( userExternalContractorCheckbox ) {
userExternalContractorCheckbox.disabled = true;
userExternalContractorCheckbox.checked = false;
}
customEmailMessageBlock.style.display = 'none';
}
} );
// On load, disable Send User Notification checkbox
// and show the custom email message if Invite User checkbox is checked
if ( inviteUserWpcomCheckbox.checked ) {
sendUserNotificationCheckbox.disabled = true;
sendUserNotificationCheckbox.checked = false;
customEmailMessageBlock.style.display = 'table';
}
// On load, disable External Contractor checkbox
// and hide the custom email message if Invite User checkbox is unchecked
if ( ! inviteUserWpcomCheckbox.checked ) {
if ( userExternalContractorCheckbox ) {
userExternalContractorCheckbox.disabled = true;
}
customEmailMessageBlock.style.display = 'none';
}
}
} );

View File

@ -0,0 +1,164 @@
#loginform {
/* We set !important because sometimes static is added inline */
position: relative !important;
padding-bottom: 92px;
}
.jetpack-sso-repositioned #loginform {
padding-bottom: 26px;
}
#loginform #jetpack-sso-wrap,
#loginform #jetpack-sso-wrap * {
box-sizing: border-box;
}
#jetpack-sso-wrap__action,
#jetpack-sso-wrap__user {
display: none;
}
.jetpack-sso-form-display #jetpack-sso-wrap__action,
.jetpack-sso-form-display #jetpack-sso-wrap__user {
display: block;
}
#jetpack-sso-wrap {
position: absolute;
bottom: 20px;
padding: 0 24px;
margin-left: -24px;
margin-right: -24px;
width: 100%;
}
.jetpack-sso-repositioned #jetpack-sso-wrap {
position: relative;
bottom: auto;
padding: 0;
margin-top: 16px;
margin-left: 0;
margin-right: 0;
}
.jetpack-sso-form-display #jetpack-sso-wrap {
position: relative;
bottom: auto;
padding: 0;
margin-top: 0;
margin-left: 0;
margin-right: 0;
}
#loginform #jetpack-sso-wrap p {
color: #777777;
margin-bottom: 16px;
}
#jetpack-sso-wrap a {
display: block;
width: 100%;
text-align: center;
text-decoration: none;
}
#jetpack-sso-wrap .jetpack-sso-toggle.wpcom {
display: none;
}
.jetpack-sso-form-display #jetpack-sso-wrap .jetpack-sso-toggle.wpcom {
display: block;
}
.jetpack-sso-form-display #jetpack-sso-wrap .jetpack-sso-toggle.default {
display: none;
}
.jetpack-sso-form-display #loginform>p,
.jetpack-sso-form-display #loginform>div {
display: none;
}
.jetpack-sso-form-display #loginform #jetpack-sso-wrap {
display: block;
}
.jetpack-sso-form-display #loginform {
padding: 26px 24px;
}
.jetpack-sso-or {
margin-bottom: 16px;
position: relative;
text-align: center;
}
.jetpack-sso-or:before {
background: #dcdcde;
content: '';
height: 1px;
position: absolute;
left: 0;
top: 50%;
width: 100%;
}
.jetpack-sso-or span {
background: #fff;
color: #777;
position: relative;
padding: 0 8px;
text-transform: uppercase
}
#jetpack-sso-wrap .button {
display: flex;
justify-content: center;
align-items: center;
height: 36px;
margin-bottom: 16px;
width: 100%;
}
#jetpack-sso-wrap .button .genericon-wordpress {
font-size: 24px;
margin-right: 4px;
}
#jetpack-sso-wrap__user img {
border-radius: 50%;
display: block;
margin: 0 auto 16px;
}
#jetpack-sso-wrap__user h2 {
font-size: 21px;
font-weight: 300;
margin-bottom: 16px;
text-align: center;
}
#jetpack-sso-wrap__user h2 span {
font-weight: bold;
}
.jetpack-sso-wrap__reauth {
margin-bottom: 16px;
}
.jetpack-sso-form-display #nav {
display: none;
}
.jetpack-sso-form-display #backtoblog {
margin: 24px 0 0;
}
.jetpack-sso-clear:after {
content: "";
display: table;
clear: both;
}

View File

@ -0,0 +1,27 @@
document.addEventListener( 'DOMContentLoaded', () => {
const body = document.querySelector( 'body' ),
toggleSSO = document.querySelector( '.jetpack-sso-toggle' ),
userLogin = document.getElementById( 'user_login' ),
userPassword = document.getElementById( 'user_pass' ),
ssoWrap = document.getElementById( 'jetpack-sso-wrap' ),
loginForm = document.getElementById( 'loginform' ),
overflow = document.createElement( 'div' );
overflow.className = 'jetpack-sso-clear';
loginForm.appendChild( overflow );
overflow.appendChild( document.querySelector( 'p.forgetmenot' ) );
overflow.appendChild( document.querySelector( 'p.submit' ) );
loginForm.appendChild( ssoWrap );
body.classList.add( 'jetpack-sso-repositioned' );
toggleSSO.addEventListener( 'click', e => {
e.preventDefault();
body.classList.toggle( 'jetpack-sso-form-display' );
if ( ! body.classList.contains( 'jetpack-sso-form-display' ) ) {
userLogin.focus();
userPassword.disabled = false;
}
} );
} );

View File

@ -0,0 +1,64 @@
document.addEventListener( 'DOMContentLoaded', function () {
document
.querySelectorAll( '.jetpack-sso-invitation-tooltip-icon:not(.sso-disconnected-user)' )
.forEach( function ( tooltip ) {
tooltip.innerHTML += ' [?]';
const tooltipTextbox = document.createElement( 'span' );
tooltipTextbox.classList.add( 'jetpack-sso-invitation-tooltip', 'jetpack-sso-th-tooltip' );
const tooltipString = window.Jetpack_SSOTooltip.tooltipString;
tooltipTextbox.innerHTML += tooltipString;
tooltip.addEventListener( 'mouseenter', appendTooltip );
tooltip.addEventListener( 'focus', appendTooltip );
tooltip.addEventListener( 'mouseleave', removeTooltip );
tooltip.addEventListener( 'blur', removeTooltip );
/**
* Display the tooltip textbox.
*/
function appendTooltip() {
tooltip.appendChild( tooltipTextbox );
tooltipTextbox.style.display = 'block';
}
/**
* Remove the tooltip textbox.
*/
function removeTooltip() {
// Only remove tooltip if the element isn't currently active.
if ( document.activeElement === tooltip ) {
return;
}
tooltip.removeChild( tooltipTextbox );
}
} );
document
.querySelectorAll( '.jetpack-sso-invitation-tooltip-icon:not(.jetpack-sso-status-column)' )
.forEach( function ( tooltip ) {
tooltip.addEventListener( 'mouseenter', appendSSOInvitationTooltip );
tooltip.addEventListener( 'focus', appendSSOInvitationTooltip );
tooltip.addEventListener( 'mouseleave', removeSSOInvitationTooltip );
tooltip.addEventListener( 'blur', removeSSOInvitationTooltip );
} );
/**
* Display the SSO invitation tooltip textbox.
*/
function appendSSOInvitationTooltip() {
this.querySelector( '.jetpack-sso-invitation-tooltip' ).style.display = 'block';
}
/**
* Remove the SSO invitation tooltip textbox.
*
* @param {Event} event - Triggering event.
*/
function removeSSOInvitationTooltip( event ) {
if ( document.activeElement === event.target ) {
return;
}
this.querySelector( '.jetpack-sso-invitation-tooltip' ).style.display = 'none';
}
} );

View File

@ -10,6 +10,7 @@ namespace Automattic\Jetpack\Connection\Webhooks;
use Automattic\Jetpack\Admin_UI\Admin_Menu;
use Automattic\Jetpack\Constants;
use Automattic\Jetpack\Licensing;
use Automattic\Jetpack\Status\Host;
use Automattic\Jetpack\Tracking;
use GP_Locales;
use Jetpack_Network;
@ -21,14 +22,14 @@ class Authorize_Redirect {
/**
* The Connection Manager object.
*
* @var Manager
* @var \Automattic\Jetpack\Connection\Manager
*/
private $connection;
/**
* Constructs the object
*
* @param Automattic\Jetpack\Connection\Manager $connection The Connection Manager object.
* @param \Automattic\Jetpack\Connection\Manager $connection The Connection Manager object.
*/
public function __construct( $connection ) {
$this->connection = $connection;
@ -97,44 +98,57 @@ class Authorize_Redirect {
}
/**
* Create the Jetpack authorization URL. Copied from Jetpack class.
* Create the Jetpack authorization URL.
*
* @since 2.7.6 Added optional $from and $raw parameters.
*
* @param bool|string $redirect URL to redirect to.
* @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
* @param bool $raw If true, URL will not be escaped.
*
* @todo Update default value for redirect since the called function expects a string.
*
* @return mixed|void
*/
public function build_authorize_url( $redirect = false ) {
public function build_authorize_url( $redirect = false, $from = false, $raw = false ) {
add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
$url = $this->connection->get_authorization_url( wp_get_current_user(), $redirect );
$url = $this->connection->get_authorization_url( wp_get_current_user(), $redirect, $from, $raw );
remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
/** This filter is documented in plugins/jetpack/class-jetpack.php */
return apply_filters( 'jetpack_build_authorize_url', $url );
/**
* Filter the URL used when authorizing a user to a WordPress.com account.
*
* @since jetpack-8.9.0
* @since 2.7.6 Added $raw parameter.
*
* @param string $url Connection URL.
* @param bool $raw If true, URL will not be escaped.
*/
return apply_filters( 'jetpack_build_authorize_url', $url, $raw );
}
/**
* Filters the redirection URL that is used for connect requests. The redirect
* URL should return the user back to the Jetpack console.
* Copied from Jetpack class.
* URL should return the user back to the My Jetpack page.
*
* @param String $redirect the default redirect URL used by the package.
* @return String the modified URL.
* @param string $redirect the default redirect URL used by the package.
* @return string the modified URL.
*/
public static function filter_connect_redirect_url( $redirect ) {
$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=my-jetpack' ) );
$redirect = $redirect
? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
: $jetpack_admin_page;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_REQUEST['is_multisite'] ) ) {
if (
class_exists( 'Jetpack_Network' )
&& isset( $_REQUEST['is_multisite'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
) {
$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
}
@ -143,7 +157,6 @@ class Authorize_Redirect {
/**
* Filters the connection URL parameter array.
* Copied from Jetpack class.
*
* @param array $args default URL parameters used by the package.
* @return array the modified URL arguments array.
@ -170,7 +183,7 @@ class Authorize_Redirect {
)
);
$calypso_env = self::get_calypso_env();
$calypso_env = ( new Host() )->get_calypso_env();
if ( ! empty( $calypso_env ) ) {
$args['calypso_env'] = $calypso_env;
@ -184,25 +197,15 @@ class Authorize_Redirect {
* it with different Calypso enrionments, such as localhost.
* Copied from Jetpack class.
*
* @deprecated 2.7.6
*
* @since 1.37.1
*
* @return string Calypso environment
*/
public static function get_calypso_env() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['calypso_env'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
return sanitize_key( $_GET['calypso_env'] );
}
_deprecated_function( __METHOD__, '2.7.6', 'Automattic\\Jetpack\\Status\\Host::get_calypso_env' );
if ( getenv( 'CALYPSO_ENV' ) ) {
return sanitize_key( getenv( 'CALYPSO_ENV' ) );
}
if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
return sanitize_key( CALYPSO_ENV );
}
return '';
return ( new Host() )->get_calypso_env();
}
}

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-04-30
### Changed
- Internal updates.
## [2.0.1] - 2024-03-12
### Changed
- Internal updates.
@ -162,6 +166,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Packages: Finish the constants package
[2.0.2]: https://github.com/Automattic/jetpack-constants/compare/v2.0.1...v2.0.2
[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

View File

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

View File

@ -29,7 +29,7 @@ class Constants {
* @access public
* @static
*
* @var array.
* @var array
*/
public static $set_constants = array();

View File

@ -5,6 +5,13 @@ 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.3] - 2024-04-08
### Added
- Added functionality for extracting the browser and desktop platform from a user agent. [#36568]
### Changed
- Add new bots. [#36477]
## [2.1.2] - 2024-03-18
### Changed
- Internal updates.
@ -189,6 +196,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Moving jetpack_is_mobile into a package
[2.1.3]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.2...v2.1.3
[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

View File

@ -49,6 +49,8 @@ class Device_Detection {
'is_smartphone' => self::is_mobile( 'smart', false, $ua_info ),
'is_tablet' => $ua_info->is_tablet(),
'platform' => $ua_info->get_platform(),
'desktop_platform' => $ua_info->get_desktop_platform(),
'browser' => $ua_info->get_browser(),
);
$info['is_handheld'] = $info['is_phone'] || $info['is_tablet'];

View File

@ -64,19 +64,30 @@ class User_Agent_Info {
*
* @var null|string
*/
private $platform = null;
const PLATFORM_WINDOWS = 'windows';
const PLATFORM_IPHONE = 'iphone';
const PLATFORM_IPOD = 'ipod';
const PLATFORM_IPAD = 'ipad';
const PLATFORM_BLACKBERRY = 'blackberry';
const PLATFORM_BLACKBERRY_10 = 'blackberry_10';
const PLATFORM_SYMBIAN = 'symbian_series60';
const PLATFORM_SYMBIAN_S40 = 'symbian_series40';
const PLATFORM_J2ME_MIDP = 'j2me_midp';
const PLATFORM_ANDROID = 'android';
const PLATFORM_ANDROID_TABLET = 'android_tablet';
const PLATFORM_FIREFOX_OS = 'firefoxOS';
private $platform = null;
const PLATFORM_WINDOWS = 'windows';
const PLATFORM_IPHONE = 'iphone';
const PLATFORM_IPOD = 'ipod';
const PLATFORM_IPAD = 'ipad';
const PLATFORM_BLACKBERRY = 'blackberry';
const PLATFORM_BLACKBERRY_10 = 'blackberry_10';
const PLATFORM_SYMBIAN = 'symbian_series60';
const PLATFORM_SYMBIAN_S40 = 'symbian_series40';
const PLATFORM_J2ME_MIDP = 'j2me_midp';
const PLATFORM_ANDROID = 'android';
const PLATFORM_ANDROID_TABLET = 'android_tablet';
const PLATFORM_FIREFOX_OS = 'firefoxOS';
const PLATFORM_DESKTOP_LINUX = 'linux';
const PLATFORM_DESKTOP_MAC = 'mac';
const PLATFORM_DESKTOP_WINDOWS = 'windows';
const PLATFORM_DESKTOP_CHROME = 'chrome';
const BROWSER_CHROME = 'chrome';
const BROWSER_FIREFOX = 'firefox';
const BROWSER_SAFARI = 'safari';
const BROWSER_EDGE = 'edge';
const BROWSER_OPERA = 'opera';
const BROWSER_IE = 'ie';
const OTHER = 'other';
/**
* A list of dumb-phone user agent parts.
@ -277,6 +288,57 @@ class User_Agent_Info {
return $this->platform;
}
/**
* Returns the platform for desktops
*
* @return string
*/
public function get_desktop_platform() {
$ua = $this->useragent;
if ( empty( $ua ) ) {
return false;
}
$platform = self::OTHER;
if ( static::is_linux_desktop() ) {
$platform = self::PLATFORM_DESKTOP_LINUX;
} elseif ( static::is_mac_desktop() ) {
$platform = self::PLATFORM_DESKTOP_MAC;
} elseif ( static::is_windows_desktop() ) {
$platform = self::PLATFORM_DESKTOP_WINDOWS;
} elseif ( static::is_chrome_desktop() ) {
$platform = self::PLATFORM_DESKTOP_CHROME;
}
return $platform;
}
/**
* A simple pattern matching method for extracting the browser from the user agent.
*
* @return string
*/
public function get_browser() {
$ua = $this->useragent;
if ( empty( $ua ) ) {
return self::OTHER;
}
if ( static::is_opera_mini() || static::is_opera_mobile() || static::is_opera_desktop() || static::is_opera_mini_dumb() ) {
return self::BROWSER_OPERA;
} elseif ( static::is_edge_browser() ) {
return self::BROWSER_EDGE;
} elseif ( static::is_chrome_desktop() || self::is_chrome_for_iOS() ) {
return self::BROWSER_CHROME;
} elseif ( static::is_safari_browser() ) {
return self::BROWSER_SAFARI;
} elseif ( static::is_firefox_mobile() || static::is_firefox_desktop() ) {
return self::BROWSER_FIREFOX;
} elseif ( static::is_ie_browser() ) {
return self::BROWSER_IE;
}
return self::OTHER;
}
/**
* This method detects for UA which can display iPhone-optimized web content.
* Includes iPhone, iPod Touch, Android, WebOS, Fennec (Firefox mobile), etc.
@ -714,6 +776,46 @@ class User_Agent_Info {
}
}
/**
* Detect Safari browser
*/
public static function is_safari_browser() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
if ( false === strpos( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 'Safari' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
return false;
}
return true;
}
/**
* Detect Edge browser
*/
public static function is_edge_browser() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
if ( false === strpos( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 'Edge' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
return false;
}
return true;
}
/**
* Detect Edge browser
*/
public static function is_ie_browser() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
$ua = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
if ( false === ( strpos( $ua, 'MSIE' ) || strpos( $ua, 'Trident/7' ) ) ) {
return false;
}
return true;
}
/**
* Detect modern Opera desktop
*
@ -1271,6 +1373,66 @@ class User_Agent_Info {
return ( strpos( $agent, 'bb10' ) !== false ) && ( strpos( $agent, 'mobile' ) !== false );
}
/**
* Determines whether a desktop platform is Linux OS
*
* @return bool
*/
public static function is_linux_desktop() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
if ( ! preg_match( '/linux/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
return false;
}
return true;
}
/**
* Determines whether a desktop platform is Mac OS
*
* @return bool
*/
public static function is_mac_desktop() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
if ( ! preg_match( '/macintosh|mac os x/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
return false;
}
return true;
}
/**
* Determines whether a desktop platform is Windows OS
*
* @return bool
*/
public static function is_windows_desktop() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
if ( ! preg_match( '/windows|win32/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
return false;
}
return true;
}
/**
* Determines whether a desktop platform is Chrome OS
*
* @return bool
*/
public static function is_chrome_desktop() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
if ( ! preg_match( '/chrome/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
return false;
}
return true;
}
/**
* Retrieve the blackberry OS version.
*
@ -1579,6 +1741,7 @@ class User_Agent_Info {
'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
'linkwalker', // https://www.linkwalker.com/
);
foreach ( $bot_agents as $bot_agent ) {

View File

@ -5,6 +5,62 @@ 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.20.0] - 2024-05-20
### Removed
- IDC: Remove deprecated code. [#37421]
## [0.19.0] - 2024-05-16
### Added
- IDC: Add escapes to echo statements. [#37395]
### Changed
- Updated package dependencies. [#37379]
- Updated package dependencies. [#37380]
## [0.18.6] - 2024-05-06
### Changed
- Updated package dependencies. [#37147]
### Fixed
- Fix class references in `URL_Secret::create_secret()` exception handler. [#37201]
## [0.18.5] - 2024-04-29
### Changed
- Internal updates.
## [0.18.4] - 2024-04-25
### Changed
- Update dependencies.
## [0.18.3] - 2024-04-08
### Changed
- Updated package dependencies. [#36760]
## [0.18.2] - 2024-04-02
### Changed
- Internal updates.
## [0.18.1] - 2024-04-01
### Changed
- Tests: moved tests from a separate files to the main tests file. [#36656]
## [0.18.0] - 2024-03-29
### Added
- Packages: added version tracking for identity-crisis. [#36635]
- REST: added endpoint for IDC validation. [#36537]
## [0.17.6] - 2024-03-27
### Changed
- Updated package dependencies. [#36585]
## [0.17.5] - 2024-03-25
### Changed
- Internal updates.
## [0.17.4] - 2024-03-18
### Changed
- Internal updates.
## [0.17.3] - 2024-03-14
### Changed
- Internal updates.
@ -503,6 +559,18 @@ 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.20.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.19.0...v0.20.0
[0.19.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.6...v0.19.0
[0.18.6]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.5...v0.18.6
[0.18.5]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.4...v0.18.5
[0.18.4]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.3...v0.18.4
[0.18.3]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.2...v0.18.3
[0.18.2]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.1...v0.18.2
[0.18.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.18.0...v0.18.1
[0.18.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.6...v0.18.0
[0.17.6]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.5...v0.17.6
[0.17.5]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.4...v0.17.5
[0.17.4]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.17.3...v0.17.4
[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

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' => '607b018606bd0a465d3b');
<?php return array('dependencies' => array('react', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '4b5c20bc6fc08deb6c73');

View File

@ -5,14 +5,14 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"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"
"automattic/jetpack-connection": "^2.8.3",
"automattic/jetpack-constants": "^2.0.2",
"automattic/jetpack-status": "^3.1.0",
"automattic/jetpack-logo": "^2.0.3",
"automattic/jetpack-assets": "^2.1.11"
},
"require-dev": {
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/jetpack-changelogger": "^4.2.4",
"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.17.x-dev"
"dev-trunk": "0.20.x-dev"
}
},
"config": {

View File

@ -26,7 +26,12 @@ class Identity_Crisis {
/**
* Package Version
*/
const PACKAGE_VERSION = '0.17.3';
const PACKAGE_VERSION = '0.20.0';
/**
* Package Slug
*/
const PACKAGE_SLUG = 'identity-crisis';
/**
* Persistent WPCOM blog ID that stays in the options after disconnect.
@ -58,7 +63,7 @@ class Identity_Crisis {
/**
* The current screen, which is set if the current user is a non-admin and this is an admin page.
*
* @var WP_Screen
* @var \WP_Screen
*/
public static $current_screen;
@ -98,6 +103,9 @@ class Identity_Crisis {
add_filter( 'jetpack_register_request_body', array( static::class, 'register_request_body' ) );
add_action( 'jetpack_site_registered', array( static::class, 'site_registered' ) );
// Set up package version hook.
add_filter( 'jetpack_package_versions', array( static::class, 'send_package_version_to_tracker' ) );
$urls_in_crisis = self::check_identity_crisis();
if ( false === $urls_in_crisis ) {
return;
@ -107,6 +115,19 @@ class Identity_Crisis {
add_action( 'init', array( $this, 'wordpress_init' ) );
}
/**
* Adds the package slug and version to the package version tracker's data.
*
* @param array $package_versions The package version array.
*
* @return array The package version array.
*/
public static function send_package_version_to_tracker( $package_versions ) {
$package_versions[ self::PACKAGE_SLUG ] = self::PACKAGE_VERSION;
return $package_versions;
}
/**
* Disconnect current connection and clear IDC options.
*/
@ -138,17 +159,6 @@ class Identity_Crisis {
return ! self::validate_sync_error_idc_option();
}
/**
* 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' );
}
/**
* This method loops through the array of processed items from sync and checks if one of the items was the
* home_url or site_url callable. If so, then we delete the jetpack_migrate_for_idc option.
@ -354,19 +364,6 @@ class Identity_Crisis {
return false;
}
/**
* 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 ) );
}
/**
* Clears all IDC specific options. This method is used on disconnect and reconnect.
*
@ -571,447 +568,6 @@ class Identity_Crisis {
return (bool) apply_filters( 'jetpack_should_handle_idc', $default );
}
/**
* 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">
<g>
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"></path>
</g>
</svg>
<div class="dops-notice__content">
<span class="dops-notice__text">
<?php esc_html_e( 'Something went wrong:', 'jetpack-idc' ); ?>
<span class="jp-idc-error__desc"></span>
</span>
<a class="dops-notice__action" href="javascript:void(0);">
<span id="jp-idc-error__action">
<?php esc_html_e( 'Try Again', 'jetpack-idc' ); ?>
</span>
</a>
</div>
</div>
<?php
}
/**
* 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">
<h3 class="jp-idc-notice__content-header__lead">
<?php echo $this->get_first_step_header_lead(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</h3>
<p class="jp-idc-notice__content-header__explanation">
<?php echo $this->get_first_step_header_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
</div>
<?php $this->render_error_notice(); ?>
<div class="jp-idc-notice__actions">
<div class="jp-idc-notice__action">
<p class="jp-idc-notice__action__explanation">
<?php echo $this->get_confirm_safe_mode_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
<button id="jp-idc-confirm-safe-mode-action" class="dops-button">
<?php echo $this->get_confirm_safe_mode_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</button>
</div>
<div class="jp-idc-notice__action">
<p class="jp-idc-notice__action__explanation">
<?php echo $this->get_first_step_fix_connection_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
<button id="jp-idc-fix-connection-action" class="dops-button">
<?php echo $this->get_first_step_fix_connection_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</button>
</div>
</div>
</div>
<?php
}
/**
* 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">
<h3 class="jp-idc-notice__content-header__lead">
<?php echo $this->get_second_step_header_lead(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</h3>
</div>
<?php $this->render_error_notice(); ?>
<div class="jp-idc-notice__actions">
<div class="jp-idc-notice__action">
<p class="jp-idc-notice__action__explanation">
<?php echo $this->get_migrate_site_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
<button id="jp-idc-migrate-action" class="dops-button">
<?php echo $this->get_migrate_site_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</button>
</div>
<div class="jp-idc-notice__action">
<p class="jp-idc-notice__action__explanation">
<?php echo $this->get_start_fresh_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
<button id="jp-idc-reconnect-site-action" class="dops-button">
<?php echo $this->get_start_fresh_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</button>
</div>
</div>
<p class="jp-idc-notice__unsure-prompt">
<?php echo $this->get_unsure_prompt(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
</div>
<?php
}
/**
* 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. */
__( 'Jetpack has been placed into <a href="%1$s">Safe mode</a> because we noticed this is an exact copy of <a href="%2$s">%3$s</a>.', 'jetpack-idc' ),
esc_url( self::get_safe_mod_doc_url() ),
esc_url( self::$wpcom_home_url ),
self::prepare_url_for_display( esc_url_raw( self::$wpcom_home_url ) )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default header text in the first step of the Safe Mode notice.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_first_step_header_lead', $html );
}
/**
* 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. */
__( 'Please confirm Safe Mode or fix the Jetpack connection. Select one of the options below or <a href="%1$s">learn more about Safe Mode</a>.', 'jetpack-idc' ),
esc_url( self::get_safe_mod_doc_url() )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default header explanation text in the first step of the Safe Mode notice.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_first_step_header_explanation', $html );
}
/**
* 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. */
__( 'Is this website a temporary duplicate of <a href="%1$s">%2$s</a> for the purposes of testing, staging or development? If so, we recommend keeping it in Safe Mode.', 'jetpack-idc' ),
esc_url( untrailingslashit( self::$wpcom_home_url ) ),
self::prepare_url_for_display( esc_url( self::$wpcom_home_url ) )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default text used to explain the confirm safe mode action.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_confirm_safe_mode_explanation', $html );
}
/**
* 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' );
/**
* Allows overriding of the default text used for the confirm safe mode action button.
*
* @param string $string The string to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_confirm_safe_mode_button_text', $string );
}
/**
* 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. */
__( 'If this is a separate and new website, or the new home of <a href="%1$s">%2$s</a>, we recommend turning Safe Mode off, and re-establishing your connection to WordPress.com.', 'jetpack-idc' ),
esc_url( untrailingslashit( self::$wpcom_home_url ) ),
self::prepare_url_for_display( esc_url( self::$wpcom_home_url ) )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default text used to explain the fix Jetpack connection action.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_first_fix_connection_explanation', $html );
}
/**
* 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' );
/**
* Allows overriding of the default text used for the fix Jetpack connection action button.
*
* @param string $string The string to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_first_step_fix_connection_button_text', $string );
}
/**
* 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' ),
untrailingslashit( self::normalize_url_protocol_agnostic( get_home_url() ) ),
untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
);
/**
* Allows overriding of the default header text in the second step of the Safe Mode notice.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_second_step_header_lead', $string );
}
/**
* 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. */
__( 'Yes. <a href="%1$s">%2$s</a> is replacing <a href="%3$s">%4$s</a>. I would like to migrate my stats and subscribers from <a href="%3$s">%4$s</a> to <a href="%1$s">%2$s</a>.', 'jetpack-idc' ),
esc_url( get_home_url() ),
self::prepare_url_for_display( get_home_url() ),
esc_url( self::$wpcom_home_url ),
untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default text for explaining the migrate site action.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_migrate_site_explanation', $html );
}
/**
* 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' );
/**
* Allows overriding of the default text used for the migrate site action button.
*
* @param string $string The string to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_migrate_site_button_text', $string );
}
/**
* 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. */
__( 'No. <a href="%1$s">%2$s</a> is a new and different website that\'s separate from <a href="%3$s">%4$s</a>. It requires a new connection to WordPress.com for new stats and subscribers.', 'jetpack-idc' ),
esc_url( get_home_url() ),
self::prepare_url_for_display( get_home_url() ),
esc_url( self::$wpcom_home_url ),
untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default text for explaining the start fresh action.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_start_fresh_explanation', $html );
}
/**
* 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' );
/**
* Allows overriding of the default text used for the start fresh action button.
*
* @param string $string The string to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_start_fresh_button_text', $string );
}
/**
* 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. */
__( 'Unsure what to do? <a href="%1$s">Read more about Jetpack Safe Mode</a>', 'jetpack-idc' ),
esc_url( self::get_safe_mod_doc_url() )
),
array( 'a' => array( 'href' => array() ) )
);
/**
* Allows overriding of the default text using in the "Unsure what to do?" prompt.
*
* @param string $html The HTML to be displayed.
*
* @since 0.2.0
* @since-jetpack 4.4.0
*/
return apply_filters( 'jetpack_idc_unsure_prompt', $html );
}
/**
* Whether the site is undergoing identity crisis.
*
@ -1138,6 +694,7 @@ class Identity_Crisis {
* @param array $response The endpoint response that we're modifying.
*
* @return array
*
* phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag -- The exception is being caught, false positive.
*/
public static function add_secret_to_url_validation_response( array $response ) {

View File

@ -10,6 +10,7 @@ namespace Automattic\Jetpack\IdentityCrisis;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Connection\Rest_Authentication;
use Jetpack_Options;
use Jetpack_XMLRPC_Server;
use WP_Error;
use WP_REST_Server;
@ -64,6 +65,17 @@ class REST_Endpoints {
)
);
// Fetch URL and secret for IDC check.
register_rest_route(
'jetpack/v4',
'/identity-crisis/idc-url-validation',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( static::class, 'validate_urls_and_set_secret' ),
'permission_callback' => array( static::class, 'url_secret_permission_check' ),
)
);
// Fetch URL verification secret.
register_rest_route(
'jetpack/v4',
@ -214,6 +226,20 @@ class REST_Endpoints {
return new WP_Error( 'invalid_user_permission_identity_crisis', $error_msg, array( 'status' => rest_authorization_required_code() ) );
}
/**
* Endpoint for URL validation and creating a secret.
*
* @since 0.18.0
*
* @return array
*/
public static function validate_urls_and_set_secret() {
$xmlrpc_server = new Jetpack_XMLRPC_Server();
$result = $xmlrpc_server->validate_urls_for_idc_mitigation();
return $result;
}
/**
* Endpoint for fetching the existing secret.
*

View File

@ -7,6 +7,8 @@
namespace Automattic\Jetpack\IdentityCrisis;
use Automattic\Jetpack\Connection\Urls;
use Automattic\Jetpack\Tracking;
use Jetpack_Options;
/**

View File

@ -5,6 +5,46 @@ 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.11] - 2024-05-16
### Changed
- Updated package dependencies. [#37379]
## [3.1.10] - 2024-05-06
### Changed
- Updated package dependencies. [#37147]
## [3.1.9] - 2024-04-26
### Changed
- Internal updates.
## [3.1.8] - 2024-04-25
### Changed
- Internal updates.
## [3.1.7] - 2024-04-22
### Changed
- Internal updates.
## [3.1.6] - 2024-04-15
### Changed
- Internal updates.
## [3.1.5] - 2024-04-11
### Changed
- Internal updates.
## [3.1.4] - 2024-04-08
### Changed
- Updated package dependencies. [#36760]
## [3.1.3] - 2024-03-27
### Changed
- Updated package dependencies. [#36585]
## [3.1.2] - 2024-03-25
### Changed
- Update Phan baselines [#36441]
## [3.1.1] - 2024-03-14
### Changed
- Internal updates.
@ -683,6 +723,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update Jetpack to use new JITM package
[3.1.11]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.10...v3.1.11
[3.1.10]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.9...v3.1.10
[3.1.9]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.8...v3.1.9
[3.1.8]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.7...v3.1.8
[3.1.7]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.6...v3.1.7
[3.1.6]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.5...v3.1.6
[3.1.5]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.4...v3.1.5
[3.1.4]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.3...v3.1.4
[3.1.3]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.2...v3.1.3
[3.1.2]: https://github.com/Automattic/jetpack-jitm/compare/v3.1.1...v3.1.2
[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

View File

@ -6,17 +6,17 @@
"require": {
"php": ">=7.0",
"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"
"automattic/jetpack-assets": "^2.1.10",
"automattic/jetpack-connection": "^2.8.2",
"automattic/jetpack-device-detection": "^2.1.3",
"automattic/jetpack-logo": "^2.0.2",
"automattic/jetpack-redirect": "^2.0.2",
"automattic/jetpack-status": "^3.0.3"
},
"require-dev": {
"brain/monkey": "2.6.1",
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.1"
"automattic/jetpack-changelogger": "^4.2.3"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."

View File

@ -20,7 +20,7 @@ use Automattic\Jetpack\Status;
*/
class JITM {
const PACKAGE_VERSION = '3.1.1';
const PACKAGE_VERSION = '3.1.11';
/**
* The configuration method that is called from the jetpack-config package.

View File

@ -27,7 +27,7 @@ class Post_Connection_JITM extends JITM {
/**
* Tracking object.
*
* @var Automattic\Jetpack\Tracking
* @var \Automattic\Jetpack\Tracking
*
* @access private
*/
@ -43,9 +43,9 @@ class Post_Connection_JITM extends JITM {
/**
* A special filter for WooCommerce, to set a message based on local state.
*
* @param string $content The current message.
* @param object $content The current message.
*
* @return array The new message.
* @return object The new message.
*/
public static function jitm_woocommerce_services_msg( $content ) {
if ( ! function_exists( 'wc_get_base_location' ) ) {

View File

@ -8,6 +8,8 @@
namespace Automattic\Jetpack\JITMS;
use Automattic\Jetpack\Connection\REST_Connector;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;
/**
@ -61,7 +63,7 @@ class Rest_Api_Endpoints {
$query['s'] = $request['s'];
}
return $jitm->get_messages( $request['message_path'], urldecode_deep( $query ), 'true' === $request['full_jp_logo_exists'] ? true : false );
return $jitm->get_messages( $request['message_path'], urldecode_deep( $query ), 'true' === $request['full_jp_logo_exists'] );
}
/**
@ -91,6 +93,6 @@ class Rest_Api_Endpoints {
return true;
}
return new \WP_Error( 'invalid_user_permission_jetpack_delete_jitm_message', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
return new WP_Error( 'invalid_user_permission_jetpack_delete_jitm_message', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
}
}

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.0.5] - 2024-04-22
### Changed
- Internal updates.
## [2.0.4] - 2024-04-08
### Changed
- Internal updates.
## [2.0.3] - 2024-03-25
### Changed
- Internal updates.
## [2.0.2] - 2024-03-14
### Changed
- Internal updates.
@ -260,6 +272,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Licensing: Add support for Jetpack licenses
[2.0.5]: https://github.com/Automattic/jetpack-licensing/compare/v2.0.4...v2.0.5
[2.0.4]: https://github.com/Automattic/jetpack-licensing/compare/v2.0.3...v2.0.4
[2.0.3]: https://github.com/Automattic/jetpack-licensing/compare/v2.0.2...v2.0.3
[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

View File

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

View File

@ -13,6 +13,8 @@ use Automattic\Jetpack\Licensing;
use Automattic\Jetpack\Status\Visitor;
use Jetpack_Options;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
/**
* Class Endpoints.

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.3] - 2024-05-20
### Changed
- Replaced heredoc syntax with strings. [#37396]
## [2.0.2] - 2024-03-18
### Changed
- Internal updates.
@ -174,6 +178,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Packages: Add a basic Jetpack Logo package
[2.0.3]: https://github.com/Automattic/jetpack-logo/compare/v2.0.2...v2.0.3
[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

View File

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

View File

@ -19,14 +19,14 @@ namespace Automattic\Jetpack\Assets;
*
* @var string
*/
const JETPACK_LOGO_SVG = <<<'EOSVG'
const JETPACK_LOGO_SVG = '
<svg xmlns="http://www.w3.org/2000/svg" height="32" class="jetpack-logo" viewBox="0 0 118 32">
<path class="jetpack-logo__icon-circle" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z" fill="#069e08" />
<polygon class="jetpack-logo__icon-triangle" points="15,19 7,19 15,3" fill="#fff" />
<polygon class="jetpack-logo__icon-triangle" points="17,29 17,13 25,13" fill="#fff" />
<path class="jetpack-logo__text" d="M41.3 26.6c-.5-.7-.9-1.4-1.3-2.1 2.3-1.4 3-2.5 3-4.6V8h-3V6h6v13.4C46 22.8 45 24.8 41.3 26.6zM58.5 21.3c-1.5.5-2.7.6-4.2.6-3.6 0-5.8-1.8-5.8-6 0-3.1 1.9-5.9 5.5-5.9s4.9 2.5 4.9 4.9c0 .8 0 1.5-.1 2h-7.3c.1 2.5 1.5 2.8 3.6 2.8 1.1 0 2.2-.3 3.4-.7C58.5 19 58.5 21.3 58.5 21.3zM56 15c0-1.4-.5-2.9-2-2.9-1.4 0-2.3 1.3-2.4 2.9C51.6 15 56 15 56 15zM65 18.4c0 1.1.8 1.3 1.4 1.3.5 0 2-.2 2.6-.4v2.1c-.9.3-2.5.5-3.7.5-1.5 0-3.2-.5-3.2-3.1V12H60v-2h2.1V7.1H65V10h4v2h-4V18.4zM71 10h3v1.3c1.1-.8 1.9-1.3 3.3-1.3 2.5 0 4.5 1.8 4.5 5.6s-2.2 6.3-5.8 6.3c-.9 0-1.3-.1-2-.3V28h-3V10zM76.5 12.3c-.8 0-1.6.4-2.5 1.2v5.9c.6.1.9.2 1.8.2 2 0 3.2-1.3 3.2-3.9C79 13.4 78.1 12.3 76.5 12.3zM93 22h-3v-1.5c-.9.7-1.9 1.5-3.5 1.5-1.5 0-3.1-1.1-3.1-3.2 0-2.9 2.5-3.4 4.2-3.7l2.4-.3v-.3c0-1.5-.5-2.3-2-2.3-.7 0-2.3.5-3.7 1.1L84 11c1.2-.4 3-1 4.4-1 2.7 0 4.6 1.4 4.6 4.7L93 22zM90 16.4l-2.2.4c-.7.1-1.4.5-1.4 1.6 0 .9.5 1.4 1.3 1.4s1.5-.5 2.3-1V16.4zM104.5 21.3c-1.1.4-2.2.6-3.5.6-4.2 0-5.9-2.4-5.9-5.9 0-3.7 2.3-6 6.1-6 1.4 0 2.3.2 3.2.5V13c-.8-.3-2-.6-3.2-.6-1.7 0-3.2.9-3.2 3.6 0 2.9 1.5 3.8 3.3 3.8.9 0 1.9-.2 3.2-.7V21.3zM110 15.2c.2-.3.2-.8 3.8-5.2h3.7l-4.6 5.7 5 6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z" />
</svg>
EOSVG;
';
/**
* Create and render a Jetpack logo.

View File

@ -5,6 +5,145 @@ 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.24.1] - 2024-05-24
### Changed
- Update Search to require user connection. [#37496]
## [4.24.0] - 2024-05-23
### Changed
- Improve the active/inactive statuses on My Jetpack. [#37217]
## [4.23.3] - 2024-05-20
### Added
- Add tracks events for dropdown on action buttons. [#37292]
### Changed
- Updated package dependencies. [#37379] [#37380] [#37382]
## [4.23.2] - 2024-05-09
### Changed
- My Jetpack Agency banner copy change. [#37248]
### Fixed
- Fix typo [#37303]
## [4.23.1] - 2024-05-08
### Changed
- Update dependencies.
## [4.23.0] - 2024-05-06
### Added
- Add a has_free_offering boolean on My Jetpack products. [#36710]
- Add copy for AI features on connection screen. [#37218]
- Define `$module_name` static property on `Hybrid_Product` (defaulting to null). [#37201]
- Jetpack AI: Add new badge to Featured Image video on the product page. [#37197]
- My Jetpack: Add errors from the connection package to the new notice component. [#36840]
### Changed
- Change "go back" in My Jetpack interstitials to always return to the My Jetpack page. [#36685]
- Fix z-index issue and close action button dropdown when clicked outside. [#37169]
- Jetpack AI: Add featured image generation as a feature on the product interstitial. [#37199]
- Jetpack AI: Include video about featured image generation on the product page. [#37192]
- Updated package dependencies. [#37147] [#37148] [#37160]
## [4.22.3] - 2024-04-29
### Changed
- Internal updates.
## [4.22.2] - 2024-04-25
### Fixed
- My Jetpack: Fix issue where the TOS component was being called inside of a <p>, throwing a warning that <p> can't be a descendant of <p>. This also fixes the font size of the TOS text. [#37034]
## [4.22.1] - 2024-04-22
### Changed
- Internal updates.
## [4.22.0] - 2024-04-11
### Added
- Add new tracking event for product activations made through My Jetpack [#36745]
- My Jetpack: add a way to connect a Jetpack site to WordPress.com asynchronously and update the message in the notice to reflect that. [#36771]
### Fixed
- Ensure page_view gets loaded before product_card_loaded [#36790]
## [4.21.0] - 2024-04-08
### Added
- Add 'from' property to connection call to WP.com. [#36741]
- My Jetpack: Update Notice component to allow adding a loading text when an action is in a loading state. Add a new resetNotice action to NoticeContext. [#36614]
### Changed
- My Jetpack: Show Boost score increase in Boost product card. [#36072]
- Updated package dependencies. [#36756] [#36760] [#36761]
- Update My Jetpack to use the Notice component from @automattic/jetpack-components and be more consistent with the other notices in Jetpack. [#36711]
### Removed
- My Jetpack: Removed custom Notice component from My Jetpack. [#36772]
- My Jetpack: Remove red bubble from connection notice in favor of using the status of the Notice component. [#36773]
### Fixed
- Fix cache on front end request for boost speed scores [#36700]
- fix tier upgrades in my Jetpack [#36705]
## [4.20.2] - 2024-04-02
### Fixed
- Fix Boost score inconsistency [#36679]
## [4.20.1] - 2024-04-01
### Added
- Change Phan baselines. [#36627]
## [4.20.0] - 2024-03-29
### Added
- Track active red bubble slugs on My Jetpack page view [#36611]
### Fixed
- Better handling on product interstitial pages if the site already has a paid product [#36570]
- Shows Jetpack CRM card as active on My Jetpack if the plugin is installed and active [#36594]
## [4.19.0] - 2024-03-27
### Added
- Add red bubble to notices tied to red bubble notifications [#36543]
- My Jetpack: add a version of WordPress' Notice component to My Jetpack considering the context of how we use notices on that screen [#36551]
### Changed
- Updated package dependencies. [#36539, #36585]
### Fixed
- Fixed Jetpack Creator going to the wrong screen when the free version is selected" [#36547]
- Fixes some pricing showing twice by connecting sites that select a free option [#36533]
## [4.18.0] - 2024-03-25
### Added
- Hook into red bubble notification when bad installation is detected [#36449]
- Jetpack AI: add notices on product page for exhausted requests [#35910]
- Jetpack AI: add plans/tier information on product page and corresponding CTAs [#35910]
- Jetpack AI: add support and create post links on product page [#35910]
- My Jetpack: add AI product page view event [#36488]
- My Jetpack: add feedback link on Jetpack AI product page [#35910]
- My Jetpack: AI pricing table is skipped once the user has opted for the free version [#35910]
- My Jetpack: change AI product for tiered pricing table display [#35910]
### Changed
- Add notice priorities to My Jetpack [#36438]
- Jetpack AI: address responsive issues on the styles [#35910]
- My Jetpack: AI product page styles update and responsive fixes [#35910]
- My Jetpack: change AI card action button target for upgraded users, point to product page [#35910]
### Removed
- My Jetpack: remove Jetpack AI code added throughout the new product page project [#35910]
- Removed reference to Creator Network, which is being deprecated. [#36168]
### Fixed
- Boost tooltips: fix typo in string. [#36520]
- My Jetpack: fix AI interstitial "remain free" flow [#35910]
- My Jetpack: fix interstitial event property malformed name productSlug -> product_slug [#36486]
- My Jetpack: fix spacing issues on the new product page [#35910]
- My Jetpack: new AI interstitial margin on admin page was messing with correct top spacing [#35910]
## [4.17.1] - 2024-03-18
### Added
- Add a loaded event for My Jetpack product cards [#36397]
## [4.17.0] - 2024-03-14
### Changed
- Rewrite My Jetpack hooks to Typescript [#36288]
@ -1347,6 +1486,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Created package
[4.24.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.24.0...4.24.1
[4.24.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.3...4.24.0
[4.23.3]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.2...4.23.3
[4.23.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.1...4.23.2
[4.23.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.23.0...4.23.1
[4.23.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.3...4.23.0
[4.22.3]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.2...4.22.3
[4.22.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.1...4.22.2
[4.22.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.22.0...4.22.1
[4.22.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.21.0...4.22.0
[4.21.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.20.2...4.21.0
[4.20.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.20.1...4.20.2
[4.20.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.20.0...4.20.1
[4.20.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.19.0...4.20.0
[4.19.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.18.0...4.19.0
[4.18.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.17.1...4.18.0
[4.17.1]: https://github.com/Automattic/jetpack-my-jetpack/compare/4.17.0...4.17.1
[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

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' => '5782f9bf704638c25046');
<?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' => 'cc72b3b90919a178b53e');

View File

@ -5,23 +5,22 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0",
"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"
"automattic/jetpack-admin-ui": "^0.4.2",
"automattic/jetpack-assets": "^2.1.11",
"automattic/jetpack-boost-speed-score": "^0.3.11",
"automattic/jetpack-connection": "^2.8.4",
"automattic/jetpack-jitm": "^3.1.11",
"automattic/jetpack-licensing": "^2.0.5",
"automattic/jetpack-plugins-installer": "^0.4.0",
"automattic/jetpack-redirect": "^2.0.2",
"automattic/jetpack-constants": "^2.0.2",
"automattic/jetpack-plans": "^0.4.7",
"automattic/jetpack-status": "^3.2.0"
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
"automattic/jetpack-changelogger": "^4.1.1",
"automattic/wordbless": "@dev",
"automattic/jetpack-videopress": "^0.23.10"
"automattic/jetpack-changelogger": "^4.2.4",
"automattic/wordbless": "@dev"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
@ -69,10 +68,16 @@
"link-template": "https://github.com/Automattic/jetpack-my-jetpack/compare/${old}...${new}"
},
"branch-alias": {
"dev-trunk": "4.17.x-dev"
"dev-trunk": "4.24.x-dev"
},
"version-constants": {
"::PACKAGE_VERSION": "src/class-initializer.php"
},
"dependencies": {
"test-only": [
"packages/search",
"packages/videopress"
]
}
},
"config": {

View File

@ -1,7 +1,24 @@
declare module '*.png';
declare module '*.svg';
declare module '*.jpeg';
declare module '*.jpg';
declare module '*.scss';
// These libraries don't have types, this suppresses the TypeScript errors
declare module '@wordpress/components';
declare module '@wordpress/compose';
declare module '@wordpress/i18n';
declare module '@wordpress/icons';
interface Window {
myJetpackInitialState?: {
siteSuffix: string;
siteUrl: string;
latestBoostSpeedScores: {
previousScores: {
desktop: number;
mobile: number;
};
scores: {
desktop: number;
mobile: number;
@ -62,8 +79,6 @@ interface Window {
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[];
@ -179,12 +194,18 @@ interface Window {
check_dns: boolean;
} >;
};
redBubbleAlerts: {
'missing-site-connection'?: null;
'welcome-banner-active'?: null;
[ key: `${ string }-bad-installation` ]: {
data: {
plugin: string;
};
};
};
topJetpackMenuItemUrl: string;
userIsAdmin: string;
userIsNewToJetpack: string;
welcomeBanner: {
hasBeenDismissed: boolean;
};
};
JP_CONNECTION_INITIAL_STATE: {
apiRoot: string;

View File

@ -9,7 +9,6 @@ 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;
@ -26,6 +25,7 @@ use Automattic\Jetpack\Status\Host as Status_Host;
use Automattic\Jetpack\Terms_Of_Service;
use Automattic\Jetpack\Tracking;
use Jetpack;
use WP_Error;
/**
* The main Initializer class that registers the admin menu and eneuque the assets.
@ -37,7 +37,7 @@ class Initializer {
*
* @var string
*/
const PACKAGE_VERSION = '4.17.0';
const PACKAGE_VERSION = '4.24.1';
/**
* HTML container ID for the IDC screen on My Jetpack page.
@ -62,7 +62,7 @@ class Initializer {
/**
* Holds info/data about the site (from the /sites/%d endpoint)
*
* @var stdClass Object
* @var object
*/
public static $site_info;
@ -87,8 +87,7 @@ class Initializer {
}
// Initialize Boost Speed Score
$boost_modules = Jetpack_Boost_Modules::init();
new Speed_Score( $boost_modules, 'jetpack-my-jetpack' );
new Speed_Score( array(), 'jetpack-my-jetpack' );
// Add custom WP REST API endoints.
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
@ -200,7 +199,14 @@ class Initializer {
);
$modules = new Modules();
$connection = new Connection_Manager();
$speed_score_history = new Speed_Score_History( wp_parse_url( get_site_url(), PHP_URL_HOST ) );
$speed_score_history = new Speed_Score_History( get_site_url() );
$latest_score = $speed_score_history->latest();
$previous_score = array();
if ( $speed_score_history->count() > 1 ) {
$previous_score = $speed_score_history->latest( 1 );
}
$latest_score['previousScores'] = $previous_score['scores'] ?? array();
wp_localize_script(
'my_jetpack_main_app',
'myJetpackInitialState',
@ -216,6 +222,7 @@ class Initializer {
'myJetpackCheckoutUri' => admin_url( 'admin.php?page=my-jetpack' ),
'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
'siteSuffix' => ( new Status() )->get_site_suffix(),
'siteUrl' => esc_url( get_site_url() ),
'blogID' => Connection_Manager::get_site_id( true ),
'myJetpackVersion' => self::PACKAGE_VERSION,
'myJetpackFlags' => self::get_my_jetpack_flags(),
@ -237,14 +244,11 @@ class Initializer {
'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(
'isEnabled' => Jetpack_Manage::could_use_jp_manage(),
'isAgencyAccount' => Jetpack_Manage::is_agency_account(),
),
'latestBoostSpeedScores' => $speed_score_history->latest(),
'latestBoostSpeedScores' => $latest_score,
)
);
@ -496,7 +500,7 @@ class Initializer {
$body = json_decode( wp_remote_retrieve_body( $response ) );
if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
return new \WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ) );
return new WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ) );
}
return rest_ensure_response( $body, 200 );
@ -505,7 +509,7 @@ class Initializer {
/**
* Populates the self::$site_info var with site data from the /sites/%d endpoint
*
* @return Object|WP_Error
* @return object|WP_Error
*/
public static function get_site_info() {
static $site_info = null;
@ -620,7 +624,7 @@ class Initializer {
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' ) );
add_filter( 'my_jetpack_red_bubble_notification_slugs', array( __CLASS__, 'add_red_bubble_alerts' ) );
$red_bubble_alerts = self::get_red_bubble_alerts();
// The Jetpack menu item should be on index 3
@ -653,6 +657,22 @@ class Initializer {
return $red_bubble_alerts;
}
/**
* Add relevant red bubble notifications
*
* @param array $red_bubble_slugs - slugs that describe the reasons the red bubble is showing.
* @return array
*/
public static function add_red_bubble_alerts( array $red_bubble_slugs ) {
$welcome_banner_dismissed = \Jetpack_Options::get_option( 'dismissed_welcome_banner', false );
if ( self::is_jetpack_user_new() && ! $welcome_banner_dismissed ) {
$red_bubble_slugs['welcome-banner-active'] = null;
return $red_bubble_slugs;
} else {
return self::alert_if_missing_site_connection( $red_bubble_slugs );
}
}
/**
* Add an alert slug if the site is missing a site connection
*
@ -661,7 +681,7 @@ class Initializer {
*/
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;
$red_bubble_slugs[ self::MISSING_SITE_CONNECTION_NOTIFICATION_KEY ] = null;
}
return $red_bubble_slugs;

View File

@ -36,6 +36,7 @@ class Products {
'protect' => Products\Protect::class,
'videopress' => Products\Videopress::class,
'stats' => Products\Stats::class,
'ai' => Products\Jetpack_Ai::class,
);
/**
@ -155,7 +156,7 @@ class Products {
'status' => array(
'title' => 'The product status',
'type' => 'string',
'enum' => array( 'active', 'inactive', 'plugin_absent', 'needs_purchase', 'needs_purchase_or_free', 'error' ),
'enum' => array( 'active', 'inactive', 'plugin_absent', 'needs_purchase', 'needs_purchase_or_free', 'needs_first_site_connection', 'user_connection_error', 'site_connection_error' ),
),
'class' => array(
'title' => 'The product class handler',
@ -179,6 +180,7 @@ class Products {
'protect',
'crm',
'search',
'ai',
);
// Add plugin action links for the core Jetpack plugin.

View File

@ -190,6 +190,7 @@ class REST_Products {
$activate_product_result->add_data( array( 'status' => 400 ) );
return $activate_product_result;
}
set_transient( 'my_jetpack_product_activated', $product_slug, 10 );
return rest_ensure_response( Products::get_product( $product_slug ), 200 );
}

View File

@ -9,6 +9,7 @@ namespace Automattic\Jetpack\My_Jetpack;
use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use WP_Error;
/**
* Registers the REST routes for Purchases.
@ -42,7 +43,7 @@ class REST_Purchases {
$is_site_connected = $connection->is_connected();
if ( ! $is_site_connected ) {
return new \WP_Error(
return new WP_Error(
'not_connected',
__( 'Your site is not connected to Jetpack.', 'jetpack-my-jetpack' ),
array(
@ -68,7 +69,7 @@ class REST_Purchases {
$body = json_decode( wp_remote_retrieve_body( $response ) );
if ( is_wp_error( $response ) || empty( $response['body'] ) || 200 !== $response_code ) {
return new \WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ? $response_code : 400 ) );
return new WP_Error( 'site_data_fetch_failed', 'Site data fetch failed', array( 'status' => $response_code ? $response_code : 400 ) );
}
return rest_ensure_response( $body, 200 );

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