diff options
Diffstat (limited to 'plugins/jetpack/jetpack_vendor/automattic/jetpack-sync')
23 files changed, 2937 insertions, 40 deletions
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md index aca0a702..531c86f0 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md @@ -5,6 +5,115 @@ 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). +## [1.35.0] - 2022-05-30 +### Changed +- Sync: Add '_jetpack_blogging_prompt_key' to default post meta whitelist + +## [1.34.0] - 2022-05-24 +### Changed +- Dedicated Sync - Introduce custom endpoint for spawning Sync requests [#24468] +- Sync: Add 'active_modules' to default whitelisted callables. [#24453] + +## [1.33.1] - 2022-05-19 +### Removed +- Removed dedicated sync custom endpoints pending error investigation [#24419] + +## [1.33.0] - 2022-05-18 +### Changed +- Dedicated Sync: Introduce custom endpoint for spawning Sync requests [#24344] + +## [1.32.0] - 2022-05-10 +### Added +- Search: add search options to option whitelist [#24167] + +## [1.31.1] - 2022-05-04 +### Changed +- Updated package dependencies. [#24095] +- WordPress 6.1 Compatibilty [#24083] + +### Deprecated +- Moved the options class into Connection. [#24095] + +## [1.31.0] - 2022-04-26 +### Added +- Adds filter to get_themes callable + +### Deprecated +- Removed Heartbeat by hoisting it into Connection. + +## [1.30.8] - 2022-04-19 +### Added +- Added get_themes Callable to sync the list of installed themes on a site +- Added get_themes to Sync defaults + +### Changed +- PHPCS: Fix `WordPress.Security.ValidatedSanitizedInput` +- Updated package dependencies. + +## [1.30.7] - 2022-04-12 +### Added +- Adding new site option to be synced. + +## [1.30.6] - 2022-04-06 +### Changed +- Updated package dependencies. + +### Fixed +- Dedicated Sync: Only try to run the sender once if Dedicated Sync is enabled as it has its own requeueing mechanism. + +## [1.30.5] - 2022-03-29 +### Changed +- Microperformance: Use === null instead of is_null + +## [1.30.4] - 2022-03-23 +### Changed +- Enable syncing of dedicated_sync_enabled Sync setting + +### Fixed +- Dedicated Sync: Allow spawning request with expired Retry-After + +## [1.30.3] - 2022-03-15 +### Changed +- Search Sync Settings :: Add ETB taxonomy to allow list. + +## [1.30.2] - 2022-03-08 +### Changed +- Disallow syncing of _term_meta post_type + +## [1.30.1] - 2022-03-02 +### Added +- Dedicated Sync flow: Allow enabling or disabling via WPCOM response header + +## [1.30.0] - 2022-02-22 +### Added +- Add Sync dedicated request flow. + +### Changed +- Updated package dependencies. + +## [1.29.2] - 2022-02-09 +### Added +- Allow sync package consumers to provide custom data settings. + +### Fixed +- Fixed some new PHPCS warnings. + +## [1.29.1] - 2022-02-02 +### Changed +- Updated package dependencies. + +## [1.29.0] - 2022-01-25 +### Added +- Jetpack Search: update the allowed post meta when search is active to include all indexable meta. + +## [1.28.2] - 2022-01-18 +### Changed +- Updated package dependencies. + +## [1.28.1] - 2022-01-13 +### Changed +- Updated package dependencies. + ## [1.28.0] - 2022-01-04 ### Changed - Listener: Do not enqueue actions when the site is disconnected @@ -550,6 +659,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Packages: Move sync to a classmapped package +[1.35.0]: https://github.com/Automattic/jetpack-sync/compare/v1.34.0...v1.35.0 +[1.34.0]: https://github.com/Automattic/jetpack-sync/compare/v1.33.1...v1.34.0 +[1.33.1]: https://github.com/Automattic/jetpack-sync/compare/v1.33.0...v1.33.1 +[1.33.0]: https://github.com/Automattic/jetpack-sync/compare/v1.32.0...v1.33.0 +[1.32.0]: https://github.com/Automattic/jetpack-sync/compare/v1.31.1...v1.32.0 +[1.31.1]: https://github.com/Automattic/jetpack-sync/compare/v1.31.0...v1.31.1 +[1.31.0]: https://github.com/Automattic/jetpack-sync/compare/v1.30.8...v1.31.0 +[1.30.8]: https://github.com/Automattic/jetpack-sync/compare/v1.30.7...v1.30.8 +[1.30.7]: https://github.com/Automattic/jetpack-sync/compare/v1.30.6...v1.30.7 +[1.30.6]: https://github.com/Automattic/jetpack-sync/compare/v1.30.5...v1.30.6 +[1.30.5]: https://github.com/Automattic/jetpack-sync/compare/v1.30.4...v1.30.5 +[1.30.4]: https://github.com/Automattic/jetpack-sync/compare/v1.30.3...v1.30.4 +[1.30.3]: https://github.com/Automattic/jetpack-sync/compare/v1.30.2...v1.30.3 +[1.30.2]: https://github.com/Automattic/jetpack-sync/compare/v1.30.1...v1.30.2 +[1.30.1]: https://github.com/Automattic/jetpack-sync/compare/v1.30.0...v1.30.1 +[1.30.0]: https://github.com/Automattic/jetpack-sync/compare/v1.29.2...v1.30.0 +[1.29.2]: https://github.com/Automattic/jetpack-sync/compare/v1.29.1...v1.29.2 +[1.29.1]: https://github.com/Automattic/jetpack-sync/compare/v1.29.0...v1.29.1 +[1.29.0]: https://github.com/Automattic/jetpack-sync/compare/v1.28.2...v1.29.0 +[1.28.2]: https://github.com/Automattic/jetpack-sync/compare/v1.28.1...v1.28.2 +[1.28.1]: https://github.com/Automattic/jetpack-sync/compare/v1.28.0...v1.28.1 [1.28.0]: https://github.com/Automattic/jetpack-sync/compare/v1.27.6...v1.28.0 [1.27.6]: https://github.com/Automattic/jetpack-sync/compare/v1.27.5...v1.27.6 [1.27.5]: https://github.com/Automattic/jetpack-sync/compare/v1.27.4...v1.27.5 diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php index e2f05c98..768ade58 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php @@ -89,6 +89,8 @@ class Actions { /** * Initialize Sync for cron jobs, set up listeners for WordPress Actions, * and set up a shut-down action for sending actions to WordPress.com + * If dedicated Sync is enabled and this is a dedicated Sync request + * up an init action for sending actions to WordPress.com instead. * * @access public * @static @@ -99,6 +101,15 @@ class Actions { return; } + // If dedicated Sync is enabled and this is a dedicated Sync request, no need to + // initialize Sync for cron jobs, set up listeners or set up a shut-down action + // for sending actions to WordPress.com. + // We only need to set up an init action for sending actions to WordPress.com and exit early. + if ( Settings::is_dedicated_sync_enabled() && Dedicated_Sender::is_dedicated_sync_request() ) { + add_action( 'init', array( __CLASS__, 'add_dedicated_sync_sender_init' ), 90 ); + return; + } + if ( self::sync_via_cron_allowed() ) { self::init_sync_cron_jobs(); } elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) { @@ -164,6 +175,22 @@ class Actions { } /** + * Immediately sends actions on init for the current dedicated Sync request. + * + * @access public + * @static + */ + public static function add_dedicated_sync_sender_init() { + if ( apply_filters( + 'jetpack_sync_sender_should_load', + true + ) ) { + self::initialize_sender(); + self::$sender->do_dedicated_sync_and_exit(); + } + } + + /** * Define JETPACK_SYNC_READ_ONLY constant if not defined. * This notifies sync to not run in shutdown if it was initialized during init. * @@ -194,6 +221,13 @@ class Actions { return self::sync_via_cron_allowed(); } + /** + * For now, if dedicated Sync is enabled we will always initialize send, even for GET and unauthenticated requests. + */ + if ( Settings::is_dedicated_sync_enabled() ) { + return true; + } + if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) { return true; } @@ -379,14 +413,16 @@ class Actions { public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) { $query_args = array( - 'sync' => '1', // Add an extra parameter to the URL so we can tell it's a sync action. - 'codec' => $codec_name, - 'timestamp' => $sent_timestamp, - 'queue' => $queue_id, - 'cd' => sprintf( '%.4f', $checkout_duration ), - 'pd' => sprintf( '%.4f', $preprocess_duration ), - 'queue_size' => $queue_size, - 'buffer_id' => $buffer_id, + 'sync' => '1', // Add an extra parameter to the URL so we can tell it's a sync action. + 'codec' => $codec_name, + 'timestamp' => $sent_timestamp, + 'queue' => $queue_id, + 'cd' => sprintf( '%.4f', $checkout_duration ), + 'pd' => sprintf( '%.4f', $preprocess_duration ), + 'queue_size' => $queue_size, + 'buffer_id' => $buffer_id, + // TODO this will be extended in the future. Might be good to extract in a separate method to support future entries too. + 'sync_flow_type' => Settings::is_dedicated_sync_enabled() ? 'dedicated' : 'default', ); $query_args['timeout'] = Settings::is_doing_cron() ? 30 : 20; @@ -437,6 +473,17 @@ class Actions { } } + // Enable/Disable Dedicated Sync flow via response headers. + $dedicated_sync_header = $rpc->get_response_header( 'Jetpack-Dedicated-Sync' ); + if ( false !== $dedicated_sync_header ) { + $dedicated_sync_enabled = 'on' === $dedicated_sync_header ? 1 : 0; + Settings::update_settings( + array( + 'dedicated_sync_enabled' => $dedicated_sync_enabled, + ) + ); + } + if ( ! $result ) { if ( false === $retry_after ) { // We received a non standard response from WP.com, lets backoff from sending requests for 1 minute. @@ -631,6 +678,14 @@ class Actions { break; } + /** + * Only try to sync once if Dedicated Sync is enabled. Dedicated Sync has its own requeueing mechanism + * that will re-run it if there are items in the queue at the end. + */ + if ( 'sync' === $type && $executions >= 1 && Settings::is_dedicated_sync_enabled() ) { + break; + } + $result = 'full_sync' === $type ? self::$sender->do_full_sync() : self::$sender->do_sync(); // # of send actions performed. @@ -676,6 +731,36 @@ class Actions { } /** + * Initializes sync for Instant Search. + * + * @access public + * @static + */ + public static function initialize_search() { + if ( false === class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) { + return; + } + $search_module = new \Automattic\Jetpack\Search\Module_Control(); + if ( $search_module->is_instant_search_enabled() ) { + add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) ); + } + } + + /** + * Add Search updates to Sync Filters. + * + * @access public + * @static + * + * @param array $sync_modules The list of sync modules declared prior to this filter. + * @return array A list of sync modules that now includes Search's modules. + */ + public static function add_search_sync_module( $sync_modules ) { + $sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\Search'; + return $sync_modules; + } + + /** * Adds Woo's sync modules to existing modules for sending. * * @access public diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-data-settings.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-data-settings.php new file mode 100644 index 00000000..fa2adae4 --- /dev/null +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-data-settings.php @@ -0,0 +1,355 @@ +<?php +/** + * The Data Settings class. + * + * @package automattic/jetpack-sync + */ + +namespace Automattic\Jetpack\Sync; + +/** + * The Data_Settings class + */ +class Data_Settings { + + /** + * The data that must be synced for every synced site. + */ + const MUST_SYNC_DATA_SETTINGS = array( + 'jetpack_sync_modules' => array( + 'Automattic\\Jetpack\\Sync\\Modules\\Callables', + 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync_Immediately', // enable Initial Sync on Site Connection. + ), + 'jetpack_sync_callable_whitelist' => array( + 'site_url' => array( 'Automattic\\Jetpack\\Connection\\Urls', 'site_url' ), + 'home_url' => array( 'Automattic\\Jetpack\\Connection\\Urls', 'home_url' ), + 'paused_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_paused_plugins' ), + 'paused_themes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_paused_themes' ), + ), + ); + + const MODULE_FILTER_MAPPING = array( + 'Automattic\\Jetpack\\Sync\\Modules\\Options' => array( + 'jetpack_sync_options_whitelist', + 'jetpack_sync_options_contentless', + ), + 'Automattic\\Jetpack\\Sync\\Modules\\Constants' => array( + 'jetpack_sync_constants_whitelist', + ), + 'Automattic\\Jetpack\\Sync\\Modules\\Callables' => array( + 'jetpack_sync_callable_whitelist', + 'jetpack_sync_multisite_callable_whitelist', + ), + 'Automattic\\Jetpack\\Sync\\Modules\\Posts' => array( + 'jetpack_sync_post_meta_whitelist', + ), + 'Automattic\\Jetpack\\Sync\\Modules\\Comments' => array( + 'jetpack_sync_comment_meta_whitelist', + ), + 'Automattic\\Jetpack\\Sync\\Modules\\Users' => array( + 'jetpack_sync_capabilities_whitelist', + ), + 'Automattic\\Jetpack\\Sync\\Modules\\Import' => array( + 'jetpack_sync_known_importers', + ), + ); + + const MODULES_FILTER_NAME = 'jetpack_sync_modules'; + + /** + * The static data settings array which contains the aggregated data settings for + * each sync filter. + * + * @var array + */ + private static $data_settings = array(); + + /** + * The static array which contains the list of filter hooks that have already been set up. + * + * @var array + */ + private static $set_filter_hooks = array(); + + /** + * Adds the data settings provided by a plugin to the Sync data settings. + * + * @param array $plugin_settings The array provided by the plugin. The array must use filters + * from the DATA_FILTER_DEFAULTS list as keys. + */ + public function add_settings_list( $plugin_settings = array() ) { + if ( empty( $plugin_settings[ self::MODULES_FILTER_NAME ] ) + || ! is_array( $plugin_settings[ self::MODULES_FILTER_NAME ] ) ) { + /* + * No modules have been set, so use defaults for everything and bail early. + */ + $this->set_all_defaults(); + return; + } + + $this->add_filters_custom_settings_and_hooks( $plugin_settings ); + + if ( ! did_action( 'jetpack_sync_add_required_data_settings' ) ) { + $this->add_required_settings(); + /** + * Fires when the required settings have been adding to the static + * data_settings array. + * + * @since 1.29.2 + * + * @module sync + */ + do_action( 'jetpack_sync_add_required_data_settings' ); + } + } + + /** + * Sets the default values for sync modules and all sync data filters. + */ + private function set_all_defaults() { + $this->add_sync_filter_setting( self::MODULES_FILTER_NAME, Modules::DEFAULT_SYNC_MODULES ); + + foreach ( array_keys( Default_Filter_Settings::DATA_FILTER_DEFAULTS ) as $filter ) { + $this->add_sync_filter_setting( $filter, $this->get_default_setting_for_filter( $filter ) ); + } + } + + /** + * Returns the default settings for the given filter. + * + * @param string $filter The filter name. + * + * @return array The filter's default settings array. + */ + private function get_default_setting_for_filter( $filter ) { + if ( self::MODULES_FILTER_NAME === $filter ) { + return Modules::DEFAULT_SYNC_MODULES; + } + + return ( new Default_Filter_Settings() )->get_default_settings( $filter ); + } + + /** + * Adds the custom settings and sets up the necessary filter hooks. + * + * @param array $filters_settings The custom settings. + */ + private function add_filters_custom_settings_and_hooks( $filters_settings ) { + if ( ! isset( $filters_settings[ self::MODULES_FILTER_NAME ] ) ) { + // This shouldn't happen. + return; + } + + $this->add_custom_filter_setting( self::MODULES_FILTER_NAME, $filters_settings[ self::MODULES_FILTER_NAME ] ); + + $enabled_modules = $filters_settings[ self::MODULES_FILTER_NAME ]; + $all_modules = Modules::DEFAULT_SYNC_MODULES; + + foreach ( $all_modules as $module ) { + if ( in_array( $module, $enabled_modules, true ) ) { + $this->add_filters_for_enabled_module( $module, $filters_settings ); + } else { + $this->add_filters_for_disabled_module( $module ); + } + } + } + + /** + * Adds the filters for the provided enabled module. If the settings provided custom filter settings + * for the module's filters, those are used. Otherwise, the filter's default settings are used. + * + * @param string $module The module name. + * @param array $filters_settings The settings for the filters. + */ + private function add_filters_for_enabled_module( $module, $filters_settings ) { + $module_mapping = self::MODULE_FILTER_MAPPING; + $filters_for_module = isset( $module_mapping[ $module ] ) ? $module_mapping[ $module ] : array(); + + foreach ( $filters_for_module as $filter ) { + if ( isset( $filters_settings[ $filter ] ) ) { + $this->add_custom_filter_setting( $filter, $filters_settings[ $filter ] ); + } else { + $this->add_sync_filter_setting( $filter, $this->get_default_setting_for_filter( $filter ) ); + } + } + } + + /** + * Adds the filters for the provided disabled module. The disabled module's associated filter settings are + * set to an empty array. + * + * @param string $module The module name. + */ + private function add_filters_for_disabled_module( $module ) { + $module_mapping = self::MODULE_FILTER_MAPPING; + $filters_for_module = isset( $module_mapping[ $module ] ) ? $module_mapping[ $module ] : array(); + + foreach ( $filters_for_module as $filter ) { + $this->add_custom_filter_setting( $filter, array() ); + } + } + + /** + * Adds the provided custom setting for a filter. If the filter setting isn't valid, the default + * value is used. + * + * If the filter's hook hasn't already been set up, it gets set up. + * + * @param string $filter The filter. + * @param array $setting The filter setting. + */ + private function add_custom_filter_setting( $filter, $setting ) { + if ( ! $this->is_valid_filter_setting( $filter, $setting ) ) { + /* + * The provided setting isn't valid, so use the default for this filter. + * We're using the default values so there's no need to set the filter hook. + */ + $this->add_sync_filter_setting( $filter, $this->get_default_setting_for_filter( $filter ) ); + return; + } + + if ( ! isset( static::$set_filter_hooks[ $filter ] ) ) { + // First time a custom modules setting is provided, so set the filter hook. + add_filter( $filter, array( $this, 'sync_data_filter_hook' ) ); + static::$set_filter_hooks[ $filter ] = 1; + } + + $this->add_sync_filter_setting( $filter, $setting ); + } + + /** + * Determines whether the filter setting is valid. The setting array is in the correct format (associative or indexed). + * + * @param string $filter The filter to check. + * @param array $filter_settings The filter settings. + * + * @return bool Whether the filter settings can be used. + */ + private function is_valid_filter_setting( $filter, $filter_settings ) { + if ( ! is_array( $filter_settings ) ) { + // The settings for each filter must be an array. + return false; + } + + if ( empty( $filter_settings ) ) { + // Empty settings are allowed. + return true; + } + + $indexed_array = isset( $filter_settings[0] ); + if ( in_array( $filter, Default_Filter_Settings::ASSOCIATIVE_FILTERS, true ) && ! $indexed_array ) { + return true; + } elseif ( ! in_array( $filter, Default_Filter_Settings::ASSOCIATIVE_FILTERS, true ) && $indexed_array ) { + return true; + } + + return false; + } + + /** + * Adds the data settings that are always required for every plugin that uses Sync. + */ + private function add_required_settings() { + foreach ( self::MUST_SYNC_DATA_SETTINGS as $filter => $setting ) { + $this->add_custom_filter_setting( $filter, $setting ); + } + } + + /** + * Adds the provided data setting for the provided filter. + * + * @param string $filter The filter name. + * @param array $value The data setting. + */ + private function add_sync_filter_setting( $filter, $value ) { + if ( ! isset( static::$data_settings[ $filter ] ) ) { + static::$data_settings[ $filter ] = $value; + return; + } + + if ( in_array( $filter, Default_Filter_Settings::ASSOCIATIVE_FILTERS, true ) ) { + $this->add_associative_filter_setting( $filter, $value ); + } else { + $this->add_indexed_filter_setting( $filter, $value ); + } + } + + /** + * Adds the provided data setting for the provided filter. This method handles + * adding settings to data that is stored as an associative array. + * + * @param string $filter The filter name. + * @param array $settings The data settings. + */ + private function add_associative_filter_setting( $filter, $settings ) { + foreach ( $settings as $key => $item ) { + if ( ! array_key_exists( $key, static::$data_settings[ $filter ] ) ) { + static::$data_settings[ $filter ][ $key ] = $item; + } + } + } + + /** + * Adds the provided data setting for the provided filter. This method handles + * adding settings to data that is stored as an indexed array. + * + * @param string $filter The filter name. + * @param array $settings The data settings. + */ + private function add_indexed_filter_setting( $filter, $settings ) { + static::$data_settings[ $filter ] = array_unique( + array_merge( + static::$data_settings[ $filter ], + $settings + ) + ); + } + + /** + * The callback function added to the sync data filters. Combines the list in the $data_settings property + * with any non-default values from the received array. + * + * @param array $filtered_values The data revieved from the filter. + * + * @return array The data settings for the filter. + */ + public function sync_data_filter_hook( $filtered_values ) { + if ( ! is_array( $filtered_values ) ) { + // Something is wrong with the input, so set it to an empty array. + $filtered_values = array(); + } + + $current_filter = current_filter(); + + if ( ! isset( static::$data_settings[ $current_filter ] ) ) { + return $filtered_values; + } + + if ( in_array( $current_filter, Default_Filter_Settings::ASSOCIATIVE_FILTERS, true ) ) { + $extra_filters = array_diff_key( $filtered_values, $this->get_default_setting_for_filter( $current_filter ) ); + $this->add_associative_filter_setting( $current_filter, $extra_filters ); + return static::$data_settings[ $current_filter ]; + } + + $extra_filters = array_diff( $filtered_values, $this->get_default_setting_for_filter( $current_filter ) ); + $this->add_indexed_filter_setting( $current_filter, $extra_filters ); + return static::$data_settings[ $current_filter ]; + } + + /** + * Sets the $data_settings property to an empty array. This is useful for testing. + */ + public function empty_data_settings_and_hooks() { + static::$data_settings = array(); + static::$set_filter_hooks = array(); + } + + /** + * Returns the $data_settings property. + * + * @return array The data_settings property. + */ + public function get_data_settings() { + return static::$data_settings; + } +} diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-dedicated-sender.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-dedicated-sender.php new file mode 100644 index 00000000..0d068766 --- /dev/null +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-dedicated-sender.php @@ -0,0 +1,204 @@ +<?php +/** + * Dedicated Sender. + * + * The class is responsible for spawning dedicated Sync requests. + * + * @package automattic/jetpack-sync + */ + +namespace Automattic\Jetpack\Sync; + +use WP_Error; +/** + * Class to manage Sync spawning. + * The purpose of this class is to provide the means to unblock Sync + * from running in the shutdown hook of regular requests by spawning a + * dedicated Sync request instead which will trigger Sync to run. + */ +class Dedicated_Sender { + + /** + * The transient name for storing the response code + * after spawning a dedicated sync test request. + */ + const DEDICATED_SYNC_CHECK_TRANSIENT = 'jetpack_sync_dedicated_sync_spawn_check'; + + /** + * Validation string to check if the endpoint is working correctly. + * + * This is extracted and not hardcoded, as we might want to change it in the future. + */ + const DEDICATED_SYNC_VALIDATION_STRING = 'DEDICATED SYNC OK'; + + /** + * Filter a URL to check if Dedicated Sync is enabled. + * We need to remove slashes and then run it through `urldecode` as sometimes the + * URL is in an encoded form, depending on server configuration. + * + * @param string $url The URL to filter. + * + * @return string + */ + public static function prepare_url_for_dedicated_request_check( $url ) { + return urldecode( $url ); + } + /** + * Check if this request should trigger Sync to run. + * + * @access public + * + * @return boolean True if this is a 'jetpack/v4/sync/spawn-sync', false otherwise. + */ + public static function is_dedicated_sync_request() { + /** + * Check $_SERVER['REQUEST_URI'] first, to see if we're in the right context. + * This is done to make sure we can hook in very early in the initialization of WordPress to + * be able to send sync requests to the backend as fast as possible, without needing to continue + * loading things for the request. + */ + if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { + return false; + } + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.NonceVerification.Recommended + $check_url = self::prepare_url_for_dedicated_request_check( wp_unslash( $_SERVER['REQUEST_URI'] ) ); + if ( strpos( $check_url, 'jetpack/v4/sync/spawn-sync' ) !== false ) { + return true; + } + + /** + * If the above check failed, we might have an issue with detecting calls to the REST endpoint early on. + * Sometimes, like when permalinks are disabled, the REST path is sent via the `rest_route` GET parameter. + * We want to check it too, to make sure we managed to cover more cases and be more certain we actually + * catch calls to the endpoint. + */ + if ( ! isset( $_GET['rest_route'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended + return false; + } + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.NonceVerification.Recommended + $check_url = self::prepare_url_for_dedicated_request_check( wp_unslash( $_GET['rest_route'] ) ); + if ( strpos( $check_url, 'jetpack/v4/sync/spawn-sync' ) !== false ) { + return true; + } + + return false; + } + + /** + * Send a request to run Sync for a certain sync queue + * through HTTP request that doesn't halt page loading. + * + * @access public + * + * @param Automattic\Jetpack\Sync\Queue $queue Queue object. + * + * @return boolean|WP_Error True if spawned, WP_Error otherwise. + */ + public static function spawn_sync( $queue ) { + if ( ! Settings::is_dedicated_sync_enabled() ) { + return new WP_Error( 'dedicated_sync_disabled', 'Dedicated Sync flow is disabled.' ); + } + + if ( $queue->is_locked() ) { + return new WP_Error( 'locked_queue_' . $queue->id ); + } + + if ( $queue->size() === 0 ) { + return new WP_Error( 'empty_queue_' . $queue->id ); + } + + // Return early if we've gotten a retry-after header response that is not expired. + $retry_time = get_option( Actions::RETRY_AFTER_PREFIX . $queue->id ); + if ( $retry_time && $retry_time >= microtime( true ) ) { + return new WP_Error( 'retry_after_' . $queue->id ); + } + + // Don't sync if we are throttled. + $sync_next_time = Sender::get_instance()->get_next_sync_time( $queue->id ); + if ( $sync_next_time > microtime( true ) ) { + return new WP_Error( 'sync_throttled_' . $queue->id ); + } + + $url = rest_url( 'jetpack/v4/sync/spawn-sync' ); + $url = add_query_arg( 'time', time(), $url ); // Enforce Cache busting. + $args = array( + 'cookies' => $_COOKIE, + 'blocking' => false, + 'timeout' => 0.01, + /** This filter is documented in wp-includes/class-wp-http-streams.php */ + 'sslverify' => apply_filters( 'https_local_ssl_verify', false ), + ); + + $result = wp_remote_get( $url, $args ); + if ( is_wp_error( $result ) ) { + return $result; + } + + return true; + } + + /** + * Test Sync spawning functionality by making a request to the + * Sync spawning endpoint and storing the result (status code) in a transient. + * + * @since $$next_version$$ + * + * @return bool True if we got a successful response, false otherwise. + */ + public static function can_spawn_dedicated_sync_request() { + $dedicated_sync_check_transient = self::DEDICATED_SYNC_CHECK_TRANSIENT; + + $dedicated_sync_response_body = get_transient( $dedicated_sync_check_transient ); + + if ( false === $dedicated_sync_response_body ) { + $url = rest_url( 'jetpack/v4/sync/spawn-sync' ); + $url = add_query_arg( 'time', time(), $url ); // Enforce Cache busting. + $args = array( + 'cookies' => $_COOKIE, + 'timeout' => 30, + /** This filter is documented in wp-includes/class-wp-http-streams.php */ + 'sslverify' => apply_filters( 'https_local_ssl_verify', false ), + ); + + $response = wp_remote_get( $url, $args ); + $dedicated_sync_response_code = wp_remote_retrieve_response_code( $response ); + $dedicated_sync_response_body = trim( wp_remote_retrieve_body( $response ) ); + + /** + * Limit the size of the body that we save in the transient to avoid cases where an error + * occurs and a whole generated HTML page is returned. We don't need to store the whole thing. + * + * The regexp check is done to make sure we can detect the string even if the body returns some additional + * output, like some caching plugins do when they try to pad the request. + */ + $regexp = '!' . preg_quote( self::DEDICATED_SYNC_VALIDATION_STRING, '!' ) . '!uis'; + if ( preg_match( $regexp, $dedicated_sync_response_body ) ) { + $saved_response_body = self::DEDICATED_SYNC_VALIDATION_STRING; + } else { + $saved_response_body = time(); + } + + set_transient( $dedicated_sync_check_transient, $saved_response_body, HOUR_IN_SECONDS ); + + // Send a bit more information to WordPress.com to help debugging issues. + if ( $saved_response_body !== self::DEDICATED_SYNC_VALIDATION_STRING ) { + $data = array( + 'timestamp' => microtime( true ), + 'response_code' => $dedicated_sync_response_code, + 'response_body' => $dedicated_sync_response_body, + + // Send the flow type that was attempted. + 'sync_flow_type' => 'dedicated', + ); + + $sender = Sender::get_instance(); + + $sender->send_action( 'jetpack_sync_flow_error_enable', $data ); + } + } + + return self::DEDICATED_SYNC_VALIDATION_STRING === $dedicated_sync_response_body; + } +} diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-default-filter-settings.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-default-filter-settings.php new file mode 100644 index 00000000..81946fe8 --- /dev/null +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-default-filter-settings.php @@ -0,0 +1,80 @@ +<?php +/** + * The Default Filter Settings class. + * + * This class provides the default whitelist values for the Sync data filters. + * See the DATA_FILTER_DEFAULTS constant for the list of filters. + * + * @package automattic/jetpack-sync + */ + +namespace Automattic\Jetpack\Sync; + +/** + * The Default_Filter_Settings class + */ +class Default_Filter_Settings { + + /** + * The class that contains the default values of the filters. + */ + const DEFAULT_FILTER_CLASS = 'Automattic\Jetpack\Sync\Defaults'; + + /** + * A map of each Sync filter name to the associated property name in the Defaults class. + */ + const DATA_FILTER_DEFAULTS = array( + 'jetpack_sync_options_whitelist' => 'default_options_whitelist', + 'jetpack_sync_options_contentless' => 'default_options_contentless', + 'jetpack_sync_constants_whitelist' => 'default_constants_whitelist', + 'jetpack_sync_callable_whitelist' => 'default_callable_whitelist', + 'jetpack_sync_multisite_callable_whitelist' => 'default_multisite_callable_whitelist', + 'jetpack_sync_post_meta_whitelist' => 'post_meta_whitelist', + 'jetpack_sync_comment_meta_whitelist' => 'comment_meta_whitelist', + 'jetpack_sync_capabilities_whitelist' => 'default_capabilities_whitelist', + 'jetpack_sync_known_importers' => 'default_known_importers', + ); + + /** + * The data associated with these filters are stored as associative arrays. + * (All other filters store data as indexed arrays.) + */ + const ASSOCIATIVE_FILTERS = array( + 'jetpack_sync_callable_whitelist', + 'jetpack_sync_multisite_callable_whitelist', + 'jetpack_sync_known_importers', + ); + + /** + * Returns the default data settings list for the provided filter. + * + * @param string $filter The filter name. + * + * @return array|false The default list of data settings. Returns false if the provided + * filter doesn't not have an array of default settings. + */ + public function get_default_settings( $filter ) { + if ( ! is_string( $filter ) || ! array_key_exists( $filter, self::DATA_FILTER_DEFAULTS ) ) { + return false; + } + + $property = self::DATA_FILTER_DEFAULTS[ $filter ]; + $class = self::DEFAULT_FILTER_CLASS; + return $class::$$property; + } + + /** + * Returns an array containing the default values for all of the filters shown + * in DATA_FILTER_DEFAULTS. + * + * @return array The array containing all sync data filters and their default values. + */ + public function get_all_filters_default_settings() { + $defaults = array(); + + foreach ( self::DATA_FILTER_DEFAULTS as $filter => $default_location ) { + $defaults[ $filter ] = $this->get_default_settings( $filter ); + } + return $defaults; + } +} diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php index c8c43501..4386c88f 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php @@ -95,6 +95,7 @@ class Defaults { 'jetpack_sync_settings_post_meta_whitelist', 'jetpack_sync_settings_post_types_blacklist', 'jetpack_sync_settings_taxonomies_blacklist', + 'jetpack_sync_settings_dedicated_sync_enabled', // is Dedicated Sync flow enabled. 'jetpack_testimonial', 'jetpack_testimonial_posts_per_page', 'jetpack_wga', @@ -171,6 +172,7 @@ class Defaults { 'wpcom_is_fse_activated', 'wpcom_publish_comments_with_markdown', 'wpcom_publish_posts_with_markdown', + 'videopress_private_enabled_for_site', ); /** @@ -282,6 +284,7 @@ class Defaults { */ public static $default_callable_whitelist = array( 'get_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins' ), + 'get_themes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_themes' ), 'get_plugins_action_links' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins_action_links' ), 'has_file_system_write_access' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'file_system_write_access' ), 'home_url' => array( 'Automattic\\Jetpack\\Connection\\Urls', 'home_url' ), @@ -309,6 +312,7 @@ class Defaults { 'wp_get_environment_type' => 'wp_get_environment_type', 'wp_max_upload_size' => 'wp_max_upload_size', 'wp_version' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'wp_version' ), + 'active_modules' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_active_modules' ), ); /** @@ -375,6 +379,7 @@ class Defaults { * @var array Blacklisted post types. */ public static $blacklisted_post_types = array( + '_term_meta', 'ai1ec_event', 'ai_log', // Logger - https://github.com/alleyinteractive/logger. 'amp_validated_url', // AMP Validation Errors. @@ -728,6 +733,7 @@ class Defaults { 'switch_like_status', 'videopress_guid', 'vimeo_poster_image', + '_jetpack_blogging_prompt_key', ); /** @@ -1269,4 +1275,11 @@ class Defaults { ), ); + /** + * Default for enabling dedicated Sync flow. + * + * @var int Bool-ish. Default 0. + */ + public static $default_dedicated_sync_enabled = 0; + } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php index 02de16cd..b007c695 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php @@ -9,6 +9,7 @@ namespace Automattic\Jetpack\Sync; use Automattic\Jetpack\Connection\Urls; use Automattic\Jetpack\Constants; +use Automattic\Jetpack\Modules as Jetpack_Modules; /** * Utility functions to generate data synced to wpcom @@ -72,7 +73,7 @@ class Functions { $cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) ); // recursive taxonomies are no fun. - if ( is_null( $cloned_taxonomy ) ) { + if ( $cloned_taxonomy === null ) { return null; } // Remove any meta_box_cb if they are not the default wp ones. @@ -82,7 +83,7 @@ class Functions { } // Remove update call back. if ( isset( $cloned_taxonomy->update_count_callback ) && - ! is_null( $cloned_taxonomy->update_count_callback ) ) { + $cloned_taxonomy->update_count_callback !== null ) { $cloned_taxonomy->update_count_callback = null; } // Remove rest_controller_class if it something other then the default. @@ -466,7 +467,7 @@ class Functions { } $plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() ); if ( ! empty( $plugins_action_links ) ) { - if ( is_null( $plugin_file_singular ) ) { + if ( $plugin_file_singular === null ) { return $plugins_action_links; } return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null ); @@ -628,4 +629,47 @@ class Functions { return $any; } + + /** + * Return the list of installed themes + * + * @since 1.31.0 + * + * @return array + */ + public static function get_themes() { + $current_stylesheet = get_stylesheet(); + $installed_themes = wp_get_themes(); + $synced_headers = array( 'Name', 'ThemeURI', 'Author', 'Version', 'Template', 'Status', 'TextDomain', 'RequiresWP', 'RequiresPHP' ); + $themes = array(); + foreach ( $installed_themes as $stylesheet => $theme ) { + $themes[ $stylesheet ] = array(); + foreach ( $synced_headers as $header ) { + $themes[ $stylesheet ][ $header ] = $theme->get( $header ); + } + $themes[ $stylesheet ]['active'] = $stylesheet === $current_stylesheet; + if ( method_exists( $theme, 'is_block_theme' ) ) { + $themes[ $stylesheet ]['is_block_theme'] = $theme->is_block_theme(); + } + } + /** + * Filters the output of Sync's get_theme callable + * + * @since 1.31.0 + * + * @param array $themes The list of installed themes formatted in an array with a collection of information extracted from the Theme's headers + */ + return apply_filters( 'jetpack_sync_get_themes_callable', $themes ); + } + + /** + * Return the list of active Jetpack modules. + * + * @since $$next_version$$ + * + * @return array + */ + public static function get_active_modules() { + return ( new Jetpack_Modules() )->get_active(); + } } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php index ce2862a4..37040d00 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php @@ -240,7 +240,7 @@ class Listener { */ $args = apply_filters( "jetpack_sync_before_enqueue_$action_name", $args ); $action_data = array( $args ); - if ( ! is_null( $previous_end ) ) { + if ( $previous_end !== null ) { $action_data[] = $previous_end; } // allow listeners to abort. @@ -426,7 +426,7 @@ class Listener { ); if ( $this->should_send_user_data_with_actor( $current_filter ) ) { - $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : ''; + $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; if ( defined( 'JETPACK__PLUGIN_DIR' ) ) { if ( ! function_exists( 'jetpack_protect_get_ip' ) ) { require_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php'; @@ -435,7 +435,7 @@ class Listener { } $actor['ip'] = $ip; - $actor['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown'; + $actor['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? filter_var( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : 'unknown'; } return $actor; @@ -480,7 +480,8 @@ class Listener { */ public function get_request_url() { if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) { - return 'http' . ( isset( $_SERVER['HTTPS'] ) ? 's' : '' ) . '://' . "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- False positive, sniff misses the call to esc_url_raw. + return esc_url_raw( 'http' . ( isset( $_SERVER['HTTPS'] ) ? 's' : '' ) . '://' . wp_unslash( "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}" ) ); } return is_admin() ? get_admin_url( get_current_blog_id() ) : home_url(); } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php index b7e590a9..b3998c90 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php @@ -46,6 +46,16 @@ class Main { } /** + * Sets the Sync data settings. + * + * @param array $data_settings An array containing the Sync data options. An empty array indicates that the default + * values will be used for all Sync data. + */ + public static function set_sync_data_options( $data_settings = array() ) { + ( new Data_Settings() )->add_settings_list( $data_settings ); + } + + /** * Initialize the main sync actions. * * @action plugins_loaded @@ -58,6 +68,7 @@ class Main { * For now additional modules are enabled based on whether the third party plugin * class exists or not. */ + Sync_Actions::initialize_search(); Sync_Actions::initialize_woocommerce(); Sync_Actions::initialize_wp_super_cache(); diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php index 69a9faf3..55383a00 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php @@ -12,7 +12,7 @@ namespace Automattic\Jetpack\Sync; */ class Package_Version { - const PACKAGE_VERSION = '1.28.0'; + const PACKAGE_VERSION = '1.35.0'; const PACKAGE_SLUG = 'sync'; diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php index fe80cf90..bf8cf812 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php @@ -411,7 +411,7 @@ class Queue { if ( is_wp_error( $is_valid ) ) { // Always delete ids_to_remove even when buffer is no longer checked-out. // They were processed by WP.com so safe to remove from queue. - if ( ! is_null( $ids_to_remove ) ) { + if ( $ids_to_remove !== null ) { $this->delete( $ids_to_remove ); } return $is_valid; @@ -420,7 +420,7 @@ class Queue { $this->delete_checkout_id(); // By default clear all items in the buffer. - if ( is_null( $ids_to_remove ) ) { + if ( $ids_to_remove === null ) { $ids_to_remove = $buffer->get_item_ids(); } @@ -478,6 +478,15 @@ class Queue { } /** + * Checks if the queue is locked. + * + * @return bool + */ + public function is_locked() { + return (bool) $this->get_checkout_id(); + } + + /** * Locks checkouts from the queue * tries to wait up to $timeout seconds for the queue to be empty. * @@ -735,7 +744,7 @@ class Queue { } // TODO: change to strict comparison. - if ( $checkout_id != $buffer->id ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison + if ( $checkout_id != $buffer->id ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual return new WP_Error( 'buffer_mismatch', 'The buffer you checked in was not checked out' ); } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php index 6687fec5..ae947009 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php @@ -1319,7 +1319,7 @@ class Replicastore implements Replicastore_Interface { } // Validate / Determine Buckets. - if ( is_null( $buckets ) || $buckets < 1 ) { + if ( $buckets === null || $buckets < 1 ) { $buckets = $this->calculate_buckets( $table, $start_id, $end_id ); } if ( is_wp_error( $buckets ) ) { diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php index ae12ff32..6e4cb8c9 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php @@ -306,6 +306,17 @@ class REST_Endpoints { ) ); + // Trigger Dedicated Sync request. + register_rest_route( + 'jetpack/v4', + '/sync/spawn-sync', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => __CLASS__ . '::spawn_sync', + 'permission_callback' => '__return_true', + ) + ); + } /** @@ -726,6 +737,38 @@ class REST_Endpoints { } /** + * This endpoint is used by Sync to spawn a + * dedicated Sync request which will trigger Sync to run. + * + * If Dedicated Sync is enabled, this callback should never run as + * processing of Sync actions will occur earlier and exit. + * + * @see Actions::init + * @see Sender::do_dedicated_sync_and_exit + * + * @since $$next_version$$ + * + * @return \WP_REST_Response + */ + public static function spawn_sync() { + nocache_headers(); + + if ( ! Settings::is_dedicated_sync_enabled() ) { + return new WP_Error( + 'dedicated_sync_disabled', + 'Dedicated Sync flow is disabled.', + array( 'status' => 422 ) + ); + } + + return new WP_Error( + 'dedicated_sync_failed', + 'Failed to process Dedicated Sync request', + array( 'status' => 500 ) + ); + } + + /** * Verify that request has default permissions to perform sync actions. * * @since 1.23.1 diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php index 6699dd61..757ce490 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php @@ -321,7 +321,59 @@ class Sender { * @return boolean|WP_Error True if this sync sending was successful, error object otherwise. */ public function do_sync() { - return $this->do_sync_and_set_delays( $this->sync_queue ); + if ( ! Settings::is_dedicated_sync_enabled() ) { + $result = $this->do_sync_and_set_delays( $this->sync_queue ); + } else { + $result = Dedicated_Sender::spawn_sync( $this->sync_queue ); + } + + return $result; + } + + /** + * Trigger incremental sync and early exit on Dedicated Sync request. + * + * @access public + * + * @param bool $do_real_exit If we should exit at the end of the request. We should by default. + * In the context of running this in the REST API, we actually want to return an error. + * + * @return void|WP_Error + */ + public function do_dedicated_sync_and_exit( $do_real_exit = true ) { + nocache_headers(); + + if ( ! Settings::is_dedicated_sync_enabled() ) { + return new WP_Error( 'dedicated_sync_disabled', 'Dedicated Sync flow is disabled.' ); + } + + if ( ! Dedicated_Sender::is_dedicated_sync_request() ) { + return new WP_Error( 'non_dedicated_sync_request', 'Not a Dedicated Sync request.' ); + } + + /** + * Output an `OK` to show that Dedicated Sync is enabled and we can process events. + * This is used to test the feature is working. + * + * @see \Automattic\Jetpack\Sync\Dedicated_Sender::can_spawn_dedicated_sync_request + */ + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Dedicated_Sender::DEDICATED_SYNC_VALIDATION_STRING; + + // Try to disconnect the request as quickly as possible and process things in the background. + $this->fastcgi_finish_request(); + + // Actually try to send Sync events. + $result = $this->do_sync_and_set_delays( $this->sync_queue ); + + // If no errors occurred, re-spawn a dedicated Sync request. + if ( true === $result ) { + Dedicated_Sender::spawn_sync( $this->sync_queue ); + } + + if ( $do_real_exit ) { + exit; + } } /** diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php index a923fbf3..31cee3b2 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php @@ -56,6 +56,7 @@ class Settings { 'full_sync_send_duration' => true, 'full_sync_limits' => true, 'checksum_disable' => true, + 'dedicated_sync_enabled' => true, ); /** @@ -198,10 +199,10 @@ class Settings { if ( self::is_network_setting( $setting ) ) { if ( is_multisite() && is_main_site() ) { - update_site_option( self::SETTINGS_OPTION_PREFIX . $setting, $value ); + $updated = update_site_option( self::SETTINGS_OPTION_PREFIX . $setting, $value ); } } else { - update_option( self::SETTINGS_OPTION_PREFIX . $setting, $value, true ); + $updated = update_option( self::SETTINGS_OPTION_PREFIX . $setting, $value, true ); } // If we set the disabled option to true, clear the queues. @@ -210,6 +211,13 @@ class Settings { $listener->get_sync_queue()->reset(); $listener->get_full_sync_queue()->reset(); } + + // Do not enable Dedicated Sync if we cannot spawn a Dedicated Sync request. + if ( 'dedicated_sync_enabled' === $setting && $updated && (bool) $value ) { + if ( ! Dedicated_Sender::can_spawn_dedicated_sync_request() ) { + update_option( self::SETTINGS_OPTION_PREFIX . $setting, 0, true ); + } + } } } @@ -443,7 +451,7 @@ class Settings { * @return boolean Whether WordPress is currently importing. */ public static function is_importing() { - if ( ! is_null( self::$is_importing ) ) { + if ( self::$is_importing !== null ) { return self::$is_importing; } @@ -484,7 +492,7 @@ class Settings { * @return boolean Whether WordPress is currently doing WP cron. */ public static function is_doing_cron() { - if ( ! is_null( self::$is_doing_cron ) ) { + if ( self::$is_doing_cron !== null ) { return self::$is_doing_cron; } @@ -565,4 +573,16 @@ class Settings { return ! (bool) self::get_setting( 'checksum_disable' ); } + /** + * Whether dedicated Sync flow is enabled. + * + * @access public + * @static + * + * @return boolean Whether dedicated Sync flow is enabled. + */ + public static function is_dedicated_sync_enabled() { + return (bool) self::get_setting( 'dedicated_sync_enabled' ); + } + } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php index 8a8c83f8..316df9ce 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php @@ -127,16 +127,16 @@ class Users { if ( $user_id === $master_user_id && 'administrator' !== $role ) { $query = new \WP_User_Query( array( - 'fields' => array( 'id' ), + 'fields' => array( 'ID' ), 'role' => 'administrator', - 'orderby' => 'id', + 'orderby' => 'ID', 'exclude' => array( $master_user_id ), ) ); $new_master = false; $connection = new Jetpack_Connection(); foreach ( $query->results as $result ) { - $found_user_id = absint( $result->id ); + $found_user_id = absint( $result->ID ); if ( $found_user_id && $connection->is_user_connected( $found_user_id ) ) { $new_master = $found_user_id; break; diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php index 436554c9..4240744e 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php @@ -77,8 +77,9 @@ class Callables extends Module { */ const OPTION_NAMES_TO_CALLABLE_NAMES = array( // @TODO: Audit the other option names for differences between the option names and callable names. - 'home' => 'home_url', - 'siteurl' => 'site_url', + 'home' => 'home_url', + 'siteurl' => 'site_url', + 'jetpack_active_modules' => 'active_modules', ); /** @@ -484,7 +485,7 @@ class Callables extends Module { $checksum = $this->get_check_sum( $value ); // Explicitly not using Identical comparison as get_option returns a string. - if ( ! is_null( $value ) && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) { + if ( $value !== null && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) { // Only send callable if the non sorted checksum also does not match. if ( $this->should_send_callable( $callable_checksums, $name, $this->get_check_sum( $value, false ) ) ) { diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php index d71a0fe1..db0d9ff4 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php @@ -198,7 +198,7 @@ class Constants extends Module { foreach ( $constants as $name => $value ) { $checksum = $this->get_check_sum( $value ); // Explicitly not using Identical comparison as get_option returns a string. - if ( ! $this->still_valid_checksum( $constants_checksums, $name, $checksum ) && ! is_null( $value ) ) { + if ( ! $this->still_valid_checksum( $constants_checksums, $name, $checksum ) && $value !== null ) { /** * Tells the client to sync a constant to the server * diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php index 4017df16..bee3e889 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php @@ -246,7 +246,10 @@ class Full_Sync_Immediately extends Module { // Set default configuration, calculate totals, and save configuration if totals > 0. $status = array(); foreach ( $full_sync_config as $name => $config ) { - $module = Modules::get_module( $name ); + $module = Modules::get_module( $name ); + if ( ! $module ) { + continue; + } $status[ $name ] = array( 'total' => $module->total( $config ), 'sent' => 0, diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php index 0fe9245c..90121e88 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php @@ -133,7 +133,7 @@ class Full_Sync extends Module { $total_items = $module->estimate_full_sync_actions( $module_config ); // If there's information to process, configure this module. - if ( ! is_null( $total_items ) && $total_items > 0 ) { + if ( $total_items !== null && $total_items > 0 ) { $full_sync_config[ $module_name ] = $module_config; $enqueue_status[ $module_name ] = array( $total_items, // Total. @@ -266,7 +266,7 @@ class Full_Sync extends Module { $enqueue_status[ $module->name() ][2] = $next_enqueue_state; // If items were processed, subtract them from the limit. - if ( ! is_null( $items_enqueued ) && $items_enqueued > 0 ) { + if ( $items_enqueued !== null && $items_enqueued > 0 ) { $enqueue_status[ $module->name() ][1] += $items_enqueued; $remaining_items_to_enqueue -= $items_enqueued; } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php index b244834f..06c06ab0 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php @@ -205,7 +205,7 @@ class Plugins extends Module { $plugins = get_plugins(); // Get the most up to date info. if ( isset( $plugins[ $slug ] ) ) { return array_merge( array( 'slug' => $slug ), $plugins[ $slug ] ); - }; + } // Try grabbing the info from before the update. return isset( $this->plugins[ $slug ] ) ? array_merge( array( 'slug' => $slug ), $this->plugins[ $slug ] ) : array( 'slug' => $slug ); } @@ -263,8 +263,8 @@ class Plugins extends Module { return; } - // phpcs:ignore WordPress.Security.NonceVerification.Missing - $plugin = $_POST['plugin']; + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated manually just after. + $plugin = wp_unslash( $_POST['plugin'] ); $plugins = get_plugins(); if ( ! isset( $plugins[ $plugin ] ) ) { return; diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php index b9ea21d1..5623cc2b 100644 --- a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php @@ -589,7 +589,7 @@ class Posts extends Module { * @param boolean $update Whether this is an existing post being updated or not. */ public function wp_insert_post( $post_ID, $post = null, $update = null ) { - if ( ! is_numeric( $post_ID ) || is_null( $post ) ) { + if ( ! is_numeric( $post_ID ) || $post === null ) { return; } @@ -633,7 +633,7 @@ class Posts extends Module { * @param \WP_Post $post Post object. **/ public function wp_after_insert_post( $post_ID, $post ) { - if ( ! is_numeric( $post_ID ) || is_null( $post ) ) { + if ( ! is_numeric( $post_ID ) || $post === null ) { return; } diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-search.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-search.php new file mode 100644 index 00000000..e6472c54 --- /dev/null +++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-search.php @@ -0,0 +1,1846 @@ +<?php +/** + * Configuration lists for Jetpack Search Fields + * + * Post Meta: list of post meta keys that are available in the index + * and how they are configured. + * + * Custom Taxonomy: list of custom taxonomies that are indexed. + * + * The reason we need an allowed list is that Elasticsearch runs into scaling problems + * with more than 200k-ish total fields. The barrier to adding new fields is low, + * just open a PR. + * + * Although the comments indicate specific plugins, you don't need to be running + * that plugin for the indexing to work. The metakey just has to match. + * + * If you need a new meta key or taxonomy also consider using: + * jetpack-search-meta0 - jetpack-search-meta9 + * jetpack-search-tag0 - jetpack-search-tag9 + * + * @package automattic/jetpack-sync + */ + +namespace Automattic\Jetpack\Sync\Modules; + +/** + * Class to handle sync for Jetpack Search. + */ +class Search extends Module { + + /** + * Sync module name. + * + * @access public + * + * @return string + */ + public function name() { + return 'search'; + } + + /** + * Constructor. + */ + public function __construct() { + // Post meta whitelists. + add_filter( 'jetpack_sync_post_meta_whitelist', array( $this, 'add_search_post_meta_whitelist' ), 10 ); + // Add options + add_filter( 'jetpack_sync_options_whitelist', array( $this, 'add_search_options_whitelist' ), 10 ); + } + + /** + * Post meta search specification. + * + * We sync and index all meta keys in this list. Additionally there are a few + * options. + * + * 'metakey' => [ 'searchable_in_all_content' => true ], + * Field will be included in the all_content fields + * + * 'metakey' => [ 'available' => false, 'alternatives' => [ 'metakey_processed' ] ], + * Field not in meta.* but has data in an alternative field(s) name that + * should work similarly. For instance, woocommerce total_sales does not go into + * the index, but the percentage of sales does. + * + * @static + * @access private + * @var array + */ + private static $postmeta_to_sync = array( + // jetpack. + 'jetpack-search-meta0' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta1' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta2' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta3' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta4' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta5' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta6' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta7' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta8' => array( 'searchable_in_all_content' => true ), + 'jetpack-search-meta9' => array( 'searchable_in_all_content' => true ), + + // woocommerce. + 'entity_types' => array(), + 'exclude_product_categories' => array(), + 'exclude_product_ids' => array(), + 'free_shipping' => array(), + 'id_field' => array(), + 'individual_use' => array(), + 'limit_usage_to_x_items' => array(), + 'maximum_amount' => array(), + 'minimum_amount' => array(), + 'post_id' => array(), + 'product_categories' => array( 'searchable_in_all_content' => true ), + 'product_ids' => array(), + 'total_sales' => array( + 'available' => false, + 'alternatives' => array( + 'wc.percent_of_sales', + ), + ), + 'usage_limit' => array(), + 'usage_limit_per_user' => array(), + '_crosssell_ids' => array(), + '_downloadable' => array(), + '_featured' => array(), + '_height' => array(), + '_length' => array(), + '_price' => array( + 'alternatives' => array( + 'wc.price', + 'wc.min_price', + 'wc.max_price', + ), + ), + '_prices_include_tax' => array(), + '_product_attributes' => array(), + '_product_version' => array(), + '_regular_price' => array( + 'alternatives' => array( + 'wc.regular_price', + ), + ), + '_sale_price' => array( + 'alternatives' => array( + 'wc.sale_price', + ), + ), + '_sale_price_dates_from' => array(), + '_sale_price_dates_to' => array(), + '_sku' => array( 'searchable_in_all_content' => true ), + '_stock_status' => array(), + '_wc_average_rating' => array( + 'alternatives' => array( + 'wc.ave_rating_score', + ), + ), + '_wc_rating_count' => array( + 'alternatives' => array( + 'wc.rating', // wc.rating.count_1, wc.rating.count_2, ... + ), + ), + '_wc_review_count' => array(), + '_weight' => array(), + '_width' => array(), + + // co-authors plus. + 'cap-description' => array( 'searchable_in_all_content' => true ), + 'cap-user_login' => array( 'searchable_in_all_content' => true ), + 'cap-user_email' => array(), + 'cap-last_name' => array( 'searchable_in_all_content' => true ), + 'cap-first_name' => array( 'searchable_in_all_content' => true ), + 'cap-display_name' => array( 'searchable_in_all_content' => true ), + 'cap-website' => array(), + 'cap-jabber' => array(), + 'cap-aim' => array(), + 'cap-twitter' => array(), + 'cap-facebook' => array(), + 'cap-google_plus' => array(), + 'cap-job_title' => array( 'searchable_in_all_content' => true ), + + // bbpress. + 'bbpl_like' => array(), + 'bbpress_discussion_comments_copied' => array(), + 'bbpress_discussion_tags_copied' => array(), + 'bbpress_discussion_topic_id' => array(), + 'bbpress_discussion_use_defaults' => array(), + 'bbpress_page_header_bg' => array(), + 'bbpress_title_bg' => array(), + 'use_bbpress_discussion_topic' => array(), + + // wpml. + 'tm_meta_wpml' => array(), + 'wpml_language' => array(), + 'wpml_media_lang' => array(), + 'wpml_media_processed' => array(), + + // blogger import. + 'blogger_author' => array( 'searchable_in_all_content' => true ), + 'blogger_blog' => array( 'searchable_in_all_content' => true ), + 'blogger_permalink' => array( 'searchable_in_all_content' => true ), + + // geo. + 'geo_address' => array( 'searchable_in_all_content' => true ), + 'geo_latitude' => array(), + 'geo_longitude' => array(), + 'geo_public' => array(), + 'geolocated' => array(), + 'geolocation_city' => array( 'searchable_in_all_content' => true ), + 'geolocation_country_long' => array( 'searchable_in_all_content' => true ), + 'geolocation_country_short' => array( 'searchable_in_all_content' => true ), + 'geolocation_formatted_address' => array( 'searchable_in_all_content' => true ), + 'geolocation_lat' => array(), + 'geolocation_long' => array(), + 'geolocation_postcode' => array( 'searchable_in_all_content' => true ), + 'geolocation_state_long' => array( 'searchable_in_all_content' => true ), + 'geolocation_state_short' => array( 'searchable_in_all_content' => true ), + + // wp-ultimate-recipe. + 'recipe_alternate_image' => array(), + 'recipe_cook_time' => array(), + 'recipe_cook_time_text' => array(), + 'recipe_description' => array( 'searchable_in_all_content' => true ), + 'recipe_ingredients' => array( 'searchable_in_all_content' => true ), + 'recipe_instructions' => array( 'searchable_in_all_content' => true ), + 'recipe_notes' => array( 'searchable_in_all_content' => true ), + 'recipe_nutritional' => array( 'searchable_in_all_content' => true ), + 'recipe_passive_time' => array(), + 'recipe_passive_time_text' => array(), + 'recipe_prep_time' => array(), + 'recipe_prep_time_text' => array(), + 'recipe_rating' => array(), + 'recipe_servings' => array(), + 'recipe_servings_normalized' => array(), + 'recipe_servings_type' => array(), + 'recipe_terms' => array( 'searchable_in_all_content' => true ), + 'recipe_terms_with_parents' => array(), + 'recipe_title' => array( 'searchable_in_all_content' => true ), + 'recipe_user_ratings' => array(), + 'recipe_user_ratings_rating' => array(), + + // generic fields. + // from advanced-custom-fields and metabox.io . + 'Link' => array(), + 'Location' => array(), + 'Title' => array( 'searchable_in_all_content' => true ), + 'ad_code' => array(), + 'address' => array(), + 'admin_mail' => array(), + 'admin_only' => array(), + 'advertisers' => array( 'searchable_in_all_content' => true ), + 'age' => array(), + 'aliases' => array(), + 'alternate_title' => array(), + 'amazon' => array(), + 'answer' => array( 'searchable_in_all_content' => true ), + 'area' => array(), + 'attention' => array(), + 'attr' => array(), + 'author' => array( 'searchable_in_all_content' => true ), + 'author_name' => array( 'searchable_in_all_content' => true ), + 'blog' => array(), + 'blog_id' => array(), + 'call_to_action' => array(), + 'campaign_preview' => array(), + 'canonical_url' => array(), + 'catch_text' => array(), + 'category' => array( 'searchable_in_all_content' => true ), + 'classificacao' => array(), + 'classification' => array(), + 'code' => array(), + 'codigo' => array(), + 'company' => array( 'searchable_in_all_content' => true ), + 'company_website' => array(), + 'config' => array(), + 'construction' => array(), + 'container_ids' => array(), + 'content' => array( 'searchable_in_all_content' => true ), + 'content_body-full_content' => array( 'searchable_in_all_content' => true ), + 'copyright' => array(), + 'custom_page_title' => array( 'searchable_in_all_content' => true ), + 'custom_permalink' => array(), + 'customize' => array(), + 'data' => array(), + 'date' => array(), + 'day' => array(), + 'descripcion' => array( 'searchable_in_all_content' => true ), + 'description' => array( 'searchable_in_all_content' => true ), + 'display_settings' => array(), + 'display_type' => array(), + 'duration' => array(), + 'embed' => array(), + 'entity_ids' => array(), + 'entity_types' => array(), + 'event_subtitle' => array( 'searchable_in_all_content' => true ), + 'excluded_container_ids' => array(), + 'exclusions' => array(), + 'experience' => array(), + 'external_url' => array(), + 'featured' => array(), + 'featured_image' => array(), + 'featured_post' => array(), + 'featured_story' => array(), + 'fee' => array(), + 'filter' => array(), + 'follow' => array(), + 'footer_text' => array(), + 'from_header' => array(), + 'fullscreen_view' => array(), + 'gallery' => array(), + 'genre' => array( 'searchable_in_all_content' => true ), + 'guests' => array( 'searchable_in_all_content' => true ), + 'has_variations' => array(), + 'hashtag' => array(), + 'header_image' => array(), + 'hidden_from_ui' => array(), + 'hide_on_screen' => array(), + 'homepage_order' => array(), + 'hours' => array(), + 'i18n' => array(), + 'id' => array(), + 'image' => array(), + 'image_size' => array(), + 'image_source' => array(), + 'index' => array(), + 'intro_text' => array( 'searchable_in_all_content' => true ), + 'job_mention' => array( 'searchable_in_all_content' => true ), + 'keywords' => array( 'searchable_in_all_content' => true ), + 'latest_news' => array(), + 'layout' => array(), + 'link' => array(), + 'link_dump' => array( 'searchable_in_all_content' => true ), + 'link_url' => array(), + 'location' => array(), + 'logo' => array(), + 'main_title' => array( 'searchable_in_all_content' => true ), + 'maximum_entity_count' => array(), + 'media' => array(), + 'mentions' => array(), + 'messages' => array(), + 'meta_description' => array( 'searchable_in_all_content' => true ), + 'meta_id' => array(), + 'meta_index' => array(), + 'meta_key' => array(), + 'meta_value' => array(), + 'modal-dialog-id' => array(), + 'name' => array( 'searchable_in_all_content' => true ), + 'nombre' => array( 'searchable_in_all_content' => true ), + 'notes' => array( 'searchable_in_all_content' => true ), + 'options' => array(), + 'order_by' => array(), + 'order_direction' => array(), + 'original_cats' => array(), + 'original_headers' => array(), + 'original_link' => array(), + 'original_message' => array(), + 'original_subject' => array(), + 'original_title' => array(), + 'original_to' => array(), + 'other_setting' => array(), + 'page_canonical' => array(), + 'page_layout' => array(), + 'page_sidebar' => array(), + 'page_tags' => array(), + 'panels_data' => array(), + 'parking' => array(), + 'pdf_upload' => array(), + 'photo' => array(), + 'play_time' => array(), + 'position' => array(), + 'post-rating' => array(), + 'post_background' => array(), + 'post_color' => array(), + 'post_sidebar' => array(), + 'post_subtitle' => array( 'searchable_in_all_content' => true ), + 'price' => array(), + 'publication' => array(), + 'rating' => array(), + 'ratings_average' => array(), + 'ratings_score' => array(), + 'ratings_users' => array(), + 'relation' => array(), + 'reply_to_header' => array(), + 'required' => array(), + 'returns' => array(), + 'review_post' => array(), + 'rule' => array(), + 'section' => array( 'searchable_in_all_content' => true ), + 'session_transcript' => array(), + 'settings' => array(), + 'sex' => array(), + 'shares_count' => array(), + 'show_description' => array( 'searchable_in_all_content' => true ), + 'show_page_title' => array(), + 'side' => array(), + 'sidebar' => array(), + 'site' => array(), + 'situation' => array(), + 'slide_template' => array(), + 'slug' => array(), + 'sortorder' => array(), + 'source' => array(), + 'start_date' => array(), + 'status' => array(), + 'styles' => array(), + 'subtitle' => array( 'searchable_in_all_content' => true ), + 'subtitulo' => array(), + 'success' => array(), + 'summary' => array( 'searchable_in_all_content' => true ), + 'synopsis' => array( 'searchable_in_all_content' => true ), + 'tel' => array(), + 'tema' => array(), + 'testimonial' => array(), + 'testimonial_author' => array(), + 'text_already_subscribed' => array(), + 'text_error' => array(), + 'text_invalid_email' => array(), + 'text_not_subscribed' => array(), + 'text_required_field_missing' => array(), + 'text_subscribed' => array(), + 'text_unsubscribed' => array(), + 'thumbnail' => array(), + 'time' => array(), + 'time_jump_list' => array( 'searchable_in_all_content' => true ), + 'title' => array( 'searchable_in_all_content' => true ), + 'title_view' => array(), + 'titre' => array( 'searchable_in_all_content' => true ), + 'titulo' => array( 'searchable_in_all_content' => true ), + 'to_header' => array(), + 'toc' => array(), + 'transcript' => array( 'searchable_in_all_content' => true ), + 'transport_uri' => array(), + 'type' => array(), + 'url' => array(), + 'validation' => array(), + 'value' => array(), + 'values' => array(), + 'variation' => array(), + 'video' => array(), + 'video_type' => array(), + 'video_url' => array(), + 'videopress_guid' => array(), + 'website' => array(), + 'weight' => array(), + 'year' => array(), + + ); // end indexed post meta. + + /** + * Postmeta being considered for indexing + * but currently not in the index + * this list is really only for documentation. + * + * @static + * @access private + * @var array + */ + private static $unindexed_postmeta = array( + + // Core. + '_wp_attached_file' => array(), + '_wp_attachment_context' => array(), + '_wp_attachment_image_alt' => array(), + '_wp_attachment_is_custom_header' => array(), + '_wp_attachment_metadata' => array(), + '_wp_desired_post_slug' => array(), + '_wp_old_date' => array(), + '_wp_old_slug' => array(), + '_wp_page_template' => array(), + + // WooCommerce products. + // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-product-data-store-cpt.php#L21 . + '_backorders' => array(), + '_default_attributes' => array(), + '_download_expiry' => array(), + '_download_limit' => array(), + '_download_permissions_granted' => array(), + '_downloadable_files' => array(), + '_file_paths' => array(), + '_manage_stock' => array(), + '_product_image_gallery' => array(), + '_purchase_note' => array(), + '_recorded_coupon_usage_counts' => array(), + '_recorded_sales' => array(), + '_sold_individually' => array(), + '_stock' => array(), + '_tax_class' => array(), + '_tax_status' => array(), + '_thumbnail_id' => array(), + '_upsell_ids' => array(), + '_variation_description' => array(), + '_virtual' => array(), + '_visibility' => array(), + 'coupon_amount' => array(), + 'default_source' => array(), + 'discount_type' => array(), + 'exclude_sale_items' => array(), + 'expiry_date' => array(), + + // Woocommerce orders and refunds. + // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L27 . + // See https://github.com/woocommerce/woocommerce/blob/b8a2815ae546c836467008739e7ff5150cb08e93/includes/data-stores/class-wc-order-refund-data-store-cpt.php#L20 . + '_billing_address_1' => array(), + '_billing_address_2' => array(), + '_billing_address_index' => array(), + '_billing_city' => array(), + '_billing_company' => array(), + '_billing_country' => array(), + '_billing_email' => array(), + '_billing_first_name' => array(), + '_billing_last_name' => array(), + '_billing_phone' => array(), + '_billing_postcode' => array(), + '_billing_state' => array(), + '_cart_discount' => array(), + '_cart_discount_tax' => array(), + '_completed_date' => array(), + '_created_via' => array(), + '_customer_ip_address' => array(), + '_customer_user_agent' => array(), + '_date_completed' => array(), + '_date_paid' => array(), + '_order_currency' => array(), + '_order_key' => array(), + '_order_shipping' => array(), + '_order_shipping_tax' => array(), + '_order_stock_reduced' => array(), + '_order_tax' => array(), + '_order_total' => array(), + '_order_version' => array(), + '_paid_date' => array(), + '_payment_method' => array(), + '_payment_method_title' => array(), + '_payment_tokens' => array(), + '_recorded_coupon_usage_counts' => array(), + '_refund_amount' => array(), + '_refund_reason' => array(), + '_refunded_by' => array(), + '_shipping_address_1' => array(), + '_shipping_address_2' => array(), + '_shipping_address_index' => array(), + '_shipping_city' => array(), + '_shipping_company' => array(), + '_shipping_country' => array(), + '_shipping_first_name' => array(), + '_shipping_last_name' => array(), + '_shipping_postcode' => array(), + '_shipping_state' => array(), + '_transaction_id' => array(), + + // aioseop. + '_aioseop_description' => array(), + '_aioseop_keywords' => array(), + '_aioseop_title' => array(), + + // yoast. + '_yoast_wpseo_authorship' => array(), + '_yoast_wpseo_bctitle' => array(), + '_yoast_wpseo_canonical' => array(), + '_yoast_wpseo_content_score' => array(), + '_yoast_wpseo_focuskw' => array(), + '_yoast_wpseo_focuskw_text_input' => array(), + '_yoast_wpseo_google-plus-description' => array(), + '_yoast_wpseo_google-plus-image' => array(), + '_yoast_wpseo_linkdex' => array(), + '_yoast_wpseo_meta-robots-adv' => array(), + '_yoast_wpseo_meta-robots-nofollow' => array(), + '_yoast_wpseo_meta-robots-noindex' => array(), + '_yoast_wpseo_metadesc' => array(), + '_yoast_wpseo_metakeywords' => array(), + '_yoast_wpseo_opengraph-description' => array(), + '_yoast_wpseo_opengraph-image' => array(), + '_yoast_wpseo_opengraph-title' => array(), + '_yoast_wpseo_primary_byline' => array(), + '_yoast_wpseo_primary_category' => array(), + '_yoast_wpseo_primary_product_cat' => array(), + '_yoast_wpseo_primary_sponsor-type' => array(), + '_yoast_wpseo_primary_tema_category' => array(), + '_yoast_wpseo_primary_wpdmcategory' => array(), + '_yoast_wpseo_primary_wt_portfolio_category' => array(), + '_yoast_wpseo_redirect' => array(), + '_yoast_wpseo_sitemap-include' => array(), + '_yoast_wpseo_sitemap-prio' => array(), + '_yoast_wpseo_title' => array(), + '_yoast_wpseo_twitter-description' => array(), + '_yoast_wpseo_twitter-image' => array(), + + // bbpress. + 'bbppu_read_by' => array(), + '_bbp_activity_id' => array(), + '_bbp_attachment' => array(), + '_bbp_attachment_upload_error' => array(), + '_bbp_forum_id' => array(), + '_bbp_forum_parent_id' => array(), + '_bbp_forum_subforum_count' => array(), + '_bbp_forum_type' => array(), + '_bbp_group_ids' => array(), + '_bbp_last_active_id' => array(), + '_bbp_last_active_time' => array(), + '_bbp_last_reply_id' => array(), + '_bbp_last_topic_id' => array(), + '_bbp_old_forum_id' => array(), + '_bbp_old_sticky_status' => array(), + '_bbp_old_topic_id' => array(), + '_bbp_post_id' => array(), + '_bbp_reply_count' => array(), + '_bbp_reply_is_private' => array(), + '_bbp_reply_to' => array(), + '_bbp_revision_log' => array(), + '_bbp_status' => array(), + '_bbp_sticky_topics' => array(), + '_bbp_topic_count' => array(), + '_bbp_topic_id' => array(), + '_bbp_total_reply_count' => array(), + '_bbp_total_topic_count' => array(), + '_bbp_voice_count' => array(), + + // ??? + '_locale' => array(), + + // wp-job-manager. + '_job_title' => array(), + '_job_description' => array(), + + // wpml. + '_wpml_media_duplicate' => array(), + '_wpml_media_featured' => array(), + + // generic fields. + 'ad_clicks_count' => array(), + 'email' => array(), + 'usage_count' => array(), + 'user_mail' => array(), + 'views' => array(), + '_EventAllDay' => array(), + '_EventCost' => array(), + '_EventCurrencyPosition' => array(), + '_EventCurrencySymbol' => array(), + '_EventDuration' => array(), + '_EventEndDate' => array(), + '_EventEndDateUTC' => array(), + '_EventOrganizerID' => array(), + '_EventOrigin' => array(), + '_EventShowMap' => array(), + '_EventShowMapLink' => array(), + '_EventStartDate' => array(), + '_EventStartDateUTC' => array(), + '_EventTimezone' => array(), + '_EventTimezoneAbbr' => array(), + '_EventURL' => array(), + '_EventVenueID' => array(), + '_OrganizerEmail' => array(), + '_OrganizerOrganizer' => array(), + '_OrganizerOrigin' => array(), + '_OrganizerPhone' => array(), + '_OrganizerWebsite' => array(), + '_VenueAddress' => array(), + '_VenueCity' => array(), + '_VenueCountry' => array(), + '_VenueOrigin' => array(), + '_VenuePhone' => array(), + '_VenueProvince' => array(), + '_VenueShowMap' => array(), + '_VenueShowMapLink' => array(), + '_VenueState' => array(), + '_VenueStateProvince' => array(), + '_VenueURL' => array(), + '_VenueVenue' => array(), + '_VenueVenueID' => array(), + '_VenueZip' => array(), + '_default_attributes' => array(), + '_description' => array(), + '_edit_last' => array(), + '_feedback_all_fields' => array(), + '_feedback_author' => array(), + '_feedback_author_email' => array(), + '_feedback_author_url' => array(), + '_feedback_contact_form_url' => array(), + '_feedback_ip' => array(), + '_feedback_subject' => array(), + '_file_paths' => array(), + '_layout' => array(), + '_links_to' => array(), + '_links_to_target' => array(), + '_mail' => array(), + '_mail_2' => array(), + '_messages' => array(), + '_numero' => array(), + '_post_restored_from' => array(), + '_video_url' => array(), + '_website' => array(), + + ); // end unindexed post meta. + + /** + * List of indexed taxonomy slugs - VARCHAR(32) + * + * @access private + * @static + * + * @var array + */ + private static $taxonomies_to_sync = array( + + // Core. + 'link_category', + 'nav_menu', + 'post_format', // Special, limited to certain values. + + // bbpress. + 'topic', + 'topic-tag', + 'topics', + + // buddypress. + 'bp-email-type', + 'bp-email-type', + 'bp_docs_access', + 'bp_docs_associated_item', + 'bp_docs_comment_access', + 'bp_docs_doc_in_folder', + 'bp_docs_folder_in_group', + 'bp_docs_tag', + 'bp_member_type', + + // co-authors plus. + 'author', + + // events calendar plus. + // the events calendar. + 'event-categories', + 'event-category', + 'event-tag', + 'event-tags', + 'event-type', + 'event-venue', + 'event_category', + 'event_location', + 'event_organizer', + 'event_tag', + 'event_type', + 'event_type_2', + 'event_users', + 'events_categories', + 'events_category', + 'events_feeds', + 'events_tags', + 'tribe_events_cat', + + // jetpack. + 'jetpack-portfolio-tag', + 'jetpack-portfolio-type', + 'jetpack-search-tag0', + 'jetpack-search-tag1', + 'jetpack-search-tag2', + 'jetpack-search-tag3', + 'jetpack-search-tag4', + 'jetpack-search-tag5', + 'jetpack-search-tag6', + 'jetpack-search-tag7', + 'jetpack-search-tag8', + 'jetpack-search-tag9', + + // nextgen gallery. + 'ngg_tag', + + // polylang. + // wpml. + 'language', + 'post_translations', + 'term_language', + 'term_translations', + 'translation_priority', + + // woocommerce. + 'pa_accessory-type', + 'pa_actor', + 'pa_age', + 'pa_ambulance', + 'pa_amount', + 'pa_arm-roll', + 'pa_aspectratio', + 'pa_audiencerating', + 'pa_author', + 'pa_axle', + 'pa_battery', + 'pa_belakang', + 'pa_binding', + 'pa_body-type', + 'pa_bore-x-stroke-mm', + 'pa_box-cargo', + 'pa_brakes', + 'pa_brand', + 'pa_brands', + 'pa_bus', + 'pa_c', + 'pa_cabin-to-end', + 'pa_capacity', + 'pa_catalognumberlist', + 'pa_ce-keurmerk', + 'pa_chassis-front', + 'pa_chassis-rear', + 'pa_chassis-weight-kg', + 'pa_chip-log', + 'pa_clothing-size', + 'pa_clutch', + 'pa_clutch-type', + 'pa_collection', + 'pa_color', + 'pa_colors', + 'pa_colour', + 'pa_compactor', + 'pa_condition', + 'pa_cor', + 'pa_couleur', + 'pa_country', + 'pa_countryregion-of-manufacture', + 'pa_crane', + 'pa_creator', + 'pa_culoare', + 'pa_customerpackagetype', + 'pa_depan', + 'pa_depan-belakang', + 'pa_department', + 'pa_design', + 'pa_diameter', + 'pa_diameter-cakram', + 'pa_dimension-mm', + 'pa_dimensions', + 'pa_director', + 'pa_disc-diameter', + 'pa_drive-system', + 'pa_dump', + 'pa_ean', + 'pa_eanlist', + 'pa_edition', + 'pa_electric-battery', + 'pa_engine-model', + 'pa_engine-size', + 'pa_ethnicity', + 'pa_exhaust-brake', + 'pa_fabric', + 'pa_farbe', + 'pa_farg', + 'pa_farge', + 'pa_features', + 'pa_final-gear-ratio', + 'pa_finish', + 'pa_fire-fighting', + 'pa_fits', + 'pa_flat-bed', + 'pa_flavour', + 'pa_format', + 'pa_fragrance', + 'pa_frame', + 'pa_front', + 'pa_front-overhang', + 'pa_front-rear', + 'pa_front-tread', + 'pa_fuel-tank', + 'pa_fuel-type', + 'pa_garantie', + 'pa_geadviseerd-accu-type', + 'pa_gear-ratio', + 'pa_gender', + 'pa_genre', + 'pa_gewicht-exclusief-accu', + 'pa_gift-card-amount', + 'pa_grade-ability-tan-o', + 'pa_groesse', + 'pa_gtin', + 'pa_gvwr-gcwr', + 'pa_hardwareplatform', + 'pa_hazardousmaterialtype', + 'pa_height', + 'pa_hekmotor-of-boegmotor', + 'pa_helmet-size', + 'pa_hersteller', + 'pa_high-blow-tank', + 'pa_hoehe', + 'pa_inhoud', + 'pa_isadultproduct', + 'pa_isbn', + 'pa_iseligiblefortradein', + 'pa_itemdimensions', + 'pa_itempartnumber', + 'pa_kemudi-tipe', + 'pa_kleur', + 'pa_kopling-tipe', + 'pa_label', + 'pa_languages', + 'pa_lbs', + 'pa_legaldisclaimer', + 'pa_lengte-aansluitkabel', + 'pa_length', + 'pa_liquid-tank', + 'pa_location', + 'pa_losse-motor-complete-set', + 'pa_maat', + 'pa_main-brake', + 'pa_make', + 'pa_manufacturer', + 'pa_manufacturer-part-number', + 'pa_manufacturermaximumage', + 'pa_manufacturerminimumage', + 'pa_manufacturerpartswarrantydesc', + 'pa_masseinheit', + 'pa_material', + 'pa_mau-sac', + 'pa_maximum-power-ps-rpm', + 'pa_maximum-speed', + 'pa_maximum-torque-kgm-rpm', + 'pa_mediatype', + 'pa_megethos', + 'pa_merk', + 'pa_metal-type', + 'pa_min-turning-circle', + 'pa_mixer', + 'pa_model', + 'pa_model-tipe', + 'pa_model-type', + 'pa_modelo', + 'pa_mount', + 'pa_mpn', + 'pa_nicotine-strength', + 'pa_nos-of-cylinder', + 'pa_nos-of-tire', + 'pa_numberofdiscs', + 'pa_numberofitems', + 'pa_numberofpages', + 'pa_offset', + 'pa_open-cargo', + 'pa_operatingsystem', + 'pa_options', + 'pa_other-part-number', + 'pa_overall-height', + 'pa_overall-length', + 'pa_overall-width', + 'pa_overview', + 'pa_packagedimensions', + 'pa_packagequantity', + 'pa_pages', + 'pa_parking-brake', + 'pa_part-number', + 'pa_partnumber', + 'pa_pattern', + 'pa_pattern2', + 'pa_performa', + 'pa_pictureformat', + 'pa_pin-size', + 'pa_piston-displacement-cc', + 'pa_ploshhad', + 'pa_plug-type', + 'pa_power', + 'pa_product', + 'pa_productgroup', + 'pa_producttypename', + 'pa_publicationdate', + 'pa_publisher', + 'pa_quantity', + 'pa_rear', + 'pa_rear-overhang', + 'pa_rear-tread', + 'pa_refrigerated-box', + 'pa_region', + 'pa_regioncode', + 'pa_releasedate', + 'pa_rem-parkir', + 'pa_rem-pelambat', + 'pa_rem-utama', + 'pa_reverse', + 'pa_runningtime', + 'pa_scent', + 'pa_schachtlengte', + 'pa_seeds', + 'pa_series', + 'pa_setting', + 'pa_sex', + 'pa_shape', + 'pa_shirt-size', + 'pa_size', + 'pa_sizes', + 'pa_sku', + 'pa_sky-lift', + 'pa_sleeve-length', + 'pa_snelheidsregeling', + 'pa_staart', + 'pa_steering', + 'pa_steering-type', + 'pa_storlek', + 'pa_studio', + 'pa_stuwkracht-lbs', + 'pa_style', + 'pa_suspensions', + 'pa_taille', + 'pa_talla', + 'pa_tamanho', + 'pa_tamano', + 'pa_taxi', + 'pa_ticket-type', + 'pa_tire-size', + 'pa_total-chassis-weight', + 'pa_towing-truck', + 'pa_tradeinvalue', + 'pa_trailer-t-head', + 'pa_transmisi-tipe', + 'pa_transmission', + 'pa_transmission-type', + 'pa_types', + 'pa_ukuran', + 'pa_upc', + 'pa_upclist', + 'pa_variation', + 'pa_vehicle-carrier', + 'pa_vergelijkbaar-stuwkracht', + 'pa_vermogen', + 'pa_voltage', + 'pa_volume', + 'pa_warranty', + 'pa_weight', + 'pa_wheel-base', + 'pa_wheel-configuration', + 'pa_wheel-disc-size', + 'pa_width', + 'pa_zout-water-geschikt', + 'product', + 'product-category', + 'product_brand', + 'product_delivery_time', + 'product_delivery_times', + 'product_price_label', + 'product_sale_labels', + 'product_shipping_class', + 'product_tag', + 'product_type', + 'product_unit', + 'product_visibility', + 'products', + + // wp-job-manager. + 'job-category', + 'job-location', + 'job-type', + 'job_cat', + 'job_category', + 'job_listing_category', + 'job_listing_label', + 'job_listing_region', + 'job_listing_tag', + 'job_listing_type', + 'job_salary', + 'job_tag', + 'job_type', + 'jobman_category', + 'jobpost_category', + 'jobpost_job_type', + 'jobpost_location', + 'resume_category', + 'resume_groups', + 'resume_job_type', + 'resume_job_type', + 'resume_languages', + 'resume_region', + 'resume_skill', + 'resume_specialities', + + // generic. + '_resource', + 'acadp_categories', + 'acadp_locations', + 'action-group', + 'activity', + 'actor', + 'actors', + 'ad-group', + 'adace-ad-group', + 'adace-sponsor', + 'additional_features', + 'adv_location', + 'advanced_ads_groups', + 'advert_category', + 'affcoups_coupon_category', + 'affcoups_coupon_type', + 'ai_log_context', + 'ai_log_level', + 'al_product-cat', + 'aol_ad_category', + 'aol_ad_location', + 'aol_ad_type', + 'aol_application_status', + 'area', + 'article-slug', + 'asgarosforum-category', + 'asgarosforum-usergroup', + 'attachment_category', + 'attachment_tag', + 'atum_location', + 'avhec_catgroup', + 'bartype', + 'baths', + 'beds', + 'bepro_listing_types', + 'blog_category', + 'booked_custom_calendars', + 'brand', + 'brands', + 'business_category', + 'bwg_tag', + 'byline', + 'calendar_category', + 'calendar_feed', + 'calendar_type', + 'campaign_category', + 'campaign_tag', + 'carousel_cat', + 'carousels_category', + 'case27_job_listing_tags', + 'categories', + 'category_media', + 'category_portfolio', + 'celebrity_cat', + 'chapters', + 'chronosly_category', + 'city', + 'classified_listing_type', + 'client-types', + 'clients_groups', + 'cm-business-category', + 'cmdm_category', + 'cn_log_type', + 'coderevolution_post_source', + 'collection', + 'community', + 'companies', + 'company', + 'cont_category', + 'content_audit', + 'country', + 'course', + 'course-cat', + 'course-category', + 'course_cat', + 'course_category', + 'course_difficulty', + 'course_tag', + 'courses_type', + 'cp_campaign', + 'cp_recipe_category', + 'csco_post_featured', + 'ct_status', + 'ctl-stories', + 'cuisine', + 'dc_vendor_shop', + 'ddownload_category', + 'ddownload_tag', + 'dealstore', + 'department', + 'departments', + 'department-company', + 'developed-by', + 'dfads_group', + 'dgfw_gift_categories', + 'director', + 'district', + 'dlm_download_category', + 'dlm_download_tag', + 'doc_tag', + 'document-category', + 'download_artist', + 'download_category', + 'download_tag', + 'downloads_filter', + 'dps_book', + 'dt_gallery_category', + 'dt_logos_category', + 'dt_portfolio_category', + 'dt_team_category', + 'dt_testimonials_category', + 'dtcast', + 'dtcreator', + 'dtdirector', + 'dtnetworks', + 'dtstudio', + 'dtyear', + 'dvteamtaxonomy', + 'dwqa-question_category', + 'dwqa-question_tag', + 'eafl_category', + 'easy-testimonial-category', + 'ecwd_event_category', + 'edd_log_type', + 'edition', + 'ef_editorial_meta', + 'ef_usergroup', + 'element_category', + 'elementor_library_type', + 'employees_category', + 'encyclopedia-tag', + 'envira-tag', + 'epkb_post_type_1_category', + 'espresso_event_categories', + 'espresso_event_type', + 'essential_grid_category', + 'et_post_format', + 'faq-group', + 'faq-tags', + 'faq-topic', + 'faq_cat', + 'faq_categories', + 'faq_category', + 'faqs-category', + 'fdm-menu-section', + 'feature', + 'featured_item_category', + 'featured_item_tag', + 'feedback_type', + 'feeds', + 'fl-builder-template-type', + 'flamingo_inbound_channel', + 'follow_up_email_campaign', + 'follow_up_email_type', + 'following_users', + 'football-team-taxo', + 'fpd_design_category', + 'gallery-category', + 'gallery_cat', + 'gallery_categories', + 'gallery_category', + 'gallery_entries', + 'gallerycat', + 'gd_event_tags', + 'gd_eventcategory', + 'gd_place_tags', + 'gd_placecategory', + 'genre', + 'genres', + 'gg_connect_hub', + 'give_log_type', + 'gn-genre', + 'gn-location-1', + 'gn-location-2', + 'gn-location-3', + 'gp_hubs', + 'gp_portfolios', + 'gp_videos', + 'group', + 'group-documents-category', + 'groups', + 'hashtags', + 'hotel_facility', + 'ia_invited_groups', + 'ia_invitees', + 'incsub_wiki_category', + 'industry', + 'ingredient', + 'issue', + 'issuem_issue', + 'issuem_issue_tags', + 'jbp_category', + 'karma-slider-category', + 'klaviyo_shop_cart_status', + 'kwlogos-carousel', + 'layout_category', + 'layout_type', + 'ld_course_category', + 'ld_course_tag', + 'ld_lesson_category', + 'ld_lesson_tag', + 'ld_topic_tag', + 'lesson-tag', + 'level', + 'lingotek_hash', + 'lingotek_profile', + 'link_library_category', + 'linkage', + 'list-tags', + 'listing-category', + 'listing_amenities', + 'listing_category', + 'liveblog', + 'llms_access_plan_visibility', + 'llms_product_visibility', + 'localisation', + 'location', + 'location-tag', + 'locations', + 'magazine', + 'map_location_categories', + 'masonry_gallery_category', + 'mc-event-category', + 'mec_category', + 'mec_location', + 'mec_organizer', + 'media-category', + 'media-tags', + 'media_category', + 'media_folder', + 'member_cat', + 'mentions', + 'mesh_template_types', + 'ml-slider', + 'module', + 'module-tag', + 'module_width', + 'movie_cat', + 'mpp-component', + 'mpp-status', + 'mpp-type', + 'muvicast', + 'muvicountry', + 'muvidirector', + 'muviindex', + 'muviquality', + 'muviyear', + 'news-category', + 'news-tag', + 'news_category', + 'nova_menu', + 'nova_menu_item_label', + 'offer-types', + 'organization', + 'our_team_category', + 'page_category', + 'parisrestaurant', + 'parissauna', + 'partner_category', + 'partners', + 'paswdestinatari', + 'paypal_ipn_type', + 'pdf_lv_tag', + 'pec_events_category', + 'people', + 'people-expertise', + 'people-location', + 'perfect_quotes_category', + 'performer', + 'person', + 'personnal-category', + 'pexcontentslider_category', + 'pexfullslider_category', + 'pexnivoslider_category', + 'pexpricing_category', + 'pexservice_category', + 'pextestimonial_category', + 'pf_feed_item_tag', + 'pg_sas_type', + 'photo_tag', + 'phototype', + 'pj-categs', + 'pj-tags', + 'pl-categs', + 'placement', + 'plan_status', + 'platform', + 'player', + 'plugins_categories', + 'podcast', + 'pojo_sidebars', + 'popup_category', + 'pornstars', + 'portada', + 'portcat', + 'portfolio-category', + 'portfolio-gallery', + 'portfolio-skills', + 'portfolio-tag', + 'portfolio-tags', + 'portfolio-type', + 'portfolio-types', + 'portfolio_cat', + 'portfolio_categories', + 'portfolio_category', + 'portfolio_cats', + 'portfolio_client', + 'portfolio_entries', + 'portfolio_filter', + 'portfolio_in', + 'portfolio_label', + 'portfolio_skills', + 'portfolio_tag', + 'portfolio_tags', + 'portfolio_type', + 'posicao', + 'post-type', + 'post_format', + 'post_series', + 'pp_editorial_meta', + 'pp_notify_role', + 'pp_usergroup', + 'pricingcats', + 'print_section', + 'print_status', + 'programs', + 'project-attributes', + 'project-cat', + 'project-category', + 'project-type', + 'project_category', + 'project_tag', + 'projects_category', + 'projects_tag', + 'prominence', + 'promotion-categories', + 'property-city', + 'property-feature', + 'property-status', + 'property-type', + 'property-types', + 'property_action_category', + 'property_area', + 'property_category', + 'property_city', + 'property_feature', + 'property_status', + 'property_type', + 'province', + 'provinces', + 'publisher', + 'pwb-brand', + 'qmn_log_type', + 'qualification', + 'quality', + 'question-category', + 'question-tag', + 'question-type', + 'question_cat', + 'question_category', + 'question_tag', + 'quiz', + 'quiz-type', + 'quote_status', + 'rating', + 'reaction', + 'recipe-category', + 'recipe_category', + 'recipe_type', + 'region', + 'registrant-event', + 'related_keywords', + 'release-date', + 'resource-type', + 'resource_category', + 'resource_type', + 'resourcetype', + 'review-type', + 'review_category', + 'rodzaj', + 'role', + 'room_category', + 'room_tag', + 'roomtype', + 'rubriek_categorie', + 'savedreply', + 'schools', + 'scope', + 'scores_cat', + 'sdm_categories', + 'sdm_tags', + 'season', + 'secondary_html_features', + 'section', + 'sector', + 'series', + 'series_of_posts', + 'services_group', + 'serving', + 'shop_cart_status', + 'shop_cat', + 'shop_order_status', + 'shop_vendor', + 'shop_warranty_status', + 'shopp_category', + 'shopr_category', + 'show', + 'simple_link_category', + 'site-review-category', + 'sizes', + 'skill', + 'skill_level', + 'skills', + 'sld_cat', + 'slide-page', + 'slide-types', + 'slide_categories', + 'slide_type', + 'slider', + 'slider-locations', + 'slider_category', + 'slides_category', + 'slideshow', + 'sm-category', + 'snax_format', + 'sngg_media_tags', + 'solution_channel', + 'source_domain', + 'source_id', + 'sp_league', + 'sp_position', + 'sp_role', + 'sp_season', + 'sp_venue', + 'speaker', + 'speakers', + 'special-feature', + 'specialty', + 'spnl_log_type', + 'sponsor_categories', + 'sponsor_category', + 'sponsor_type', + 'spot_tag', + 'st_af_category', + 'st_af_tags', + 'staff', + 'staff-member-category', + 'staff-member-group', + 'staff_category', + 'staffgroups', + 'state', + 'status', + 'store', + 'stores', + 'studio', + 'study_level', + 'style', + 'style_category', + 'sub_transaction_action', + 'sub_transaction_result', + 'subcategory', + 'subject', + 'subscription_status', + 'swift-slider-category', + 'syn_sitegroup', + 'szbl-content-tag', + 'task-queue', + 'tax_feature', + 'tcb_symbols_tax', + 'tcp_product_category', + 'team', + 'team-category', + 'team_cat', + 'team_categories', + 'team_category', + 'team_cats', + 'team_department', + 'team_designation', + 'team_group', + 'team_member_position', + 'team_mfcategory', + 'teams', + 'tenant_categories', + 'tenant_location', + 'tender-category', + 'test-type', + 'testimonial-category', + 'testimonial-group', + 'testimonial-types', + 'testimonial_categories', + 'testimonial_category', + 'testimonials-category', + 'testimonials_category', + 'th_events_cat', + 'th_galleries_cat', + 'thegem_clients_sets', + 'thegem_news_sets', + 'thegem_portfolios', + 'thegem_quickfinders', + 'thegem_teams', + 'thegem_testimonials_sets', + 'theme', + 'themefusion_es_groups', + 'themes_categories', + 'themo_cpt_group', + 'themo_project_type', + 'themo_room_type', + 'thirstylink-category', + 'ticket_channel', + 'ticket_priority', + 'timeline_post_tag', + 'tipo', + 'tipologie', + 'tips', + 'tm-testimonials_category', + 'tm_testimonial_group', + 'tooltips_categories', + 'tour_category', + 'tour_destination', + 'tour_facility', + 'tour_phys', + 'tour_type', + 'tp_event_category', + 'transmission', + 'treatment-type', + 'tribe_events_cat', + 'truethemes-gallery-category', + 'tsas-category', + 'tshowcase-categories', + 'tsml_region', + 'ttshowcase_groups', + 'tvo_tags', + 'type', + 'types', + 'u_course_cat', + 'u_department', + 'u_event_cat', + 'ufaq-category', + 'ufaq-tag', + 'um_hashtag', + 'um_user_tag', + 'uncodeblock_category', + 'upg_cate', + 'urp-review-category', + 'us_portfolio_category', + 'us_testimonial_category', + 'user-group', + 'user_category', + 'user_status', + 'vendor', + 'venue', + 'video-category', + 'video-series', + 'video-tag', + 'video_category', + 'video_tag', + 'videos', + 'videos_categories', + 'voice_category', + 'vtmin_rule_category', + 'vtprd_rule_category', + 'w2dc-category', + 'w2dc-location', + 'w2dc-tag', + 'wcb_sponsor_level', + 'wcb_track', + 'wccf_checkout_field_field_type', + 'wccf_checkout_field_status', + 'wccf_order_field_field_type', + 'wccf_order_field_status', + 'wccf_product_field_field_type', + 'wccf_product_field_status', + 'wccf_product_prop_field_type', + 'wccf_product_prop_status', + 'wccf_user_field_field_type', + 'wccf_user_field_status', + 'wcfm_knowledgebase_category', + 'wcm_task_category', + 'wcpv_product_vendors', + 'wcs-instructor', + 'wcs-room', + 'wcs-type', + 'wdca_ad_categories', + 'where', + 'who', + 'wiki-category', + 'wiki_cats', + 'wl_entity_type', + 'workout_entries', + 'works-category', + 'wp-rest-api-log-method', + 'wp-rest-api-log-source', + 'wp-rest-api-log-status', + 'wp-type-activity-types', + 'wp-type-contacts-subtype', + 'wp-type-group', + 'wp_bannerize_tax', + 'wp_log_type', + 'wp_super_faq_category', + 'wpbdm-region', + 'wpbdp_category', + 'wpbdp_tag', + 'wpcm_make_model', + 'wpdmcategory', + 'wpfb_file_category', + 'wpfcas-category', + 'wpfd-category', + 'wplead_list_category', + 'wplss_logo_showcase_cat', + 'wpm-testimonial-category', + 'wpmf-category', + 'wpostahs-slider-category', + 'wprm_course', + 'wprm_cuisine', + 'wprm_ingredient', + 'wprm_keyword', + 'wprss_category', + 'wps_forum', + 'wpsc-variation', + 'wpsc_log_type', + 'wpsc_product_category', + 'wpseo_locations_category', + 'wpsisac_slider-category', + 'wpsl_store_category', + 'wpt_category', + 'wpt_result', + 'wpt_scale', + 'wpv_sermons_category', + 'wpvqgr_tag', + 'writer', + 'wyz_business_category', + 'wyz_business_rating_category', + 'wyz_business_tag', + 'wzkb_category', + 'year', + 'years', + 'yith_product_brand', + 'yith_shop_vendor', + 'yst_prominent_words', + 'zipcode', + 'zoninator_zones', + 'zrf_field_group', + + // End The Backlog @see https://wp.me/p9MPsk-X0. + 'bill-status', + 'etb-audience', + 'etb-state', + 'etb-target', + 'etb-topic', + 'etb-year', + 'foia-response-status', + 'target-type', + 'timeline-pillar', + 'timeline-type', + + ); // end taxonomies. + + /** + * List of options to sync + * + * @access private + * @static + * + * @var array + */ + private static $options_to_sync = array( + 'jetpack_search_color_theme', + 'jetpack_search_result_format', + 'jetpack_search_default_sort', + 'jetpack_search_overlay_trigger', + 'jetpack_search_excluded_post_types', + 'jetpack_search_highlight_color', + 'jetpack_search_enable_sort', + 'jetpack_search_inf_scroll', + 'jetpack_search_show_powered_by', + 'instant_search_enabled', + ); // end options. + + /* + * Taxonomies we know don't sync. + * See also sync/src/class-defaults.php + * + * 'network' + * 'post_status' + * 'product_cat' + * 'tags' + * + */ + + // + // Hooks into sync. + + /** + * Add Search post meta to the post meta whitelist. + * + * @param array $list Existing post meta whitelist. + * @return array Updated post meta whitelist. + */ + public function add_search_post_meta_whitelist( $list ) { + return array_merge( $list, $this->get_all_postmeta_keys() ); + } + + /** + * Add Search options to the options whitelist. + * + * @param array $list Existing options whitelist. + * @return array Updated options whitelist. + */ + public function add_search_options_whitelist( $list ) { + return array_merge( $list, $this->get_all_option_keys() ); + } + + // + // Indexing functions for wp.com. + + /** + * + * Check whether a postmeta or taxonomy 'key' is in the indexable + * list. This is called by the indexing code on wp.com to decide + * whether to include something in the index. + * + * @static + * @access public + * + * @param string $type Either 'postmeta' or 'taxonomy'. + * @param string $key The postmeta key or taxonomy name. + * @return boolean + */ + public static function is_indexable( $type, $key ) { + switch ( $type ) { + case 'postmeta': + return isset( self::$postmeta_to_sync[ $key ] ); + case 'taxonomy': + return in_array( $key, self::$taxonomies_to_sync, true ); + } + return false; + } + + /** + * + * Get the indexing spec for a postmeta key. + * + * @static + * @access public + * + * @param string $key The postmeta key. + * @return array The spec. + */ + public static function get_postmeta_spec( $key ) { + return self::$postmeta_to_sync[ $key ]; + } + + /** + * Get all post meta keys that get synced. + * + * @access public + * + * @return array List of post meta keys that get synced. + */ + public static function get_all_postmeta_keys() { + return array_keys( self::$postmeta_to_sync ); + } + + /** + * Get all option keys that get synced. + * + * @access public + * + * @return array List of option keys that get synced. + */ + public static function get_all_option_keys() { + return self::$options_to_sync; + } + + /** + * Get all unindexed postmeta. + * This is mostly for testing. + * + * @access public + * + * @return array List of postmeta that are not synced. + */ + public static function get_all_unindexed_postmeta_keys() { + return array_keys( self::$unindexed_postmeta ); + } + + /** + * Get all taxonomies that get synced. + * This is mostly for testing. + * + * @access public + * + * @return array List of taxonomies that get synced. + */ + public static function get_all_taxonomies() { + return self::$taxonomies_to_sync; + } + +} |