diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php index f704daaffafb1d..457b5c6c2b7c3a 100644 --- a/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-resolver-6-1.php @@ -16,6 +16,83 @@ * @access private */ class WP_Theme_JSON_Resolver_6_1 extends WP_Theme_JSON_Resolver_6_0 { + /** + * Returns the custom post type that contains the user's origin config + * for the current theme or a void array if none are found. + * + * This can also create and return a new draft custom post type. + * + * @param WP_Theme $theme The theme object. If empty, it + * defaults to the current theme. + * @param bool $create_post Optional. Whether a new custom post + * type should be created if none are + * found. False by default. + * @param array $post_status_filter Filter Optional. custom post type by + * post status. ['publish'] by default, + * so it only fetches published posts. + * @return array Custom Post Type for the user's origin config. + */ + public static function get_user_data_from_wp_global_styles( $theme, $create_post = false, $post_status_filter = array( 'publish' ) ) { + if ( ! $theme instanceof WP_Theme ) { + $theme = wp_get_theme(); + } + $user_cpt = array(); + $post_type_filter = 'wp_global_styles'; + $args = array( + 'posts_per_page' => 1, + 'orderby' => 'post_date', + 'order' => 'desc', + 'post_type' => $post_type_filter, + 'post_status' => $post_status_filter, + 'tax_query' => array( + array( + 'taxonomy' => 'wp_theme', + 'field' => 'name', + 'terms' => $theme->get_stylesheet(), + ), + ), + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + ); + + $cache_key = sprintf( 'wp_global_styles_%s', md5( serialize( $args ) ) ); + $post_id = (int) get_transient( $cache_key ); + // Special case: '-1' is a results not found. + if ( -1 === $post_id && ! $create_post ) { + return $user_cpt; + } + + if ( $post_id > 0 && in_array( get_post_status( $post_id ), (array) $post_status_filter, true ) ) { + return get_post( $post_id, ARRAY_A ); + } + + $gs_query = new WP_Query(); + $recent_posts = $gs_query->query( $args ); + if ( count( $recent_posts ) === 1 ) { + $user_cpt = get_post( $recent_posts[0], ARRAY_A ); + } elseif ( $create_post ) { + $cpt_post_id = wp_insert_post( + array( + 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', + 'post_status' => 'publish', + 'post_title' => __( 'Custom Styles', 'default' ), + 'post_type' => $post_type_filter, + 'post_name' => 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ), + 'tax_input' => array( + 'wp_theme' => array( wp_get_theme()->get_stylesheet() ), + ), + ), + true + ); + if ( ! is_wp_error( $cpt_post_id ) ) { + $user_cpt = get_post( $cpt_post_id, ARRAY_A ); + } + } + $cache_expiration = $user_cpt ? DAY_IN_SECONDS : HOUR_IN_SECONDS; + set_transient( $cache_key, $user_cpt ? $user_cpt['ID'] : -1, $cache_expiration ); + + return $user_cpt; + } /** * Container for data coming from core. diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index 489b2b72c91c44..109e0d787d92f0 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -268,6 +268,9 @@ function test_merges_child_theme_json_into_parent_theme_json() { ); } + /** + * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles + */ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries() { add_filter( 'query', array( $this, 'filter_db_query' ) ); $query_count = count( $this->queries ); @@ -276,7 +279,7 @@ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries( WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); } $query_count = count( $this->queries ) - $query_count; - $this->assertEquals( 1, $query_count, 'Only one SQL query should be peformed for multiple invocations of WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles()' ); + $this->assertEquals( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type' ); $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme() ); $this->assertEmpty( $user_cpt ); @@ -286,11 +289,55 @@ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries( $query_count = count( $this->queries ); for ( $i = 0; $i < 3; $i++ ) { - WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme() ); + $new_user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme() ); WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data(); + $this->assertSameSets( $user_cpt, $new_user_cpt ); } $query_count = count( $this->queries ) - $query_count; $this->assertEquals( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type' ); remove_filter( 'query', array( $this, 'filter_db_query' ) ); } + + /** + * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles + * @ticket 55392 + */ + function test_get_user_data_from_wp_global_styles_does_exist() { + $post1 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme(), true ); + $this->assertIsArray( $post1 ); + $this->assertArrayHasKey( 'ID', $post1 ); + wp_delete_post( $post1['ID'], true ); + $post2 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme(), true ); + $this->assertIsArray( $post2 ); + $this->assertArrayHasKey( 'ID', $post2 ); + } + + /** + * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles + * @ticket 55392 + */ + function test_get_user_data_from_wp_global_styles_create_post() { + $post1 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme( 'testing' ) ); + $this->assertIsArray( $post1 ); + $this->assertSameSets( array(), $post1 ); + $post2 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme( 'testing' ) ); + $this->assertIsArray( $post2 ); + $this->assertSameSets( array(), $post2 ); + $post3 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme( 'testing' ), true ); + $this->assertIsArray( $post3 ); + $this->assertArrayHasKey( 'ID', $post3 ); + } + + /** + * @covers WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles + * @ticket 55392 + */ + function test_get_user_data_from_wp_global_styles_filter_state() { + $post1 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme( 'foo' ), true, array( 'publish' ) ); + $this->assertIsArray( $post1 ); + $this->assertArrayHasKey( 'ID', $post1 ); + $post2 = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( wp_get_theme( 'foo' ), false, array( 'draft' ) ); + $this->assertIsArray( $post2 ); + $this->assertSameSets( array(), $post2 ); + } }