updated plugin Jetpack Protect
version 2.0.0
This commit is contained in:
@ -5,6 +5,90 @@ 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.4.2] - 2024-01-18
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [2.4.1] - 2024-01-15
|
||||
### Added
|
||||
- Options: synchronize block status option. [#34989]
|
||||
|
||||
### Changed
|
||||
- Sync: Dedicated sync now disabled for high queue lags only if test request fails. [#34888]
|
||||
|
||||
### Fixed
|
||||
- Added `is_array` check to `get_items_to_send` to make sure no fatals are thrown on non-array values. [#31552]
|
||||
- Jetpack Sync: Fixed buffer sanitization in Sync close endpoint [#34961]
|
||||
- Jetpack Sync: Fix restoring post global before enqueuing a post action. [#34990]
|
||||
|
||||
## [2.4.0] - 2024-01-04
|
||||
### Removed
|
||||
- Social: Removed sync option for tweetstorm. [#34330]
|
||||
|
||||
## [2.3.0] - 2023-12-20
|
||||
### Added
|
||||
- Add wpcom_ai_site_prompt option to the site settings endpoint. [#34709]
|
||||
|
||||
### Fixed
|
||||
- Added preemptive check to break expanding metadata for posts loop in Full Sync. [#34661]
|
||||
|
||||
## [2.2.1] - 2023-12-13
|
||||
### Changed
|
||||
- Refactored loop to improve efficiency and code readability [#34565]
|
||||
|
||||
## [2.2.0] - 2023-12-11
|
||||
### Added
|
||||
- Social: Add auto-conversion option to sync to WPCOM. [#34113]
|
||||
|
||||
### Fixed
|
||||
- Fixed a missing sanity check in Sync Posts handler logic that created failed builds. [#34548]
|
||||
- Sync: Update Full Sync to limit max amount of data sent in one request. [#34390]
|
||||
|
||||
## [2.1.2] - 2023-12-06
|
||||
### Changed
|
||||
- Update dependencies.
|
||||
|
||||
## [2.1.1] - 2023-12-03
|
||||
### Changed
|
||||
- Internal updates.
|
||||
|
||||
## [2.1.0] - 2023-11-24
|
||||
### Added
|
||||
- Added jetpack_verbum_subscription_modal setting to manage subscription modal show/hide on Verbum. [#34258]
|
||||
|
||||
### Fixed
|
||||
- Silenced the call to `gzinflate` to avoid a few PHP warnings. [#34186]
|
||||
|
||||
## [2.0.2] - 2023-11-21
|
||||
### Changed
|
||||
- Replaced usage of strpos() with str_contains(). [#34137]
|
||||
- Replaced usage of substr() with str_starts_with() and str_ends_with(). [#34207]
|
||||
|
||||
## [2.0.1] - 2023-11-21
|
||||
|
||||
## [2.0.0] - 2023-11-20
|
||||
### Changed
|
||||
- Replaced usage of strpos() with str_starts_with(). [#34135]
|
||||
- Updated required PHP version to >= 7.0. [#34192]
|
||||
|
||||
## [1.60.1] - 2023-10-31
|
||||
|
||||
## [1.60.0] - 2023-10-26
|
||||
### Removed
|
||||
- Remove Jetpack option jetpack-memberships-connected-account-id. [#32354]
|
||||
|
||||
## [1.59.2] - 2023-10-24
|
||||
### Changed
|
||||
- Update sync version.
|
||||
|
||||
## [1.59.1] - 2023-10-24
|
||||
### Added
|
||||
- Sync: Add missing support for supplying additional columns to do checksum on. [#33440]
|
||||
|
||||
## [1.59.0] - 2023-10-23
|
||||
### Changed
|
||||
- Dedicated Sync: Update 'init' hook priority on Dedicated Sync requests to 0, in order to start sending Sync actions to WPCOM and exit as early as possible. [#33594]
|
||||
|
||||
## [1.58.1] - 2023-10-18
|
||||
### Fixed
|
||||
- Update dependencies.
|
||||
@ -946,6 +1030,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Packages: Move sync to a classmapped package
|
||||
|
||||
[2.4.2]: https://github.com/Automattic/jetpack-sync/compare/v2.4.1...v2.4.2
|
||||
[2.4.1]: https://github.com/Automattic/jetpack-sync/compare/v2.4.0...v2.4.1
|
||||
[2.4.0]: https://github.com/Automattic/jetpack-sync/compare/v2.3.0...v2.4.0
|
||||
[2.3.0]: https://github.com/Automattic/jetpack-sync/compare/v2.2.1...v2.3.0
|
||||
[2.2.1]: https://github.com/Automattic/jetpack-sync/compare/v2.2.0...v2.2.1
|
||||
[2.2.0]: https://github.com/Automattic/jetpack-sync/compare/v2.1.2...v2.2.0
|
||||
[2.1.2]: https://github.com/Automattic/jetpack-sync/compare/v2.1.1...v2.1.2
|
||||
[2.1.1]: https://github.com/Automattic/jetpack-sync/compare/v2.1.0...v2.1.1
|
||||
[2.1.0]: https://github.com/Automattic/jetpack-sync/compare/v2.0.2...v2.1.0
|
||||
[2.0.2]: https://github.com/Automattic/jetpack-sync/compare/v2.0.1...v2.0.2
|
||||
[2.0.1]: https://github.com/Automattic/jetpack-sync/compare/v2.0.0...v2.0.1
|
||||
[2.0.0]: https://github.com/Automattic/jetpack-sync/compare/v1.60.1...v2.0.0
|
||||
[1.60.1]: https://github.com/Automattic/jetpack-sync/compare/v1.60.0...v1.60.1
|
||||
[1.60.0]: https://github.com/Automattic/jetpack-sync/compare/v1.59.2...v1.60.0
|
||||
[1.59.2]: https://github.com/Automattic/jetpack-sync/compare/v1.59.1...v1.59.2
|
||||
[1.59.1]: https://github.com/Automattic/jetpack-sync/compare/v1.59.0...v1.59.1
|
||||
[1.59.0]: https://github.com/Automattic/jetpack-sync/compare/v1.58.1...v1.59.0
|
||||
[1.58.1]: https://github.com/Automattic/jetpack-sync/compare/v1.58.0...v1.58.1
|
||||
[1.58.0]: https://github.com/Automattic/jetpack-sync/compare/v1.57.4...v1.58.0
|
||||
[1.57.4]: https://github.com/Automattic/jetpack-sync/compare/v1.57.3...v1.57.4
|
||||
|
@ -4,16 +4,17 @@
|
||||
"type": "jetpack-library",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"automattic/jetpack-connection": "^1.58.1",
|
||||
"automattic/jetpack-constants": "^1.6.23",
|
||||
"automattic/jetpack-identity-crisis": "^0.11.0",
|
||||
"automattic/jetpack-password-checker": "^0.2.14",
|
||||
"automattic/jetpack-ip": "^0.1.6",
|
||||
"automattic/jetpack-roles": "^1.4.25",
|
||||
"automattic/jetpack-status": "^1.18.5"
|
||||
"php": ">=7.0",
|
||||
"automattic/jetpack-connection": "^2.2.0",
|
||||
"automattic/jetpack-constants": "^2.0.0",
|
||||
"automattic/jetpack-identity-crisis": "^0.15.0",
|
||||
"automattic/jetpack-password-checker": "^0.3.0",
|
||||
"automattic/jetpack-ip": "^0.2.1",
|
||||
"automattic/jetpack-roles": "^2.0.0",
|
||||
"automattic/jetpack-status": "^2.0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "^3.3.11",
|
||||
"automattic/jetpack-changelogger": "^4.0.5",
|
||||
"yoast/phpunit-polyfills": "1.1.0",
|
||||
"automattic/wordbless": "@dev"
|
||||
},
|
||||
@ -48,7 +49,7 @@
|
||||
"link-template": "https://github.com/Automattic/jetpack-sync/compare/v${old}...v${new}"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-trunk": "1.58.x-dev"
|
||||
"dev-trunk": "2.4.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
|
@ -110,7 +110,7 @@ class Actions {
|
||||
// rely on 'jetpack_sync_before_send_queue_sync' are picked up and added to the queue if needed.
|
||||
if ( Settings::is_dedicated_sync_enabled() && Dedicated_Sender::is_dedicated_sync_request() ) {
|
||||
self::initialize_listener();
|
||||
add_action( 'init', array( __CLASS__, 'add_dedicated_sync_sender_init' ), 90 );
|
||||
add_action( 'init', array( __CLASS__, 'add_dedicated_sync_sender_init' ), 200 );
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1059,7 +1059,7 @@ class Actions {
|
||||
);
|
||||
|
||||
// Verify $sync_module is not false.
|
||||
if ( ( $sync_module ) && false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
|
||||
if ( ( $sync_module ) && ! str_contains( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
|
||||
$result['full_queue_size'] = $full_queue->size();
|
||||
$result['full_queue_lag'] = $full_queue->lag();
|
||||
}
|
||||
|
@ -151,35 +151,6 @@ class Dedicated_Sender {
|
||||
|
||||
$queue_lag = $queue->lag();
|
||||
|
||||
// Only check if we're failing to send events if the queue lag is longer than the threshold.
|
||||
if ( $queue_lag > $queue_send_time_threshold ) {
|
||||
/**
|
||||
* Check if Dedicated Sync is healthy and revert to Default Sync if such case is detected.
|
||||
*/
|
||||
$last_successful_queue_send_time = get_option( Actions::LAST_SUCCESS_PREFIX . $queue->id, null );
|
||||
|
||||
if ( $last_successful_queue_send_time === null ) {
|
||||
/**
|
||||
* No successful sync sending completed. This might be either a "new" sync site or a site that's totally stuck.
|
||||
*/
|
||||
self::on_dedicated_sync_lag_not_sending_threshold_reached();
|
||||
|
||||
return new WP_Error( 'dedicated_sync_not_sending', 'Dedicated Sync is not successfully sending events' );
|
||||
} else {
|
||||
/**
|
||||
* We have recorded a successful sending of events. Let's see if that is not too long ago in the past.
|
||||
*/
|
||||
$time_since_last_succesful_send = time() - $last_successful_queue_send_time;
|
||||
|
||||
if ( $time_since_last_succesful_send > $queue_send_time_threshold ) {
|
||||
// We haven't successfully sent stuff in more than 30 minutes. Revert to Default Sync
|
||||
self::on_dedicated_sync_lag_not_sending_threshold_reached();
|
||||
|
||||
return new WP_Error( 'dedicated_sync_not_sending', 'Dedicated Sync is not successfully sending events' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to acquire a request lock, so we don't spawn multiple requests at the same time.
|
||||
* This should prevent cases where sites might have limits on the amount of simultaneous requests.
|
||||
@ -189,6 +160,21 @@ class Dedicated_Sender {
|
||||
return new WP_Error( 'dedicated_request_lock', 'Unable to acquire request lock' );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the queue lag is bigger than the threshold, we want to check if Dedicated Sync is working correctly.
|
||||
* We will do by sending a test request and disabling Dedicated Sync if it's not working. We will also exit early
|
||||
* in case we send the test request since it is a blocking request.
|
||||
*/
|
||||
if ( $queue_lag > $queue_send_time_threshold ) {
|
||||
if ( false === get_transient( self::DEDICATED_SYNC_CHECK_TRANSIENT ) ) {
|
||||
if ( ! self::can_spawn_dedicated_sync_request() ) {
|
||||
self::on_dedicated_sync_lag_not_sending_threshold_reached();
|
||||
return new WP_Error( 'dedicated_sync_not_sending', 'Dedicated Sync is not successfully sending events' );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$url = rest_url( 'jetpack/v4/sync/spawn-sync' );
|
||||
$url = add_query_arg( 'time', time(), $url ); // Enforce Cache busting.
|
||||
$url = add_query_arg( self::DEDICATED_SYNC_REQUEST_LOCK_QUERY_PARAM_NAME, $request_lock, $url );
|
||||
@ -361,7 +347,6 @@ class Dedicated_Sender {
|
||||
$sender->send_action( 'jetpack_sync_flow_error_enable', $data );
|
||||
}
|
||||
}
|
||||
|
||||
return self::DEDICATED_SYNC_VALIDATION_STRING === $dedicated_sync_response_body;
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,6 @@ class Defaults {
|
||||
'image_default_link_type',
|
||||
'infinite_scroll',
|
||||
'infinite_scroll_google_analytics',
|
||||
'jetpack-memberships-connected-account-id',
|
||||
'jetpack-memberships-has-connected-account',
|
||||
'jetpack-twitter-cards-site-tag',
|
||||
'jetpack_activated',
|
||||
@ -89,6 +88,7 @@ class Defaults {
|
||||
'jetpack_publicize_options',
|
||||
'jetpack_relatedposts',
|
||||
'jetpack_social_settings',
|
||||
'jetpack_social_autoconvert_images',
|
||||
'jetpack_sso_match_by_email',
|
||||
'jetpack_sso_require_two_step',
|
||||
'jetpack_sync_non_blocking', // is non-blocking Jetpack Sync flow enabled.
|
||||
@ -178,6 +178,7 @@ class Defaults {
|
||||
'wp_mobile_excerpt',
|
||||
'wp_mobile_featured_images',
|
||||
'wp_page_for_privacy_policy',
|
||||
'wpcom_ai_site_prompt',
|
||||
'wpcom_featured_image_in_email',
|
||||
'wpcom_gifting_subscription',
|
||||
'wpcom_is_fse_activated',
|
||||
@ -190,6 +191,8 @@ class Defaults {
|
||||
'wpcom_reader_views_enabled',
|
||||
'wpcom_site_setup',
|
||||
'wpcom_subscription_emails_use_excerpt',
|
||||
'jetpack_verbum_subscription_modal',
|
||||
'jetpack_blocks_disabled',
|
||||
);
|
||||
|
||||
/**
|
||||
@ -739,7 +742,6 @@ class Defaults {
|
||||
'_wp_page_template',
|
||||
'_wp_trash_meta_comments_status',
|
||||
'_wpas_feature_enabled',
|
||||
'_wpas_is_tweetstorm',
|
||||
'_wpas_mess',
|
||||
'_wpas_options',
|
||||
'advanced_seo_description', // Jetpack_SEO_Posts::DESCRIPTION_META_KEY.
|
||||
|
@ -41,7 +41,10 @@ class JSON_Deflate_Array_Codec implements Codec_Interface {
|
||||
* @return array|mixed|object
|
||||
*/
|
||||
public function decode( $input ) {
|
||||
return $this->json_unserialize( gzinflate( base64_decode( $input ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
|
||||
$decoded = base64_decode( $input ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
|
||||
$inflated = @gzinflate( $decoded ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
|
||||
return is_string( $inflated ) ? $this->json_unserialize( $inflated ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -216,6 +216,7 @@ class Listener {
|
||||
* If we add any items to the queue, we should try to ensure that our script
|
||||
* can't be killed before they are sent.
|
||||
*/
|
||||
// https://plugins.trac.wordpress.org/ticket/2041
|
||||
if ( function_exists( 'ignore_user_abort' ) ) {
|
||||
ignore_user_abort( true );
|
||||
}
|
||||
@ -319,6 +320,7 @@ class Listener {
|
||||
* If we add any items to the queue, we should try to ensure that our script
|
||||
* can't be killed before they are sent.
|
||||
*/
|
||||
// https://plugins.trac.wordpress.org/ticket/2041
|
||||
if ( function_exists( 'ignore_user_abort' ) ) {
|
||||
ignore_user_abort( true );
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace Automattic\Jetpack\Sync;
|
||||
*/
|
||||
class Package_Version {
|
||||
|
||||
const PACKAGE_VERSION = '1.58.1';
|
||||
const PACKAGE_VERSION = '2.4.2';
|
||||
|
||||
const PACKAGE_SLUG = 'sync';
|
||||
|
||||
|
@ -1306,27 +1306,22 @@ class Replicastore implements Replicastore_Interface {
|
||||
* @param bool $perform_text_conversion If text fields should be converted to latin1 during the checksum calculation.
|
||||
*
|
||||
* @return array|WP_Error The checksum histogram.
|
||||
* @throws Exception Throws an exception if data validation fails inside `Table_Checksum` calls.
|
||||
*/
|
||||
public function checksum_histogram( $table, $buckets = null, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '', $only_range_edges = false, $detailed_drilldown = false, $perform_text_conversion = false ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->queries = array();
|
||||
try {
|
||||
$checksum_table = $this->get_table_checksum_instance( $table, $salt, $perform_text_conversion );
|
||||
$checksum_table = $this->get_table_checksum_instance( $table, $salt, $perform_text_conversion, $columns );
|
||||
} catch ( Exception $ex ) {
|
||||
return new WP_Error( 'checksum_disabled', $ex->getMessage() );
|
||||
}
|
||||
|
||||
// Validate / Determine Buckets.
|
||||
if ( $buckets === null || $buckets < 1 ) {
|
||||
$buckets = $this->calculate_buckets( $table, $start_id, $end_id );
|
||||
try {
|
||||
$range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
|
||||
} catch ( Exception $ex ) {
|
||||
return new WP_Error( 'invalid_range_edges', '[' . $start_id . '-' . $end_id . ']: ' . $ex->getMessage() );
|
||||
}
|
||||
if ( is_wp_error( $buckets ) ) {
|
||||
return $buckets;
|
||||
}
|
||||
|
||||
$range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
|
||||
|
||||
if ( $only_range_edges ) {
|
||||
return $range_edges;
|
||||
@ -1338,12 +1333,21 @@ class Replicastore implements Replicastore_Interface {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Validate / Determine Buckets.
|
||||
if ( $buckets === null || $buckets < 1 ) {
|
||||
$buckets = $this->calculate_buckets( $table, $object_count );
|
||||
}
|
||||
|
||||
$bucket_size = (int) ceil( $object_count / $buckets );
|
||||
$previous_max_id = max( 0, $range_edges['min_range'] );
|
||||
$histogram = array();
|
||||
|
||||
do {
|
||||
$ids_range = $checksum_table->get_range_edges( $previous_max_id, null, $bucket_size );
|
||||
try {
|
||||
$ids_range = $checksum_table->get_range_edges( $previous_max_id, null, $bucket_size );
|
||||
} catch ( Exception $ex ) {
|
||||
return new WP_Error( 'invalid_range_edges', '[' . $previous_max_id . '- ]: ' . $ex->getMessage() );
|
||||
}
|
||||
|
||||
if ( empty( $ids_range['min_range'] ) || empty( $ids_range['max_range'] ) ) {
|
||||
// Nothing to checksum here...
|
||||
@ -1401,20 +1405,10 @@ class Replicastore implements Replicastore_Interface {
|
||||
* Determine number of buckets to use in full table checksum.
|
||||
*
|
||||
* @param string $table Object Type.
|
||||
* @param int $start_id Min Object ID.
|
||||
* @param int $end_id Max Object ID.
|
||||
* @return int|WP_Error Number of Buckets to use.
|
||||
* @param int $object_count Object count.
|
||||
* @return int Number of Buckets to use.
|
||||
*/
|
||||
private function calculate_buckets( $table, $start_id = null, $end_id = null ) {
|
||||
// Get # of objects.
|
||||
try {
|
||||
$checksum_table = $this->get_table_checksum_instance( $table );
|
||||
} catch ( Exception $ex ) {
|
||||
return new WP_Error( 'checksum_disabled', $ex->getMessage() );
|
||||
}
|
||||
$range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
|
||||
$object_count = $range_edges['item_count'];
|
||||
|
||||
private function calculate_buckets( $table, $object_count ) {
|
||||
// Ensure no division by 0.
|
||||
if ( 0 === (int) $object_count ) {
|
||||
return 1;
|
||||
@ -1437,21 +1431,22 @@ class Replicastore implements Replicastore_Interface {
|
||||
*
|
||||
* Some tables require custom instances, due to different checksum logic.
|
||||
*
|
||||
* @param string $table The table that we want to get the instance for.
|
||||
* @param null $salt Salt to be used when generating the checksums.
|
||||
* @param false $perform_text_conversion Should we perform text encoding conversion when calculating the checksum.
|
||||
* @param string $table The table that we want to get the instance for.
|
||||
* @param string $salt Salt to be used when generating the checksums.
|
||||
* @param bool $perform_text_conversion Should we perform text encoding conversion when calculating the checksum.
|
||||
* @param array $additional_columns Additional columns to add to the checksum calculation.
|
||||
*
|
||||
* @return Table_Checksum|Table_Checksum_Usermeta
|
||||
* @throws Exception Might throw an exception if any of the input parameters were invalid.
|
||||
*/
|
||||
public function get_table_checksum_instance( $table, $salt = null, $perform_text_conversion = false ) {
|
||||
public function get_table_checksum_instance( $table, $salt = null, $perform_text_conversion = false, $additional_columns = null ) {
|
||||
if ( 'users' === $table ) {
|
||||
return new Table_Checksum_Users( $table, $salt, $perform_text_conversion );
|
||||
return new Table_Checksum_Users( $table, $salt, $perform_text_conversion, $additional_columns );
|
||||
}
|
||||
if ( 'usermeta' === $table ) {
|
||||
return new Table_Checksum_Usermeta( $table, $salt, $perform_text_conversion );
|
||||
return new Table_Checksum_Usermeta( $table, $salt, $perform_text_conversion, $additional_columns );
|
||||
}
|
||||
|
||||
return new Table_Checksum( $table, $salt, $perform_text_conversion );
|
||||
return new Table_Checksum( $table, $salt, $perform_text_conversion, $additional_columns );
|
||||
}
|
||||
}
|
||||
|
@ -679,7 +679,7 @@ class REST_Endpoints {
|
||||
}
|
||||
|
||||
// Limit to A-Z,a-z,0-9,_,- .
|
||||
$request_body['buffer_id'] = preg_replace( '/[^A-Za-z0-9]/', '', $request_body['buffer_id'] );
|
||||
$request_body['buffer_id'] = preg_replace( '/[^A-Za-z0-9\-_\.]/', '', $request_body['buffer_id'] );
|
||||
$request_body['item_ids'] = array_filter( array_map( array( 'Automattic\Jetpack\Sync\REST_Endpoints', 'sanitize_item_ids' ), $request_body['item_ids'] ) );
|
||||
|
||||
$queue = new Queue( $queue_name );
|
||||
@ -862,7 +862,7 @@ class REST_Endpoints {
|
||||
*/
|
||||
protected static function sanitize_item_ids( $item ) {
|
||||
// lets not delete any options that don't start with jpsq_sync- .
|
||||
if ( ! is_string( $item ) || substr( $item, 0, 5 ) !== 'jpsq_' ) {
|
||||
if ( ! is_string( $item ) || ! str_starts_with( $item, 'jpsq_' ) ) {
|
||||
return null;
|
||||
}
|
||||
// Limit to A-Z,a-z,0-9,_,-,. .
|
||||
|
@ -295,7 +295,7 @@ class Sender {
|
||||
|
||||
$this->continue_full_sync_enqueue();
|
||||
// immediate full sync sends data in continue_full_sync_enqueue.
|
||||
if ( false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
|
||||
if ( ! str_contains( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
|
||||
return $this->do_sync_and_set_delays( $this->full_sync_queue );
|
||||
} else {
|
||||
$status = $sync_module->get_status();
|
||||
@ -509,6 +509,11 @@ class Sender {
|
||||
* This is expensive, but the only way to really know :/
|
||||
*/
|
||||
foreach ( $items as $key => $item ) {
|
||||
if ( ! is_array( $item ) ) {
|
||||
$skipped_items_ids[] = $key;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Suspending cache addition help prevent overloading in memory cache of large sites.
|
||||
wp_suspend_cache_addition( true );
|
||||
/**
|
||||
@ -574,6 +579,7 @@ class Sender {
|
||||
* Now that we're sure we are about to sync, try to ignore user abort
|
||||
* so we can avoid getting into a bad state.
|
||||
*/
|
||||
// https://plugins.trac.wordpress.org/ticket/2041
|
||||
if ( function_exists( 'ignore_user_abort' ) ) {
|
||||
ignore_user_abort( true );
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ class Settings {
|
||||
* @return boolean Whether the setting is a network setting.
|
||||
*/
|
||||
public static function is_network_setting( $setting ) {
|
||||
return strpos( $setting, 'network_' ) === 0;
|
||||
return str_starts_with( $setting, 'network_' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -348,7 +348,6 @@ class Callables extends Module {
|
||||
public function set_plugin_action_links() {
|
||||
if (
|
||||
! class_exists( '\DOMDocument' ) ||
|
||||
! function_exists( 'libxml_use_internal_errors' ) ||
|
||||
! function_exists( 'mb_convert_encoding' )
|
||||
) {
|
||||
return;
|
||||
|
@ -308,6 +308,28 @@ SQL
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
|
||||
/**
|
||||
* Return last_item to send for Module Full Sync Configuration.
|
||||
*
|
||||
* @param array $config This module Full Sync configuration.
|
||||
*
|
||||
* @return array|object|null
|
||||
*/
|
||||
public function get_last_item( $config ) {
|
||||
global $wpdb;
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
|
||||
return $wpdb->get_var(
|
||||
<<<SQL
|
||||
SELECT {$this->id_field()}
|
||||
FROM {$wpdb->{$this->table_name()}}
|
||||
WHERE {$this->get_where_sql( $config )}
|
||||
ORDER BY {$this->id_field()}
|
||||
LIMIT 1
|
||||
SQL
|
||||
);
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial last sent object.
|
||||
*
|
||||
@ -338,27 +360,51 @@ SQL
|
||||
$limits = Settings::get_setting( 'full_sync_limits' )[ $this->name() ];
|
||||
|
||||
$chunks_sent = 0;
|
||||
// phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
|
||||
while ( $objects = $this->get_next_chunk( $config, $status, $limits['chunk_size'] ) ) {
|
||||
if ( $chunks_sent++ === $limits['max_chunks'] || microtime( true ) >= $send_until ) {
|
||||
|
||||
$last_item = $this->get_last_item( $config );
|
||||
|
||||
while ( $chunks_sent < $limits['max_chunks'] && microtime( true ) < $send_until ) {
|
||||
$objects = $this->get_next_chunk( $config, $status, $limits['chunk_size'] );
|
||||
|
||||
if ( $wpdb->last_error ) {
|
||||
$status['error'] = true;
|
||||
return $status;
|
||||
}
|
||||
|
||||
if ( empty( $objects ) ) {
|
||||
$status['finished'] = true;
|
||||
return $status;
|
||||
}
|
||||
$result = $this->send_action( 'jetpack_full_sync_' . $this->name(), array( $objects, $status['last_sent'] ) );
|
||||
|
||||
if ( is_wp_error( $result ) || $wpdb->last_error ) {
|
||||
$status['error'] = true;
|
||||
return $status;
|
||||
}
|
||||
// The $ids are ordered in descending order.
|
||||
$status['last_sent'] = end( $objects );
|
||||
$status['sent'] += count( $objects );
|
||||
// Updated the sent and last_sent status.
|
||||
$status = $this->set_send_full_sync_actions_status( $status, $objects );
|
||||
if ( $last_item === $status['last_sent'] ) {
|
||||
$status['finished'] = true;
|
||||
return $status;
|
||||
}
|
||||
++$chunks_sent;
|
||||
}
|
||||
|
||||
if ( ! $wpdb->last_error ) {
|
||||
$status['finished'] = true;
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the full sync action based on the objects that were sent.
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param array $status This module Full Sync status.
|
||||
* @param array $objects This module Full Sync objects.
|
||||
*
|
||||
* @return array The updated status.
|
||||
*/
|
||||
protected function set_send_full_sync_actions_status( $status, $objects ) {
|
||||
$status['last_sent'] = end( $objects );
|
||||
$status['sent'] += count( $objects );
|
||||
return $status;
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ class Options extends Module {
|
||||
$options = array();
|
||||
$random_string = wp_generate_password();
|
||||
foreach ( $this->options_whitelist as $option ) {
|
||||
if ( 0 === strpos( $option, Settings::SETTINGS_OPTION_PREFIX ) ) {
|
||||
if ( str_starts_with( $option, Settings::SETTINGS_OPTION_PREFIX ) ) {
|
||||
$option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $option ) );
|
||||
$options[ $option ] = $option_value;
|
||||
} else {
|
||||
@ -308,7 +308,7 @@ class Options extends Module {
|
||||
}
|
||||
|
||||
// Filter our weird array( false ) value for theme_mods_*.
|
||||
if ( 'theme_mods_' === substr( $args[0], 0, 11 ) ) {
|
||||
if ( str_starts_with( $args[0], 'theme_mods_' ) ) {
|
||||
$this->filter_theme_mods( $args[1] );
|
||||
if ( isset( $args[2] ) ) {
|
||||
$this->filter_theme_mods( $args[2] );
|
||||
@ -335,7 +335,7 @@ class Options extends Module {
|
||||
* @return boolean Whether the option is whitelisted.
|
||||
*/
|
||||
public function is_whitelisted_option( $option ) {
|
||||
return in_array( $option, $this->options_whitelist, true ) || 'theme_mods_' === substr( $option, 0, 11 );
|
||||
return in_array( $option, $this->options_whitelist, true ) || str_starts_with( $option, 'theme_mods_' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -461,7 +461,7 @@ class Options extends Module {
|
||||
$random_string = wp_generate_password();
|
||||
// Only whitelisted options can be returned.
|
||||
if ( in_array( $id, $this->options_whitelist, true ) ) {
|
||||
if ( 0 === strpos( $id, Settings::SETTINGS_OPTION_PREFIX ) ) {
|
||||
if ( str_starts_with( $id, Settings::SETTINGS_OPTION_PREFIX ) ) {
|
||||
$option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $id ) );
|
||||
return $option_value;
|
||||
} else {
|
||||
|
@ -9,6 +9,7 @@ namespace Automattic\Jetpack\Sync\Modules;
|
||||
|
||||
use Automattic\Jetpack\Constants as Jetpack_Constants;
|
||||
use Automattic\Jetpack\Roles;
|
||||
use Automattic\Jetpack\Sync\Modules;
|
||||
use Automattic\Jetpack\Sync\Settings;
|
||||
|
||||
/**
|
||||
@ -73,6 +74,16 @@ class Posts extends Module {
|
||||
*/
|
||||
const MAX_POST_META_LENGTH = 2000000;
|
||||
|
||||
/**
|
||||
* Max bytes allowed for full sync upload.
|
||||
* Current Setting : 7MB.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_SIZE_FULL_SYNC = 7000000;
|
||||
|
||||
/**
|
||||
* Default previous post state.
|
||||
* Used for default previous post status.
|
||||
@ -220,7 +231,12 @@ class Posts extends Module {
|
||||
add_filter( 'jetpack_sync_before_send_updated_post_meta', array( $this, 'trim_post_meta' ) );
|
||||
add_filter( 'jetpack_sync_before_send_deleted_post_meta', array( $this, 'trim_post_meta' ) );
|
||||
// Full sync.
|
||||
add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_post_ids' ) );
|
||||
$sync_module = Modules::get_module( 'full-sync' );
|
||||
if ( $sync_module && str_contains( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
|
||||
add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'add_term_relationships' ) );
|
||||
} else {
|
||||
add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_posts_with_metadata_and_terms' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -374,7 +390,7 @@ class Posts extends Module {
|
||||
*/
|
||||
public function is_whitelisted_post_meta( $meta_key ) {
|
||||
// The _wpas_skip_ meta key is used by Publicize.
|
||||
return in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true ) || ( 0 === strpos( $meta_key, '_wpas_skip_' ) );
|
||||
return in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true ) || str_starts_with( $meta_key, '_wpas_skip_' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -428,6 +444,10 @@ class Posts extends Module {
|
||||
*/
|
||||
public function filter_post_content_and_add_links( $post_object ) {
|
||||
global $post;
|
||||
|
||||
// Used to restore the post global.
|
||||
$current_post = $post;
|
||||
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$post = $post_object;
|
||||
|
||||
@ -440,6 +460,9 @@ class Posts extends Module {
|
||||
$non_existant_post->post_modified_gmt = $post->post_modified_gmt;
|
||||
$non_existant_post->post_status = 'jetpack_sync_non_registered_post_type';
|
||||
$non_existant_post->post_type = $post->post_type;
|
||||
// Restore global post.
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$post = $current_post;
|
||||
|
||||
return $non_existant_post;
|
||||
}
|
||||
@ -467,6 +490,10 @@ class Posts extends Module {
|
||||
$blocked_post->post_status = 'jetpack_sync_blocked';
|
||||
$blocked_post->post_type = $post->post_type;
|
||||
|
||||
// Restore global post.
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$post = $current_post;
|
||||
|
||||
return $blocked_post;
|
||||
}
|
||||
|
||||
@ -546,7 +573,13 @@ class Posts extends Module {
|
||||
$post->amp_permalink = amp_get_permalink( $post->ID );
|
||||
}
|
||||
|
||||
return $post;
|
||||
$filtered_post = $post;
|
||||
|
||||
// Restore global post.
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$post = $current_post;
|
||||
|
||||
return $filtered_post;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -745,24 +778,46 @@ class Posts extends Module {
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand post IDs to post objects within a hook before they are serialized and sent to the server.
|
||||
* Add term relationships to post objects within a hook before they are serialized and sent to the server.
|
||||
* This is used in Full Sync Immediately
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param array $args The hook parameters.
|
||||
* @return array $args The expanded hook parameters.
|
||||
*/
|
||||
public function expand_post_ids( $args ) {
|
||||
list( $post_ids, $previous_interval_end) = $args;
|
||||
public function add_term_relationships( $args ) {
|
||||
list( $filtered_posts, $previous_interval_end ) = $args;
|
||||
list( $filtered_post_ids, $filtered_posts, $filtered_posts_metadata ) = $filtered_posts;
|
||||
|
||||
$posts = array_filter( array_map( array( 'WP_Post', 'get_instance' ), $post_ids ) );
|
||||
$posts = array_map( array( $this, 'filter_post_content_and_add_links' ), $posts );
|
||||
$posts = array_values( $posts ); // Reindex in case posts were deleted.
|
||||
return array(
|
||||
$filtered_posts,
|
||||
$filtered_posts_metadata,
|
||||
$this->get_term_relationships( $filtered_post_ids ),
|
||||
$previous_interval_end,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand post IDs to post objects within a hook before they are serialized and sent to the server.
|
||||
* This is used in Legacy Full Sync
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param array $args The hook parameters.
|
||||
* @return array $args The expanded hook parameters.
|
||||
*/
|
||||
public function expand_posts_with_metadata_and_terms( $args ) {
|
||||
list( $post_ids, $previous_interval_end ) = $args;
|
||||
|
||||
$posts = $this->expand_posts( $post_ids );
|
||||
$posts_metadata = $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) );
|
||||
$term_relationships = $this->get_term_relationships( $post_ids );
|
||||
|
||||
return array(
|
||||
$posts,
|
||||
$this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) ),
|
||||
$this->get_term_relationships( $post_ids ),
|
||||
$posts_metadata,
|
||||
$term_relationships,
|
||||
$previous_interval_end,
|
||||
);
|
||||
}
|
||||
@ -780,4 +835,115 @@ class Posts extends Module {
|
||||
public function get_min_max_object_ids_for_batches( $batch_size, $where_sql = false ) {
|
||||
return parent::get_min_max_object_ids_for_batches( $batch_size, $this->get_where_sql( $where_sql ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the Module Configuration and Status return the next chunk of items to send.
|
||||
* This function also expands the posts and metadata and filters them based on the maximum size constraints.
|
||||
*
|
||||
* @param array $config This module Full Sync configuration.
|
||||
* @param array $status This module Full Sync status.
|
||||
* @param int $chunk_size Chunk size.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_next_chunk( $config, $status, $chunk_size ) {
|
||||
|
||||
$post_ids = parent::get_next_chunk( $config, $status, $chunk_size );
|
||||
|
||||
if ( empty( $post_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$posts = $this->expand_posts( $post_ids );
|
||||
$posts_metadata = $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) );
|
||||
|
||||
// Filter posts and metadata based on maximum size constraints.
|
||||
list( $filtered_post_ids, $filtered_posts, $filtered_posts_metadata ) = $this->filter_posts_and_metadata_max_size( $posts, $posts_metadata );
|
||||
return array(
|
||||
$filtered_post_ids,
|
||||
$filtered_posts,
|
||||
$filtered_posts_metadata,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand posts.
|
||||
*
|
||||
* @param array $post_ids Post IDs.
|
||||
*
|
||||
* @return array Expanded posts.
|
||||
*/
|
||||
private function expand_posts( $post_ids ) {
|
||||
$posts = array_filter( array_map( array( 'WP_Post', 'get_instance' ), $post_ids ) );
|
||||
$posts = array_map( array( $this, 'filter_post_content_and_add_links' ), $posts );
|
||||
$posts = array_values( $posts ); // Reindex in case posts were deleted.
|
||||
return $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters posts and metadata based on maximum size constraints.
|
||||
* It always allows the first post with its metadata even if they exceed the limit, otherwise they will never be synced.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param array $posts The array of posts to filter.
|
||||
* @param array $metadata The array of metadata to filter.
|
||||
* @return array An array containing the filtered post IDs, filtered posts, and filtered metadata.
|
||||
*/
|
||||
public function filter_posts_and_metadata_max_size( $posts, $metadata ) {
|
||||
$filtered_posts = array();
|
||||
$filtered_metadata = array();
|
||||
$filtered_post_ids = array();
|
||||
$current_size = 0;
|
||||
foreach ( $posts as $post ) {
|
||||
$post_content_size = isset( $post->post_content ) ? strlen( $post->post_content ) : 0;
|
||||
$current_metadata = array();
|
||||
$metadata_size = 0;
|
||||
foreach ( $metadata as $key => $metadata_item ) {
|
||||
if ( (int) $metadata_item->post_id === $post->ID ) {
|
||||
// Trimming metadata if it exceeds limit. Similar to trim_post_meta.
|
||||
$metadata_item_size = strlen( maybe_serialize( $metadata_item->meta_value ) );
|
||||
if ( $metadata_item_size >= self::MAX_POST_META_LENGTH ) {
|
||||
$metadata_item->meta_value = '';
|
||||
}
|
||||
$current_metadata[] = $metadata_item;
|
||||
$metadata_size += $metadata_item_size >= self::MAX_POST_META_LENGTH ? 0 : $metadata_item_size;
|
||||
if ( ! empty( $filtered_post_ids ) && ( $current_size + $post_content_size + $metadata_size ) > ( self::MAX_SIZE_FULL_SYNC ) ) {
|
||||
break 2; // Break both foreach loops.
|
||||
}
|
||||
unset( $metadata[ $key ] );
|
||||
}
|
||||
}
|
||||
// Always allow the first post with its metadata.
|
||||
if ( empty( $filtered_post_ids ) || ( $current_size + $post_content_size + $metadata_size ) <= ( self::MAX_SIZE_FULL_SYNC ) ) {
|
||||
$filtered_post_ids[] = strval( $post->ID );
|
||||
$filtered_posts[] = $post;
|
||||
$filtered_metadata = array_merge( $filtered_metadata, $current_metadata );
|
||||
$current_size += $post_content_size + $metadata_size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array(
|
||||
$filtered_post_ids,
|
||||
$filtered_posts,
|
||||
$filtered_metadata,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the full sync action based on the objects that were sent.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param array $status This module Full Sync status.
|
||||
* @param array $objects This module Full Sync objects.
|
||||
*
|
||||
* @return array The updated status.
|
||||
*/
|
||||
public function set_send_full_sync_actions_status( $status, $objects ) {
|
||||
$status['last_sent'] = end( $objects[0] );
|
||||
$status['sent'] += count( $objects[0] );
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
@ -178,6 +178,25 @@ class Term_Relationships extends Module {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return last_item to send for Module Full Sync Configuration.
|
||||
*
|
||||
* @param array $config This module Full Sync configuration.
|
||||
*
|
||||
* @return array|object|null
|
||||
*/
|
||||
public function get_last_item( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
global $wpdb;
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
|
||||
return $wpdb->get_results(
|
||||
"SELECT object_id, term_taxonomy_id
|
||||
FROM $wpdb->term_relationships
|
||||
ORDER BY object_id , term_taxonomy_id
|
||||
LIMIT 1",
|
||||
ARRAY_A
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Enqueue all $items within `jetpack_full_sync_term_relationships` actions.
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
namespace Automattic\Jetpack\Sync\Replicastore;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class to handle Table Checksums for the Users table.
|
||||
*/
|
||||
|
@ -132,13 +132,14 @@ class Table_Checksum {
|
||||
/**
|
||||
* Table_Checksum constructor.
|
||||
*
|
||||
* @param string $table The table to calculate checksums for.
|
||||
* @param string $salt Optional salt to add to the checksum.
|
||||
* @param string $table The table to calculate checksums for.
|
||||
* @param string $salt Optional salt to add to the checksum.
|
||||
* @param boolean $perform_text_conversion If text fields should be latin1 converted.
|
||||
* @param array $additional_columns Additional columns to add to the checksum calculation.
|
||||
*
|
||||
* @throws Exception Throws exception from inner functions.
|
||||
*/
|
||||
public function __construct( $table, $salt = null, $perform_text_conversion = false ) {
|
||||
public function __construct( $table, $salt = null, $perform_text_conversion = false, $additional_columns = null ) {
|
||||
|
||||
if ( ! Sync\Settings::is_checksum_enabled() ) {
|
||||
throw new Exception( 'Checksums are currently disabled.' );
|
||||
@ -163,6 +164,8 @@ class Table_Checksum {
|
||||
|
||||
$this->prepare_fields( $this->table_configuration );
|
||||
|
||||
$this->prepare_additional_columns( $additional_columns );
|
||||
|
||||
// Run any callbacks to check if a table is enabled or not.
|
||||
if (
|
||||
is_callable( $this->is_table_enabled_callback )
|
||||
@ -877,4 +880,48 @@ class Table_Checksum {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and append custom columns to the list of columns that we run the checksum on.
|
||||
*
|
||||
* @param string|array $additional_columns List of additional columns.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception When field validation fails.
|
||||
*/
|
||||
protected function prepare_additional_columns( $additional_columns ) {
|
||||
/**
|
||||
* No need to do anything if the parameter is not provided or empty.
|
||||
*/
|
||||
if ( empty( $additional_columns ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_array( $additional_columns ) ) {
|
||||
if ( ! is_string( $additional_columns ) ) {
|
||||
throw new Exception( 'Invalid value for additional fields' );
|
||||
}
|
||||
|
||||
$additional_columns = explode( ',', $additional_columns );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the fields. If any don't conform to the required norms, we will throw an exception and
|
||||
* halt code here.
|
||||
*/
|
||||
$this->validate_fields( $additional_columns );
|
||||
|
||||
/**
|
||||
* Assign the fields to the checksum_fields to be used in the checksum later.
|
||||
*
|
||||
* We're adding the fields to the rest of the `checksum_fields`, so we don't need
|
||||
* to implement extra logic just for the additional fields.
|
||||
*/
|
||||
$this->checksum_fields = array_unique(
|
||||
array_merge(
|
||||
$this->checksum_fields,
|
||||
$additional_columns
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user