From 7ba97939f8f557ea095192756045c26ce9acd8e0 Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 15:44:19 -0500 Subject: [PATCH 1/9] Newsletter Categories: add helper class --- ...ass-jetpack-newsletter-category-helper.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php diff --git a/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php b/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php new file mode 100644 index 0000000000000..544d437d57001 --- /dev/null +++ b/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php @@ -0,0 +1,37 @@ + Date: Tue, 29 Jul 2025 22:01:52 -0500 Subject: [PATCH 2/9] Use helper class across Jetpack --- ...ass-jetpack-newsletter-category-helper.php | 73 ++++++++++++++++--- ...lass.jetpack-core-api-module-endpoints.php | 51 ++++--------- ....wpcom-json-api-site-settings-endpoint.php | 62 +++------------- 3 files changed, 90 insertions(+), 96 deletions(-) diff --git a/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php b/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php index 544d437d57001..96488541c8c15 100644 --- a/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php +++ b/projects/plugins/jetpack/_inc/lib/class-jetpack-newsletter-category-helper.php @@ -10,28 +10,81 @@ */ class Jetpack_Newsletter_Category_Helper { - const NEWSLETTER_CATEGORIES_OPTION = 'wpcom_newsletter_categories'; - const NEWSLETTER_CATEGORIES_ENABLED_OPTION = 'wpcom_newsletter_categories_enabled'; + const NEWSLETTER_CATEGORIES_OPTION = 'wpcom_newsletter_categories'; /** * Return category ID's * - * @param array $newsletter_categories An array of id's that could be in a few different forms. * @return array An array of integers - * [123, 456] */ - public static function parse_category_ids( $newsletter_categories ) { - return $newsletter_categories; + public static function get_category_ids() { + $newsletter_categories = maybe_unserialize( get_option( 'wpcom_newsletter_categories', array() ) ); + + if ( ! is_array( $newsletter_categories ) || empty( $newsletter_categories ) ) { + return array(); + } + + // Check if it is an array of integers. + // [123, 456] + if ( isset( $newsletter_categories[0] ) && is_int( $newsletter_categories[0] ) ) { + return $newsletter_categories; + } + + // Check if it is an array of arrays with term_id keys. + // [{term_id: 123}, {term_id: 456}] + if ( isset( $newsletter_categories[0] ) && is_array( $newsletter_categories[0] ) && isset( $newsletter_categories[0]['term_id'] ) ) { + $ids = array(); + foreach ( $newsletter_categories as $category ) { + if ( isset( $category['term_id'] ) && is_numeric( $category['term_id'] ) ) { + $ids[] = (int) $category['term_id']; + } + } + return $ids; + } + + return array(); } /** - * Return category ID's ready to be saved as an option + * Handles category ID's ready to be saved as an option * * @param array $newsletter_categories An array of id's that could be in a few different forms. - * @return array An associated array with term_id keys. + * @return array|bool An associated array with term_id keys on success, or the boolean result from update_option. * [{term_id: 123}, {term_id: 456}] */ - public static function prepare_category_ids( $newsletter_categories ) { - return $newsletter_categories; + public static function save_category_ids( $newsletter_categories ) { + if ( ! is_array( $newsletter_categories ) || empty( $newsletter_categories ) ) { + return false; + } + + $formatted_categories = array(); + + // Check if it is an array of integers. + // [123, 456] + if ( isset( $newsletter_categories[0] ) && is_numeric( $newsletter_categories[0] ) ) { + foreach ( $newsletter_categories as $id ) { + if ( is_numeric( $id ) ) { + $formatted_categories[] = array( 'term_id' => (int) $id ); + } + } + } + + // Check if it is an array of arrays with term_id keys. + // [{term_id: 123}, {term_id: 456}] + if ( isset( $newsletter_categories[0] ) && is_array( $newsletter_categories[0] ) && isset( $newsletter_categories[0]['term_id'] ) ) { + foreach ( $newsletter_categories as $category ) { + if ( isset( $category['term_id'] ) && is_numeric( $category['term_id'] ) ) { + $formatted_categories[] = array( 'term_id' => (int) $category['term_id'] ); + } + } + } + + if ( empty( $formatted_categories ) ) { + return false; + } + + return update_option( self::NEWSLETTER_CATEGORIES_OPTION, $formatted_categories ) + ? $formatted_categories + : false; } } diff --git a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php index 280b89e35fb7f..50556a3bbbc30 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php @@ -504,6 +504,12 @@ public function get_all_options() { $response['newsletter_has_active_plan'] = count( Jetpack_Memberships::get_all_newsletter_plan_ids( false ) ) > 0; } + // Make sure we are returning a consistent type + if ( ! class_exists( 'Jetpack_Newsletter_Category_Helper' ) ) { + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-newsletter-category-helper.php'; + } + $response['wpcom_newsletter_categories'] = Jetpack_Newsletter_Category_Helper::get_category_ids(); + return rest_ensure_response( $response ); } @@ -650,6 +656,10 @@ public function update_data( $request ) { } } + if ( ! class_exists( 'Jetpack_Newsletter_Category_Helper' ) ) { + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-newsletter-category-helper.php'; + } + foreach ( $params as $option => $value ) { // Used if there was an error. Can be overwritten with specific error messages. @@ -1050,43 +1060,14 @@ function ( &$value ) { } break; - case 'wpcom_newsletter_categories': - $sanitized_category_ids = (array) $value; - - array_walk_recursive( - $sanitized_category_ids, - function ( &$value ) { - if ( is_int( $value ) && $value > 0 ) { - return; - } - - $value = (int) $value; - if ( $value <= 0 ) { - $value = null; - } - } - ); - - $sanitized_category_ids = array_unique( - array_filter( - $sanitized_category_ids, - function ( $category_id ) { - return $category_id !== null; - } - ) - ); - - $new_value = array_map( - function ( $category_id ) { - return array( 'term_id' => $category_id ); - }, - $sanitized_category_ids - ); - - if ( ! update_option( $option, $new_value ) ) { + case Jetpack_Newsletter_Category_Helper::NEWSLETTER_CATEGORIES_OPTION: + if ( Jetpack_Newsletter_Category_Helper::save_category_ids( (array) $value ) ) { + $updated = true; + } else { $updated = false; - $error = esc_html__( 'WPCOM Newsletter Categories failed to process.', 'jetpack' ); + $error = esc_html__( 'Newsletter catergory did not update.', 'jetpack' ); } + break; default: diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php index bf9bf97532610..4d41134e22d03 100644 --- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php @@ -374,22 +374,11 @@ public function get_settings_response() { ) ); - $newsletter_categories = maybe_unserialize( get_option( 'wpcom_newsletter_categories', array() ) ); - $newsletter_category_ids = array_filter( - array_map( - function ( $newsletter_category ) { - if ( is_array( $newsletter_category ) && isset( $newsletter_category['term_id'] ) ) { - // This is the expected format. - return (int) $newsletter_category['term_id']; - } elseif ( is_numeric( $newsletter_category ) ) { - // This is a previous format caused by a bug. - return (int) $newsletter_category; - } - return null; - }, - $newsletter_categories - ) - ); + // Make sure we are returning a consistent type + if ( ! class_exists( 'Jetpack_Newsletter_Category_Helper' ) ) { + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-newsletter-category-helper.php'; + } + $newsletter_category_ids = Jetpack_Newsletter_Category_Helper::get_category_ids(); $api_cache = $site->is_jetpack() ? (bool) get_option( 'jetpack_api_cache_enabled' ) : true; @@ -673,6 +662,10 @@ public function update_settings() { $sharing_options = array(); $updated = array(); + if ( ! class_exists( 'Jetpack_Newsletter_Category_Helper' ) ) { + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-newsletter-category-helper.php'; + } + foreach ( $input as $key => $value ) { if ( ! is_array( $value ) ) { @@ -1070,42 +1063,9 @@ function ( &$value ) { $updated[ $key ] = (int) (bool) $value; break; - case 'wpcom_newsletter_categories': - $sanitized_category_ids = (array) $value; - - array_walk_recursive( - $sanitized_category_ids, - function ( &$value ) { - if ( is_int( $value ) && $value > 0 ) { - return; - } + case Jetpack_Newsletter_Category_Helper::NEWSLETTER_CATEGORIES_OPTION: + $updated[ $key ] = Jetpack_Newsletter_Category_Helper::save_category_ids( (array) $value ); - $value = (int) $value; - if ( $value <= 0 ) { - $value = null; - } - } - ); - - $sanitized_category_ids = array_unique( - array_filter( - $sanitized_category_ids, - function ( $category_id ) { - return $category_id !== null; - } - ) - ); - - $new_value = array_map( - function ( $category_id ) { - return array( 'term_id' => $category_id ); - }, - $sanitized_category_ids - ); - - if ( update_option( $key, $new_value ) ) { - $updated[ $key ] = $sanitized_category_ids; - } break; case 'wpcom_newsletter_categories_enabled': From 58730e3037f1277be2d826c5629a06e8b7c99b4a Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 22:03:33 -0500 Subject: [PATCH 3/9] changelog --- .../plugins/jetpack/changelog/arcanagelinis-newsletter-fix-v2 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/arcanagelinis-newsletter-fix-v2 diff --git a/projects/plugins/jetpack/changelog/arcanagelinis-newsletter-fix-v2 b/projects/plugins/jetpack/changelog/arcanagelinis-newsletter-fix-v2 new file mode 100644 index 0000000000000..8248ae0157b01 --- /dev/null +++ b/projects/plugins/jetpack/changelog/arcanagelinis-newsletter-fix-v2 @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +Newsletter: fix bug in category settings From 0b36a367cb409c9082abfbbd897617853ac6f80e Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 23:19:15 -0500 Subject: [PATCH 4/9] Add tests --- ...etpack_Newsletter_Category_Helper_Test.php | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php diff --git a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php new file mode 100644 index 0000000000000..91a996f940e08 --- /dev/null +++ b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php @@ -0,0 +1,287 @@ +assertEquals( array(), $result ); + } + + /** + * Test get_category_ids() returns empty array when option is empty. + */ + public function test_get_category_ids_empty_option() { + update_option( 'wpcom_newsletter_categories', array() ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( array(), $result ); + } + + /** + * Test get_category_ids() returns empty array when option is not an array. + */ + public function test_get_category_ids_invalid_option() { + update_option( 'wpcom_newsletter_categories', 'not-an-array' ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( array(), $result ); + } + + /** + * Test get_category_ids() handles array of integers format. + */ + public function test_get_category_ids_integer_array() { + $categories = array( 123, 456, 789 ); + update_option( 'wpcom_newsletter_categories', $categories ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( $categories, $result ); + } + + /** + * Test get_category_ids() handles array of arrays with term_id format. + */ + public function test_get_category_ids_term_id_array() { + $categories = array( + array( 'term_id' => 123 ), + array( 'term_id' => 456 ), + array( 'term_id' => 789 ), + ); + update_option( 'wpcom_newsletter_categories', $categories ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( array( 123, 456, 789 ), $result ); + } + + /** + * Test get_category_ids() filters out non-numeric term_ids. + */ + public function test_get_category_ids_filters_invalid_term_ids() { + $categories = array( + array( 'term_id' => 123 ), + array( 'term_id' => 'not-a-number' ), + array( 'term_id' => 456 ), + ); + update_option( 'wpcom_newsletter_categories', $categories ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( array( 123, 456 ), $result ); + } + + /** + * Test get_category_ids() handles serialized data. + */ + public function test_get_category_ids_serialized_data() { + $categories = array( 123, 456 ); + update_option( 'wpcom_newsletter_categories', serialize( $categories ) ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( $categories, $result ); + } + + /** + * Test save_category_ids() returns false for non-array input. + */ + public function test_save_category_ids_non_array() { + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( 'not-an-array' ); + $this->assertFalse( $result ); + } + + /** + * Test save_category_ids() returns false for empty array. + */ + public function test_save_category_ids_empty_array() { + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( array() ); + $this->assertFalse( $result ); + } + + /** + * Test save_category_ids() formats array of integers correctly. + */ + public function test_save_category_ids_integer_array() { + $input = array( 123, 456, 789 ); + $expected = array( + array( 'term_id' => 123 ), + array( 'term_id' => 456 ), + array( 'term_id' => 789 ), + ); + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $this->assertEquals( $expected, $result ); + + $saved_option = get_option( 'wpcom_newsletter_categories' ); + $this->assertEquals( $expected, $saved_option ); + } + + /** + * Test save_category_ids() handles string numeric values. + */ + public function test_save_category_ids_string_numeric() { + $input = array( '123', '456', '789' ); + $expected = array( + array( 'term_id' => 123 ), + array( 'term_id' => 456 ), + array( 'term_id' => 789 ), + ); + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $this->assertEquals( $expected, $result ); + } + + /** + * Test save_category_ids() filters out non-numeric values. + */ + public function test_save_category_ids_filters_non_numeric() { + $input = array( 123, 'not-a-number', 456 ); + $expected = array( + array( 'term_id' => 123 ), + array( 'term_id' => 456 ), + ); + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $this->assertEquals( $expected, $result ); + } + + /** + * Test save_category_ids() handles array of arrays with term_id. + */ + public function test_save_category_ids_term_id_array() { + $input = array( + array( 'term_id' => 123 ), + array( 'term_id' => 456 ), + array( 'term_id' => 789 ), + ); + $expected = array( + array( 'term_id' => 123 ), + array( 'term_id' => 456 ), + array( 'term_id' => 789 ), + ); + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $this->assertEquals( $expected, $result ); + } + + /** + * Test save_category_ids() returns false when no valid categories. + */ + public function test_save_category_ids_no_valid_categories() { + $input = array( 'not-a-number', 'also-not-a-number' ); + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $this->assertFalse( $result ); + } + + /** + * Test integration between save and get methods. + */ + public function test_save_and_get_integration() { + $input = array( 123, 456, 789 ); + Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + $this->assertEquals( $input, $result ); + } + + /** + * Test that Jetpack_Core_API_Data, WPCOM_JSON_API_Site_Settings_Endpoint, + * and the helper class all return the same newsletter categories. + */ + public function test_api_endpoints_return_same_newsletter_categories() { + global $blog_id; + + // Set up test data. + $test_categories = array( 101, 202, 303 ); + Jetpack_Newsletter_Category_Helper::save_category_ids( $test_categories ); + + // Get categories using the helper directly. + $helper_result = Jetpack_Newsletter_Category_Helper::get_category_ids(); + + // Get categories using Jetpack_Core_API_Data. + if ( ! class_exists( 'Jetpack_Core_API_Data' ) ) { + require_once JETPACK__PLUGIN_DIR . '/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php'; + } + $core_api_data = new Jetpack_Core_API_Data(); + $core_api_response = $core_api_data->get_all_options(); + $core_api_categories = isset( $core_api_response->data['wpcom_newsletter_categories'] ) + ? $core_api_response->data['wpcom_newsletter_categories'] + : array(); + + // Get categories using WPCOM_JSON_API_Site_Settings_V1_4_Endpoint via a mocked request. + // Set up JSON API requirements. + require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php'; + + if ( ! defined( 'WPCOM_JSON_API__BASE' ) ) { + define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1.4' ); + } + + // Set up to test site settings API response. + WPCOM_JSON_API::init()->token_details = array( 'blog_id' => $blog_id ); + $admin = self::factory()->user->create_and_get( + array( + 'role' => 'administrator', + ) + ); + + wp_set_current_user( $admin->ID ); + + if ( ! class_exists( 'WPCOM_JSON_API_Site_Settings_V1_4_Endpoint' ) ) { + require_once JETPACK__PLUGIN_DIR . '/json-endpoints/class.wpcom-json-api-site-settings-v1-4-endpoint.php'; + } + if ( ! class_exists( 'WPCOM_JSON_API_Site_Settings_Endpoint' ) ) { + require_once JETPACK__PLUGIN_DIR . '/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php'; + } + + // Initialize the endpoint with proper configuration. + $endpoint = new WPCOM_JSON_API_Site_Settings_V1_4_Endpoint( + array( + 'description' => 'Get detailed settings information about a site.', + 'group' => '__do_not_document', + 'stat' => 'sites:X', + 'min_version' => '1.4', + 'method' => 'GET', + 'path' => '/sites/%s/settings', + 'path_labels' => array( + '$site' => '(int|string) Site ID or domain', + ), + 'query_parameters' => array( + 'context' => false, + ), + 'response_format' => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format, + ) + ); + + // Make the request using the endpoint's callback method. + $json_api_response = $endpoint->callback( '/sites/%s/settings', $blog_id ); + $json_api_categories = isset( $json_api_response['settings']['wpcom_newsletter_categories'] ) + ? $json_api_response['settings']['wpcom_newsletter_categories'] + : array(); + + // All three should return the same data. + $this->assertEquals( $test_categories, $helper_result, 'Helper should return correct categories' ); + $this->assertEquals( $test_categories, $core_api_categories, 'Core API should return same categories as helper' ); + $this->assertEquals( $test_categories, $json_api_categories, 'JSON API V1.4 should return same categories as helper' ); + } +} From a69489b09db5c28f365517befd5033e5c7045798 Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 23:39:33 -0500 Subject: [PATCH 5/9] fix stuff --- ...lass.jetpack-core-api-module-endpoints.php | 2 +- ....wpcom-json-api-site-settings-endpoint.php | 5 +- ...etpack_Newsletter_Category_Helper_Test.php | 80 ------------------- 3 files changed, 5 insertions(+), 82 deletions(-) diff --git a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php index 50556a3bbbc30..ee5fbcfcc5557 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php @@ -1065,7 +1065,7 @@ function ( &$value ) { $updated = true; } else { $updated = false; - $error = esc_html__( 'Newsletter catergory did not update.', 'jetpack' ); + $error = esc_html__( 'Newsletter category did not update.', 'jetpack' ); } break; diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php index 4d41134e22d03..f07a8eaaf3ebb 100644 --- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php @@ -1064,7 +1064,10 @@ function ( &$value ) { break; case Jetpack_Newsletter_Category_Helper::NEWSLETTER_CATEGORIES_OPTION: - $updated[ $key ] = Jetpack_Newsletter_Category_Helper::save_category_ids( (array) $value ); + $update_newsletter_categories = Jetpack_Newsletter_Category_Helper::save_category_ids( (array) $value ); + if ( $update_newsletter_categories ) { + $updated[ $key ] = $update_newsletter_categories; + } break; diff --git a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php index 91a996f940e08..98192699f99e5 100644 --- a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php +++ b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php @@ -204,84 +204,4 @@ public function test_save_and_get_integration() { $result = Jetpack_Newsletter_Category_Helper::get_category_ids(); $this->assertEquals( $input, $result ); } - - /** - * Test that Jetpack_Core_API_Data, WPCOM_JSON_API_Site_Settings_Endpoint, - * and the helper class all return the same newsletter categories. - */ - public function test_api_endpoints_return_same_newsletter_categories() { - global $blog_id; - - // Set up test data. - $test_categories = array( 101, 202, 303 ); - Jetpack_Newsletter_Category_Helper::save_category_ids( $test_categories ); - - // Get categories using the helper directly. - $helper_result = Jetpack_Newsletter_Category_Helper::get_category_ids(); - - // Get categories using Jetpack_Core_API_Data. - if ( ! class_exists( 'Jetpack_Core_API_Data' ) ) { - require_once JETPACK__PLUGIN_DIR . '/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php'; - } - $core_api_data = new Jetpack_Core_API_Data(); - $core_api_response = $core_api_data->get_all_options(); - $core_api_categories = isset( $core_api_response->data['wpcom_newsletter_categories'] ) - ? $core_api_response->data['wpcom_newsletter_categories'] - : array(); - - // Get categories using WPCOM_JSON_API_Site_Settings_V1_4_Endpoint via a mocked request. - // Set up JSON API requirements. - require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php'; - - if ( ! defined( 'WPCOM_JSON_API__BASE' ) ) { - define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1.4' ); - } - - // Set up to test site settings API response. - WPCOM_JSON_API::init()->token_details = array( 'blog_id' => $blog_id ); - $admin = self::factory()->user->create_and_get( - array( - 'role' => 'administrator', - ) - ); - - wp_set_current_user( $admin->ID ); - - if ( ! class_exists( 'WPCOM_JSON_API_Site_Settings_V1_4_Endpoint' ) ) { - require_once JETPACK__PLUGIN_DIR . '/json-endpoints/class.wpcom-json-api-site-settings-v1-4-endpoint.php'; - } - if ( ! class_exists( 'WPCOM_JSON_API_Site_Settings_Endpoint' ) ) { - require_once JETPACK__PLUGIN_DIR . '/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php'; - } - - // Initialize the endpoint with proper configuration. - $endpoint = new WPCOM_JSON_API_Site_Settings_V1_4_Endpoint( - array( - 'description' => 'Get detailed settings information about a site.', - 'group' => '__do_not_document', - 'stat' => 'sites:X', - 'min_version' => '1.4', - 'method' => 'GET', - 'path' => '/sites/%s/settings', - 'path_labels' => array( - '$site' => '(int|string) Site ID or domain', - ), - 'query_parameters' => array( - 'context' => false, - ), - 'response_format' => WPCOM_JSON_API_Site_Settings_Endpoint::$site_format, - ) - ); - - // Make the request using the endpoint's callback method. - $json_api_response = $endpoint->callback( '/sites/%s/settings', $blog_id ); - $json_api_categories = isset( $json_api_response['settings']['wpcom_newsletter_categories'] ) - ? $json_api_response['settings']['wpcom_newsletter_categories'] - : array(); - - // All three should return the same data. - $this->assertEquals( $test_categories, $helper_result, 'Helper should return correct categories' ); - $this->assertEquals( $test_categories, $core_api_categories, 'Core API should return same categories as helper' ); - $this->assertEquals( $test_categories, $json_api_categories, 'JSON API V1.4 should return same categories as helper' ); - } } From aa8187a59e6479e1895a20aab46d3f634f95a38b Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 23:49:28 -0500 Subject: [PATCH 6/9] remove unneccessary test --- .../_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php index 98192699f99e5..409268af1d7ee 100644 --- a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php +++ b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php @@ -108,14 +108,6 @@ public function test_get_category_ids_serialized_data() { $this->assertEquals( $categories, $result ); } - /** - * Test save_category_ids() returns false for non-array input. - */ - public function test_save_category_ids_non_array() { - $result = Jetpack_Newsletter_Category_Helper::save_category_ids( 'not-an-array' ); - $this->assertFalse( $result ); - } - /** * Test save_category_ids() returns false for empty array. */ From bbc7e9b1210dc7193322b4c98098f249069e45e0 Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 23:52:23 -0500 Subject: [PATCH 7/9] remove unneccessary test --- .../lib/Jetpack_Newsletter_Category_Helper_Test.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php index 409268af1d7ee..8e7bebe848fbd 100644 --- a/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php +++ b/projects/plugins/jetpack/tests/php/_inc/lib/Jetpack_Newsletter_Category_Helper_Test.php @@ -164,18 +164,14 @@ public function test_save_category_ids_filters_non_numeric() { * Test save_category_ids() handles array of arrays with term_id. */ public function test_save_category_ids_term_id_array() { - $input = array( + $input = array( array( 'term_id' => 123 ), array( 'term_id' => 456 ), array( 'term_id' => 789 ), ); - $expected = array( - array( 'term_id' => 123 ), - array( 'term_id' => 456 ), - array( 'term_id' => 789 ), - ); - $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); - $this->assertEquals( $expected, $result ); + + $result = Jetpack_Newsletter_Category_Helper::save_category_ids( $input ); + $this->assertEquals( $input, $result ); } /** From 0279a43460a083546cb81edd9fef8474df8c5f23 Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Tue, 29 Jul 2025 23:53:49 -0500 Subject: [PATCH 8/9] fix race condition --- .../jetpack/_inc/client/newsletter/newsletter-categories.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/_inc/client/newsletter/newsletter-categories.jsx b/projects/plugins/jetpack/_inc/client/newsletter/newsletter-categories.jsx index 9eeb45c8d0723..b01c64d9bee8b 100644 --- a/projects/plugins/jetpack/_inc/client/newsletter/newsletter-categories.jsx +++ b/projects/plugins/jetpack/_inc/client/newsletter/newsletter-categories.jsx @@ -54,7 +54,10 @@ function NewsletterCategories( props ) { dispatch, } = props; - const checkedCategoriesIds = newsletterCategories.map( mapCategoriesIds ); + const checkedCategoriesIds = useMemo( + () => newsletterCategories?.map( mapCategoriesIds ) ?? [], + [ newsletterCategories ] + ); const mappedCategories = useMemo( () => From 637c9bc119e85902f57c9be5baef3547231daecb Mon Sep 17 00:00:00 2001 From: Tony Arcangelini Date: Wed, 30 Jul 2025 15:36:55 -0500 Subject: [PATCH 9/9] Fix toggling --- .../class.jetpack-core-api-module-endpoints.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php index ee5fbcfcc5557..7edc3bc64aa5c 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php @@ -1061,7 +1061,17 @@ function ( &$value ) { break; case Jetpack_Newsletter_Category_Helper::NEWSLETTER_CATEGORIES_OPTION: - if ( Jetpack_Newsletter_Category_Helper::save_category_ids( (array) $value ) ) { + if ( ! is_array( $value ) || empty( $value ) ) { + break; + } + + // If we are already current, do nothing + $current_value = Jetpack_Newsletter_Category_Helper::get_category_ids(); + if ( $value === $current_value ) { + break; + } + + if ( Jetpack_Newsletter_Category_Helper::save_category_ids( $value ) ) { $updated = true; } else { $updated = false;