diff --git a/plugins/woocommerce/changelog/add-is-allowed-remote-logging b/plugins/woocommerce/changelog/add-is-allowed-remote-logging new file mode 100644 index 00000000000..67447b48cfc --- /dev/null +++ b/plugins/woocommerce/changelog/add-is-allowed-remote-logging @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add a generic function to determine remote logging eligibility diff --git a/plugins/woocommerce/includes/class-wc-remote-logger.php b/plugins/woocommerce/includes/class-wc-remote-logger.php new file mode 100644 index 00000000000..1145d4b17e8 --- /dev/null +++ b/plugins/woocommerce/includes/class-wc-remote-logger.php @@ -0,0 +1,111 @@ +is_tracking_opted_in() ) { + return false; + } + + if ( ! $this->is_variant_assignment_allowed() ) { + return false; + } + + if ( ! $this->is_latest_woocommerce_version() ) { + return false; + } + + return true; + } + + /** + * Check if the user has opted into tracking/logging. + * + * @return bool + */ + private function is_tracking_opted_in() { + return 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ); + } + + /** + * Check if the store is allowed to log based on the variant assignment percentage. + * + * @return bool + */ + private function is_variant_assignment_allowed() { + $assignment = get_option( 'woocommerce_remote_variant_assignment', 0 ); + return ( $assignment <= 12 ); // Considering 10% of the 0-120 range. + } + + /** + * Check if the current WooCommerce version is the latest. + * + * @return bool + */ + private function is_latest_woocommerce_version() { + $latest_wc_version = $this->fetch_latest_woocommerce_version(); + + if ( is_null( $latest_wc_version ) ) { + return false; + } + + return version_compare( WC()->version, $latest_wc_version, '>=' ); + } + + /** + * Fetch the latest WooCommerce version using the WordPress API and cache it. + * + * @return string|null + */ + private function fetch_latest_woocommerce_version() { + $transient_key = 'latest_woocommerce_version'; + $cached_version = get_transient( $transient_key ); + if ( $cached_version ) { + return $cached_version; + } + + if ( ! function_exists( 'plugins_api' ) ) { + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + } + + $plugin_info = plugins_api( 'plugin_information', array( 'slug' => 'woocommerce' ) ); + if ( is_wp_error( $plugin_info ) ) { + return null; + } + + if ( ! empty( $plugin_info->version ) ) { + $latest_version = $plugin_info->version; + set_transient( $transient_key, $latest_version, DAY_IN_SECONDS ); + return $latest_version; + } + + return null; + } +} diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php index c41a98c933e..5458f66576e 100644 --- a/plugins/woocommerce/includes/class-woocommerce.php +++ b/plugins/woocommerce/includes/class-woocommerce.php @@ -29,7 +29,6 @@ use Automattic\WooCommerce\Internal\Admin\Marketplace; use Automattic\WooCommerce\Proxies\LegacyProxy; use Automattic\WooCommerce\Utilities\{LoggingUtil, RestApiUtil, TimeUtil}; use Automattic\WooCommerce\Admin\WCAdminHelper; -use Automattic\WooCommerce\Admin\Features\Features; /** * Main WooCommerce Class. @@ -647,6 +646,7 @@ final class WooCommerce { include_once WC_ABSPATH . 'includes/class-wc-structured-data.php'; include_once WC_ABSPATH . 'includes/class-wc-shortcodes.php'; include_once WC_ABSPATH . 'includes/class-wc-logger.php'; + include_once WC_ABSPATH . 'includes/class-wc-remote-logger.php'; include_once WC_ABSPATH . 'includes/queue/class-wc-action-queue.php'; include_once WC_ABSPATH . 'includes/queue/class-wc-queue.php'; include_once WC_ABSPATH . 'includes/admin/marketplace-suggestions/class-wc-marketplace-updater.php'; diff --git a/plugins/woocommerce/src/Internal/Features/FeaturesController.php b/plugins/woocommerce/src/Internal/Features/FeaturesController.php index c7b33c14e36..c332e769f65 100644 --- a/plugins/woocommerce/src/Internal/Features/FeaturesController.php +++ b/plugins/woocommerce/src/Internal/Features/FeaturesController.php @@ -247,6 +247,17 @@ class FeaturesController { 'is_legacy' => true, 'option_key' => CustomOrdersTableController::HPOS_FTS_INDEX_OPTION, ), + 'remote_logging' => array( + 'name' => __( 'Remote Logging', 'woocommerce' ), + 'description' => __( + 'Enable this feature to log errors and related data to Automattic servers for debugging purposes and to improve WooCommerce', + 'woocommerce' + ), + 'enabled_by_default' => false, + 'disable_ui' => true, + 'is_legacy' => false, + 'is_experimental' => true, + ), ); foreach ( $legacy_features as $slug => $definition ) { diff --git a/plugins/woocommerce/tests/php/includes/class-wc-remote-logger.php b/plugins/woocommerce/tests/php/includes/class-wc-remote-logger.php new file mode 100644 index 00000000000..eca1185078e --- /dev/null +++ b/plugins/woocommerce/tests/php/includes/class-wc-remote-logger.php @@ -0,0 +1,184 @@ +version = '9.2.0'; + } + + /** + * Tear down. + * + * @return void + */ + public function tearDown(): void { + $this->cleanup_filters(); + + delete_option( 'woocommerce_feature_remote_logging_enabled' ); + } + + /** + * Clean up filters. + * + * @return void + */ + private function cleanup_filters() { + remove_all_filters( 'option_woocommerce_admin_remote_feature_enabled' ); + remove_all_filters( 'option_woocommerce_allow_tracking' ); + remove_all_filters( 'option_woocommerce_version' ); + remove_all_filters( 'option_woocommerce_remote_variant_assignment' ); + } + + /** + * Test that remote logging is allowed when all conditions are met. + */ + public function test_remote_logging_allowed() { + update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' ); + + add_filter( + 'option_woocommerce_allow_tracking', + function () { + return 'yes'; + } + ); + add_filter( + 'option_woocommerce_remote_variant_assignment', + function () { + return 5; + } + ); + + add_filter( + 'plugins_api', + function ( $result, $action, $args ) { + if ( 'plugin_information' === $action && 'woocommerce' === $args->slug ) { + return (object) array( + 'version' => '9.2.0', + ); + } + return $result; + }, + 10, + 3 + ); + + $checker = new WC_Remote_Logger(); + $this->assertTrue( $checker->is_remote_logging_allowed() ); + } + + /** + * Test that remote logging is not allowed when the feature flag is disabled. + */ + public function test_remote_logging_not_allowed_feature_flag_disabled() { + update_option( 'woocommerce_feature_remote_logging_enabled', 'no' ); + + add_filter( + 'option_woocommerce_allow_tracking', + function () { + return 'yes'; + } + ); + add_filter( + 'option_woocommerce_remote_variant_assignment', + function () { + return 5; + } + ); + + set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS ); + + $checker = new WC_Remote_Logger(); + $this->assertFalse( $checker->is_remote_logging_allowed() ); + } + + /** + * Test that remote logging is not allowed when user tracking is not opted in. + */ + public function test_remote_logging_not_allowed_tracking_opted_out() { + update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' ); + add_filter( + 'option_woocommerce_allow_tracking', + function () { + return 'no'; + } + ); + add_filter( + 'option_woocommerce_remote_variant_assignment', + function () { + return 5; + } + ); + + set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS ); + + $checker = new WC_Remote_Logger(); + $this->assertFalse( $checker->is_remote_logging_allowed() ); + } + + /** + * Test that remote logging is not allowed when the WooCommerce version is outdated. + */ + public function test_remote_logging_not_allowed_outdated_version() { + update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' ); + add_filter( + 'option_woocommerce_allow_tracking', + function () { + return 'yes'; + } + ); + add_filter( + 'option_woocommerce_remote_variant_assignment', + function () { + return 5; + } + ); + + set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS ); + WC()->version = '9.0.0'; + + $checker = new WC_Remote_Logger(); + $this->assertFalse( $checker->is_remote_logging_allowed() ); + } + + /** + * Test that remote logging is not allowed when the variant assignment is high. + */ + public function test_remote_logging_not_allowed_high_variant_assignment() { + update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' ); + add_filter( + 'option_woocommerce_allow_tracking', + function () { + return 'yes'; + } + ); + add_filter( + 'option_woocommerce_version', + function () { + return '9.2.0'; + } + ); + add_filter( + 'option_woocommerce_remote_variant_assignment', + function () { + return 15; + } + ); + + set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS ); + + $checker = new WC_Remote_Logger(); + $this->assertFalse( $checker->is_remote_logging_allowed() ); + } +}