From 001528df12d47bd817a399e47ee3da46c4396fdb Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 4 Nov 2022 18:22:17 +0000 Subject: [PATCH 0001/1431] Fix: Category specific templates always appear as not found. According to the docs in developer.wordpress.org/reference/classes/wp_term_query/query WP_Term_Query:->query( string|array $query ) returns WP_Term[]|int[]|string[]|string, and we were using an inexistent object property terms making it always empty and look like the taxonomy did not exist. Props mamaduka, mikachan, ockham, franz00. See #56902. git-svn-id: https://develop.svn.wordpress.org/trunk@54751 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/block-template-utils.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 5db20bf08c463..3cdcb19ceb0f4 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -637,7 +637,7 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, $args = wp_parse_args( $args, $default_args ); $terms_query = $term_query->query( $args ); - if ( empty( $terms_query->terms ) ) { + if ( empty( $terms_query ) ) { $template->title = sprintf( /* translators: Custom template title in the Site Editor, referencing a taxonomy term that was not found. 1: Taxonomy singular name, 2: Term slug. */ __( 'Not found: %1$s (%2$s)' ), @@ -647,7 +647,7 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, return false; } - $term_title = $terms_query->terms[0]->name; + $term_title = $terms_query[0]->name; $template->title = sprintf( /* translators: Custom template title in the Site Editor. 1: Taxonomy singular name, 2: Term title. */ @@ -671,7 +671,7 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, $args = wp_parse_args( $args, $default_args ); $terms_with_same_title_query = $term_query->query( $args ); - if ( count( $terms_with_same_title_query->terms ) > 1 ) { + if ( count( $terms_with_same_title_query ) > 1 ) { $template->title = sprintf( /* translators: Custom template title in the Site Editor. 1: Template title, 2: Term slug. */ __( '%1$s (%2$s)' ), From 747bf65f4b8c25d97abd29134ddb5a61a3e1a59f Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sat, 5 Nov 2022 22:01:47 +0000 Subject: [PATCH 0002/1431] Coding Standards: Correct alignment in various files. This fixes `Equals sign not aligned with surrounding statements` WPCS warnings, so that the output of `composer format` is clean. Follow-up to [54445], [54476], [54494], [54522], [54652], [54687]. See #56791. git-svn-id: https://develop.svn.wordpress.org/trunk@54754 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/block-template-utils.php | 16 ++++++++++------ src/wp-includes/class-wp-oembed.php | 2 +- src/wp-includes/functions.php | 10 +++++++--- .../endpoints/class-wp-rest-posts-controller.php | 6 +++--- src/wp-includes/script-loader.php | 3 ++- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index 3cdcb19ceb0f4..d2795612edea0 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -556,10 +556,11 @@ function _wp_build_title_and_description_for_single_post_type_block_template( $p 'no_found_rows' => true, ); - $args = array( + $args = array( 'name' => $slug, ); - $args = wp_parse_args( $args, $default_args ); + $args = wp_parse_args( $args, $default_args ); + $posts_query = new WP_Query( $args ); if ( empty( $posts_query->posts ) ) { @@ -591,7 +592,8 @@ function _wp_build_title_and_description_for_single_post_type_block_template( $p $args = array( 'title' => $post_title, ); - $args = wp_parse_args( $args, $default_args ); + $args = wp_parse_args( $args, $default_args ); + $posts_with_same_title_query = new WP_Query( $args ); if ( count( $posts_with_same_title_query->posts ) > 1 ) { @@ -630,11 +632,12 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, $term_query = new WP_Term_Query(); - $args = array( + $args = array( 'number' => 1, 'slug' => $slug, ); - $args = wp_parse_args( $args, $default_args ); + $args = wp_parse_args( $args, $default_args ); + $terms_query = $term_query->query( $args ); if ( empty( $terms_query ) ) { @@ -668,7 +671,8 @@ function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, 'number' => 2, 'name' => $term_title, ); - $args = wp_parse_args( $args, $default_args ); + $args = wp_parse_args( $args, $default_args ); + $terms_with_same_title_query = $term_query->query( $args ); if ( count( $terms_with_same_title_query ) > 1 ) { diff --git a/src/wp-includes/class-wp-oembed.php b/src/wp-includes/class-wp-oembed.php index 3fce5bdb2a38d..8b24cf752b207 100644 --- a/src/wp-includes/class-wp-oembed.php +++ b/src/wp-includes/class-wp-oembed.php @@ -80,7 +80,7 @@ public function __construct() { '#https?://(www\.)?mixcloud\.com/.*#i' => array( 'https://www.mixcloud.com/oembed', true ), '#https?://(www\.|embed\.)?ted\.com/talks/.*#i' => array( 'https://www.ted.com/services/v1/oembed.{format}', true ), '#https?://(www\.)?(animoto|video214)\.com/play/.*#i' => array( 'https://animoto.com/oembeds/create', true ), - '#https?://(.+)\.tumblr\.com/.*#i' => array( 'https://www.tumblr.com/oembed/1.0', true ), + '#https?://(.+)\.tumblr\.com/.*#i' => array( 'https://www.tumblr.com/oembed/1.0', true ), '#https?://(www\.)?kickstarter\.com/projects/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ), '#https?://kck\.st/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ), '#https?://cloudup\.com/.*#i' => array( 'https://cloudup.com/oembed', true ), diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 65b0cb03b7ebe..8e14894f8b079 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -3567,19 +3567,23 @@ function wp_nonce_ays( $action ) { __( 'You are attempting to log out of %s' ), get_bloginfo( 'name' ) ); - $html = $title; - $html .= '

'; + $redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : ''; - $html .= sprintf( + + $html = $title; + $html .= '

'; + $html .= sprintf( /* translators: %s: Logout URL. */ __( 'Do you really want to log out?' ), wp_logout_url( $redirect_to ) ); } else { $html = __( 'The link you followed has expired.' ); + if ( wp_get_referer() ) { $wp_http_referer = remove_query_arg( 'updated', wp_get_referer() ); $wp_http_referer = wp_validate_redirect( esc_url_raw( $wp_http_referer ) ); + $html .= '

'; $html .= sprintf( '%s', diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index deb83c2f0343e..571a2fd5f4bcf 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -2890,9 +2890,9 @@ public function get_collection_params() { } $query_params['slug'] = array( - 'description' => __( 'Limit result set to posts with one or more specific slugs.' ), - 'type' => 'array', - 'items' => array( + 'description' => __( 'Limit result set to posts with one or more specific slugs.' ), + 'type' => 'array', + 'items' => array( 'type' => 'string', ), ); diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index fd1bf4b6fa93b..f22928dba1cda 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3686,7 +3686,8 @@ function wp_add_editor_classic_theme_styles( $editor_settings ) { if ( WP_Theme_JSON_Resolver::theme_has_support() ) { return $editor_settings; } - $suffix = wp_scripts_get_suffix(); + + $suffix = wp_scripts_get_suffix(); $classic_theme_styles = ABSPATH . WPINC . "/css/classic-themes$suffix.css"; // This follows the pattern of get_block_editor_theme_styles, From 79492c62045c035fc983435919173f09a1426bfd Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sun, 6 Nov 2022 14:46:14 +0000 Subject: [PATCH 0003/1431] Docs: Correct DocBlock formatting for `wp_sitemaps_enabled` filter. Follow-up to [48072], [48094], [48523]. See #56792. git-svn-id: https://develop.svn.wordpress.org/trunk@54755 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/sitemaps/class-wp-sitemaps.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps.php b/src/wp-includes/sitemaps/class-wp-sitemaps.php index cd8d1e4dbc95f..85d3adddb4fdd 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps.php @@ -99,8 +99,8 @@ public function sitemaps_enabled() { * * @since 5.5.0 * - * @param bool $is_enabled Whether XML Sitemaps are enabled or not. Defaults - * to true for public sites. + * @param bool $is_enabled Whether XML Sitemaps are enabled or not. + * Defaults to true for public sites. */ return (bool) apply_filters( 'wp_sitemaps_enabled', $is_enabled ); } From 399bc757fc5efc390f0368466ea9d0e0d6e9dc40 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sun, 6 Nov 2022 15:54:34 +0000 Subject: [PATCH 0004/1431] Tests: Remove a custom callback for checking action call count in multisite tests. Use `MockAction::get_call_count()` instead, for consistency with the rest of the test suite. Follow-up to [24/tests], [99/tests], [1078/tests], [1089/tests], [29916], [30784], [30785], [33253]. See #56793. git-svn-id: https://develop.svn.wordpress.org/trunk@54756 602fd350-edb4-49c9-b593-d223f7449a82 --- tests/phpunit/tests/multisite/site.php | 107 ++++++------------ .../tests/multisite/updateBlogDetails.php | 19 +--- 2 files changed, 40 insertions(+), 86 deletions(-) diff --git a/tests/phpunit/tests/multisite/site.php b/tests/phpunit/tests/multisite/site.php index 86ad14ad52126..4192625e9f24f 100644 --- a/tests/phpunit/tests/multisite/site.php +++ b/tests/phpunit/tests/multisite/site.php @@ -412,14 +412,6 @@ public function test_wpmu_update_blogs_date() { $this->assertEqualsWithDelta( $current_time, strtotime( $blog->last_updated ), 2, 'The dates should be equal' ); } - /** - * Provide a counter to determine that hooks are firing when intended. - */ - public function action_counter_cb() { - global $test_action_counter; - $test_action_counter++; - } - /** * Test cached data for a site that does not exist and then again after it exists. * @@ -465,27 +457,24 @@ public function test_update_blog_status_invalid_status() { } public function test_update_blog_status_make_ham_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); update_blog_details( $blog_id, array( 'spam' => 1 ) ); - add_action( 'make_ham_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'make_ham_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'spam', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->spam ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'spam' stays the same. update_blog_status( $blog_id, 'spam', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->spam ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'make_ham_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_content_from_spam_blog_is_not_available() { @@ -519,189 +508,165 @@ public function test_content_from_spam_blog_is_not_available() { } public function test_update_blog_status_make_spam_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); - add_action( 'make_spam_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'make_spam_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'spam', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->spam ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'spam' stays the same. update_blog_status( $blog_id, 'spam', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->spam ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'make_spam_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_archive_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); - add_action( 'archive_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'archive_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'archived', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->archived ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'archived' stays the same. update_blog_status( $blog_id, 'archived', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->archived ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'archive_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_unarchive_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); update_blog_details( $blog_id, array( 'archived' => 1 ) ); - add_action( 'unarchive_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'unarchive_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'archived', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->archived ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'archived' stays the same. update_blog_status( $blog_id, 'archived', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->archived ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'unarchive_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_make_delete_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); - add_action( 'make_delete_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'make_delete_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'deleted', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->deleted ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'deleted' stays the same. update_blog_status( $blog_id, 'deleted', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->deleted ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'make_delete_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_make_undelete_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); update_blog_details( $blog_id, array( 'deleted' => 1 ) ); - add_action( 'make_undelete_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'make_undelete_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'deleted', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->deleted ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'deleted' stays the same. update_blog_status( $blog_id, 'deleted', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->deleted ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'make_undelete_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_mature_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); - add_action( 'mature_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'mature_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'mature', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->mature ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'mature' stays the same. update_blog_status( $blog_id, 'mature', 1 ); $blog = get_site( $blog_id ); $this->assertSame( '1', $blog->mature ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'mature_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_unmature_blog_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); update_blog_details( $blog_id, array( 'mature' => 1 ) ); - add_action( 'unmature_blog', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'unmature_blog', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'mature', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->mature ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'mature' stays the same. update_blog_status( $blog_id, 'mature', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->mature ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'unmature_blog', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function test_update_blog_status_update_blog_public_action() { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); - add_action( 'update_blog_public', array( $this, 'action_counter_cb' ), 10 ); + add_action( 'update_blog_public', array( $test_action_counter, 'action' ) ); update_blog_status( $blog_id, 'public', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->public ); - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // The action should not fire if the status of 'mature' stays the same. update_blog_status( $blog_id, 'public', 0 ); $blog = get_site( $blog_id ); $this->assertSame( '0', $blog->public ); - $this->assertSame( 1, $test_action_counter ); - - remove_action( 'update_blog_public', array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } /** diff --git a/tests/phpunit/tests/multisite/updateBlogDetails.php b/tests/phpunit/tests/multisite/updateBlogDetails.php index 80595ed99b977..1c7d19a2a5710 100644 --- a/tests/phpunit/tests/multisite/updateBlogDetails.php +++ b/tests/phpunit/tests/multisite/updateBlogDetails.php @@ -56,8 +56,7 @@ public function test_update_blog_details() { * @dataProvider data_flag_hooks */ public function test_update_blog_details_flag_action( $flag, $flag_value, $hook ) { - global $test_action_counter; - $test_action_counter = 0; + $test_action_counter = new MockAction(); $blog_id = self::factory()->blog->create(); @@ -66,7 +65,7 @@ public function test_update_blog_details_flag_action( $flag, $flag_value, $hook update_blog_details( $blog_id, array( $flag => '1' ) ); } - add_action( $hook, array( $this, 'action_counter_cb' ), 10 ); + add_action( $hook, array( $test_action_counter, 'action' ) ); update_blog_details( $blog_id, array( $flag => $flag_value ) ); $blog = get_site( $blog_id ); @@ -74,15 +73,13 @@ public function test_update_blog_details_flag_action( $flag, $flag_value, $hook $this->assertSame( $flag_value, $blog->{$flag} ); // The hook attached to this flag should have fired once during update_blog_details(). - $this->assertSame( 1, $test_action_counter ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); // Update the site to the exact same flag value for this flag. update_blog_details( $blog_id, array( $flag => $flag_value ) ); // The hook attached to this flag should not have fired again. - $this->assertSame( 1, $test_action_counter ); - - remove_action( $hook, array( $this, 'action_counter_cb' ), 10 ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); } public function data_flag_hooks() { @@ -98,14 +95,6 @@ public function data_flag_hooks() { ); } - /** - * Provide a counter to determine that hooks are firing when intended. - */ - public function action_counter_cb() { - global $test_action_counter; - $test_action_counter++; - } - /** * When the path for a site is updated with update_blog_details(), the final path * should have a leading and trailing slash. From 711dbb79934fb23302e7dbcbf8cbf4b3f53ee19b Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Sun, 6 Nov 2022 16:19:15 +0000 Subject: [PATCH 0005/1431] Tests: Move `update_blog_status()` tests to their own file. Reduce some of the clutter in `tests/multisite/site.php` and introduce `tests/multisite/updateBlogStatus.php`. Tests moved over are verbatim at this point. Follow-up to [1078/tests], [29916], [30784], [30785], [33253]. See #56793. git-svn-id: https://develop.svn.wordpress.org/trunk@54757 602fd350-edb4-49c9-b593-d223f7449a82 --- tests/phpunit/tests/multisite/site.php | 229 ----------------- .../tests/multisite/updateBlogDetails.php | 1 + .../tests/multisite/updateBlogStatus.php | 241 ++++++++++++++++++ 3 files changed, 242 insertions(+), 229 deletions(-) create mode 100644 tests/phpunit/tests/multisite/updateBlogStatus.php diff --git a/tests/phpunit/tests/multisite/site.php b/tests/phpunit/tests/multisite/site.php index 4192625e9f24f..600bee38b438e 100644 --- a/tests/phpunit/tests/multisite/site.php +++ b/tests/phpunit/tests/multisite/site.php @@ -440,235 +440,6 @@ public function test_get_blog_details_when_site_does_not_exist() { $this->assertEquals( $blog, wp_cache_get( $blog_id, 'blog-details' ) ); } - /** - * Updating a field returns the sme value that was passed. - */ - public function test_update_blog_status() { - $result = update_blog_status( 1, 'spam', 0 ); - $this->assertSame( 0, $result ); - } - - /** - * Updating an invalid field returns the same value that was passed. - */ - public function test_update_blog_status_invalid_status() { - $result = update_blog_status( 1, 'doesnotexist', 'invalid' ); - $this->assertSame( 'invalid', $result ); - } - - public function test_update_blog_status_make_ham_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - update_blog_details( $blog_id, array( 'spam' => 1 ) ); - - add_action( 'make_ham_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'spam', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->spam ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'spam' stays the same. - update_blog_status( $blog_id, 'spam', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->spam ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_content_from_spam_blog_is_not_available() { - $spam_blog_id = self::factory()->blog->create(); - switch_to_blog( $spam_blog_id ); - $post_data = array( - 'post_title' => 'Hello World!', - 'post_content' => 'Hello world content', - ); - $post_id = self::factory()->post->create( $post_data ); - $post = get_post( $post_id ); - $spam_permalink = site_url() . '/?p=' . $post->ID; - $spam_embed_url = get_post_embed_url( $post_id ); - - restore_current_blog(); - $this->assertNotEmpty( $spam_permalink ); - $this->assertSame( $post_data['post_title'], $post->post_title ); - - update_blog_status( $spam_blog_id, 'spam', 1 ); - - $post_id = self::factory()->post->create( - array( - 'post_content' => "\n $spam_permalink \n", - ) - ); - $post = get_post( $post_id ); - $content = apply_filters( 'the_content', $post->post_content ); - - $this->assertStringNotContainsString( $post_data['post_title'], $content ); - $this->assertStringNotContainsString( "src=\"{$spam_embed_url}#?", $content ); - } - - public function test_update_blog_status_make_spam_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - - add_action( 'make_spam_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'spam', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->spam ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'spam' stays the same. - update_blog_status( $blog_id, 'spam', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->spam ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_archive_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - - add_action( 'archive_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'archived', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->archived ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'archived' stays the same. - update_blog_status( $blog_id, 'archived', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->archived ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_unarchive_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - update_blog_details( $blog_id, array( 'archived' => 1 ) ); - - add_action( 'unarchive_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'archived', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->archived ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'archived' stays the same. - update_blog_status( $blog_id, 'archived', 0 ); - $blog = get_site( $blog_id ); - $this->assertSame( '0', $blog->archived ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_make_delete_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - - add_action( 'make_delete_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'deleted', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->deleted ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'deleted' stays the same. - update_blog_status( $blog_id, 'deleted', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->deleted ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_make_undelete_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - update_blog_details( $blog_id, array( 'deleted' => 1 ) ); - - add_action( 'make_undelete_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'deleted', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->deleted ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'deleted' stays the same. - update_blog_status( $blog_id, 'deleted', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->deleted ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_mature_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - - add_action( 'mature_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'mature', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->mature ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'mature' stays the same. - update_blog_status( $blog_id, 'mature', 1 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '1', $blog->mature ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_unmature_blog_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - update_blog_details( $blog_id, array( 'mature' => 1 ) ); - - add_action( 'unmature_blog', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'mature', 0 ); - - $blog = get_site( $blog_id ); - $this->assertSame( '0', $blog->mature ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'mature' stays the same. - update_blog_status( $blog_id, 'mature', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->mature ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - - public function test_update_blog_status_update_blog_public_action() { - $test_action_counter = new MockAction(); - - $blog_id = self::factory()->blog->create(); - - add_action( 'update_blog_public', array( $test_action_counter, 'action' ) ); - update_blog_status( $blog_id, 'public', 0 ); - - $blog = get_site( $blog_id ); - $this->assertSame( '0', $blog->public ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - - // The action should not fire if the status of 'mature' stays the same. - update_blog_status( $blog_id, 'public', 0 ); - $blog = get_site( $blog_id ); - - $this->assertSame( '0', $blog->public ); - $this->assertSame( 1, $test_action_counter->get_call_count() ); - } - /** * @ticket 27952 */ diff --git a/tests/phpunit/tests/multisite/updateBlogDetails.php b/tests/phpunit/tests/multisite/updateBlogDetails.php index 1c7d19a2a5710..d3249a126165d 100644 --- a/tests/phpunit/tests/multisite/updateBlogDetails.php +++ b/tests/phpunit/tests/multisite/updateBlogDetails.php @@ -7,6 +7,7 @@ * @group multisite */ class Tests_Multisite_UpdateBlogDetails extends WP_UnitTestCase { + /** * If `update_blog_details()` is called with any kind of empty arguments, it * should return false. diff --git a/tests/phpunit/tests/multisite/updateBlogStatus.php b/tests/phpunit/tests/multisite/updateBlogStatus.php new file mode 100644 index 0000000000000..707b8fa66aee5 --- /dev/null +++ b/tests/phpunit/tests/multisite/updateBlogStatus.php @@ -0,0 +1,241 @@ +assertSame( 0, $result ); + } + + /** + * Updating an invalid field returns the same value that was passed. + */ + public function test_update_blog_status_invalid_status() { + $result = update_blog_status( 1, 'doesnotexist', 'invalid' ); + $this->assertSame( 'invalid', $result ); + } + + public function test_update_blog_status_make_ham_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + update_blog_details( $blog_id, array( 'spam' => 1 ) ); + + add_action( 'make_ham_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'spam', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->spam ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'spam' stays the same. + update_blog_status( $blog_id, 'spam', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->spam ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_content_from_spam_blog_is_not_available() { + $spam_blog_id = self::factory()->blog->create(); + switch_to_blog( $spam_blog_id ); + $post_data = array( + 'post_title' => 'Hello World!', + 'post_content' => 'Hello world content', + ); + $post_id = self::factory()->post->create( $post_data ); + $post = get_post( $post_id ); + $spam_permalink = site_url() . '/?p=' . $post->ID; + $spam_embed_url = get_post_embed_url( $post_id ); + + restore_current_blog(); + $this->assertNotEmpty( $spam_permalink ); + $this->assertSame( $post_data['post_title'], $post->post_title ); + + update_blog_status( $spam_blog_id, 'spam', 1 ); + + $post_id = self::factory()->post->create( + array( + 'post_content' => "\n $spam_permalink \n", + ) + ); + $post = get_post( $post_id ); + $content = apply_filters( 'the_content', $post->post_content ); + + $this->assertStringNotContainsString( $post_data['post_title'], $content ); + $this->assertStringNotContainsString( "src=\"{$spam_embed_url}#?", $content ); + } + + public function test_update_blog_status_make_spam_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + + add_action( 'make_spam_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'spam', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->spam ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'spam' stays the same. + update_blog_status( $blog_id, 'spam', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->spam ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_archive_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + + add_action( 'archive_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'archived', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->archived ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'archived' stays the same. + update_blog_status( $blog_id, 'archived', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->archived ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_unarchive_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + update_blog_details( $blog_id, array( 'archived' => 1 ) ); + + add_action( 'unarchive_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'archived', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->archived ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'archived' stays the same. + update_blog_status( $blog_id, 'archived', 0 ); + $blog = get_site( $blog_id ); + $this->assertSame( '0', $blog->archived ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_make_delete_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + + add_action( 'make_delete_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'deleted', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->deleted ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'deleted' stays the same. + update_blog_status( $blog_id, 'deleted', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->deleted ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_make_undelete_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + update_blog_details( $blog_id, array( 'deleted' => 1 ) ); + + add_action( 'make_undelete_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'deleted', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->deleted ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'deleted' stays the same. + update_blog_status( $blog_id, 'deleted', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->deleted ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_mature_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + + add_action( 'mature_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'mature', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->mature ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'mature' stays the same. + update_blog_status( $blog_id, 'mature', 1 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '1', $blog->mature ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_unmature_blog_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + update_blog_details( $blog_id, array( 'mature' => 1 ) ); + + add_action( 'unmature_blog', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'mature', 0 ); + + $blog = get_site( $blog_id ); + $this->assertSame( '0', $blog->mature ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'mature' stays the same. + update_blog_status( $blog_id, 'mature', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->mature ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + + public function test_update_blog_status_update_blog_public_action() { + $test_action_counter = new MockAction(); + + $blog_id = self::factory()->blog->create(); + + add_action( 'update_blog_public', array( $test_action_counter, 'action' ) ); + update_blog_status( $blog_id, 'public', 0 ); + + $blog = get_site( $blog_id ); + $this->assertSame( '0', $blog->public ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + + // The action should not fire if the status of 'mature' stays the same. + update_blog_status( $blog_id, 'public', 0 ); + $blog = get_site( $blog_id ); + + $this->assertSame( '0', $blog->public ); + $this->assertSame( 1, $test_action_counter->get_call_count() ); + } + } + +endif; From 473dfe51c723639e510808cd0d1511b2e067c8a9 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Mon, 7 Nov 2022 13:25:10 +0000 Subject: [PATCH 0006/1431] =?UTF-8?q?General:=20Use=20HTTPS=20for=20the=20?= =?UTF-8?q?b2/caf=C3=A9log=20link=20in=20`readme.html`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to [691], [47285]. Props rajanpanchal2028. Fixes #57018. git-svn-id: https://develop.svn.wordpress.org/trunk@54758 602fd350-edb4-49c9-b593-d223f7449a82 --- src/readme.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/readme.html b/src/readme.html index 0048b9efa39f9..72bc2afe4be14 100644 --- a/src/readme.html +++ b/src/readme.html @@ -88,7 +88,7 @@

Final Notes

Share the Love

WordPress has no multi-million dollar marketing campaign or celebrity sponsors, but we do have something even better—you. If you enjoy WordPress please consider telling a friend, setting it up for someone less knowledgeable than yourself, or writing the author of a media article that overlooks us.

-

WordPress is the official continuation of b2/cafélog, which came from Michel V. The work has been continued by the WordPress developers. If you would like to support WordPress, please consider donating.

+

WordPress is the official continuation of b2/cafélog, which came from Michel V. The work has been continued by the WordPress developers. If you would like to support WordPress, please consider donating.

License

WordPress is free software, and is released under the terms of the GPL (GNU General Public License) version 2 or (at your option) any later version. See license.txt.

From c6e8353e369cae3be8bf6cc218972c0b7f65a1a9 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Mon, 7 Nov 2022 13:33:11 +0000 Subject: [PATCH 0007/1431] Docs: Replace HTTP links with HTTPS in `class-json.php` docblocks. Props haritpanchal. Fixes #57017. See #56792. git-svn-id: https://develop.svn.wordpress.org/trunk@54759 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-json.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-json.php b/src/wp-includes/class-json.php index 9eca6f4b578cf..d651dcdfb8861 100644 --- a/src/wp-includes/class-json.php +++ b/src/wp-includes/class-json.php @@ -54,8 +54,8 @@ * @author Brett Stimmerman * @copyright 2005 Michal Migurski * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $ - * @license http://www.opensource.org/licenses/bsd-license.php - * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + * @license https://www.opensource.org/licenses/bsd-license.php + * @link https://pear.php.net/pepr/pepr-proposal-show.php?id=198 */ /** From ad31a2e4244fc72f9abc725b0c55c351edbe759e Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Mon, 7 Nov 2022 17:45:29 +0000 Subject: [PATCH 0008/1431] Tests: Combine duplicate `update_posts_count()` tests. This combines the newer test for `update_posts_count()` located in its own file with the pre-existing one from `tests/multisite/site.php`, which was essentially testing the same thing in a similar way. Includes: * Renaming the test class per the [https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/#naming-and-organization naming conventions]. * Adjusting comments per the documentation standards. * Updating `@covers` tags for accuracy. * Removing unnecessary blog switching. * Using `assertSame()` to check the value type. Follow-up to [28835], [29667], [52207]. See #57023, #56793. git-svn-id: https://develop.svn.wordpress.org/trunk@54760 602fd350-edb4-49c9-b593-d223f7449a82 --- tests/phpunit/tests/multisite/site.php | 12 ---- .../tests/multisite/updatePostsCount.php | 58 +++++++++---------- 2 files changed, 26 insertions(+), 44 deletions(-) diff --git a/tests/phpunit/tests/multisite/site.php b/tests/phpunit/tests/multisite/site.php index 600bee38b438e..990003cde9c48 100644 --- a/tests/phpunit/tests/multisite/site.php +++ b/tests/phpunit/tests/multisite/site.php @@ -440,18 +440,6 @@ public function test_get_blog_details_when_site_does_not_exist() { $this->assertEquals( $blog, wp_cache_get( $blog_id, 'blog-details' ) ); } - /** - * @ticket 27952 - */ - public function test_posts_count() { - self::factory()->post->create(); - $post2 = self::factory()->post->create(); - $this->assertSame( 2, get_site()->post_count ); - - wp_delete_post( $post2 ); - $this->assertSame( 1, get_site()->post_count ); - } - /** * @ticket 26410 */ diff --git a/tests/phpunit/tests/multisite/updatePostsCount.php b/tests/phpunit/tests/multisite/updatePostsCount.php index 2806aaedd5b41..850d9fff59474 100644 --- a/tests/phpunit/tests/multisite/updatePostsCount.php +++ b/tests/phpunit/tests/multisite/updatePostsCount.php @@ -2,54 +2,48 @@ if ( is_multisite() ) : /** - * Test update_posts_count() get called via filters of WP_Site in multisite. + * Test that update_posts_count() gets called via default filters on multisite. * * @group ms-site * @group multisite * - * @covers ::_update_posts_count_on_delete + * @covers ::update_posts_count */ - class Tests_update_posts_count_on_delete extends WP_UnitTestCase { + class Tests_Multisite_UpdatePostsCount extends WP_UnitTestCase { /** - * Test that the posts count is updated correctly when a posts are added and deleted. + * Tests that posts count is updated correctly when posts are added or deleted. + * + * @ticket 27952 * @ticket 53443 + * + * @covers ::_update_posts_count_on_transition_post_status + * @covers ::_update_posts_count_on_delete */ - public function test_update_posts_count_on_delete() { + public function test_update_posts_count() { + $original_post_count = (int) get_site()->post_count; - $blog_id = self::factory()->blog->create(); - switch_to_blog( $blog_id ); + $post_id = self::factory()->post->create(); - $current_post_count = (int) get_option( 'post_count' ); - - $post_id = self::factory()->post->create( - array( - 'post_type' => 'post', - 'post_author' => '1', - 'post_date' => '2012-10-23 19:34:42', - 'post_status' => 'publish', - ) - ); - - /** - * Check that add_action( 'deleted_post', '_update_posts_count_on_delete' ) is called when a post is created. - * Check that _update_posts_count_on_transition_post_status() is called on that filter which then calls - * update_posts_count to update the count. + /* + * Check that posts count is updated when a post is created: + * add_action( 'transition_post_status', '_update_posts_count_on_transition_post_status', 10, 3 ); + * + * Check that _update_posts_count_on_transition_post_status() is called on that filter, + * which then calls update_posts_count() to update the count. */ - $this->assertEquals( $current_post_count + 1, (int) get_option( 'post_count' ), 'post added' ); + $this->assertSame( $original_post_count + 1, get_site()->post_count, 'Post count should be incremented by 1.' ); wp_delete_post( $post_id ); - /** - * Check that add_action( 'transition_post_status', '_update_posts_count_on_transition_post_status', 10, 3 ) - * is called when a post is deleted. - * Check that _update_posts_count_on_delete() is called on that filter which then calls update_posts_count - * to update the count. + /* + * Check that posts count is updated when a post is deleted: + * add_action( 'deleted_post', '_update_posts_count_on_delete' ); + * + * Check that _update_posts_count_on_delete() is called on that filter, + * which then calls update_posts_count() to update the count. */ - $this->assertEquals( $current_post_count, (int) get_option( 'post_count' ), 'post deleted' ); - - restore_current_blog(); - + $this->assertSame( $original_post_count, get_site()->post_count, 'Post count should match the original count.' ); } } From 41fef2f8c98200654701d0ba839a15f817311eec Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Mon, 7 Nov 2022 20:21:07 +0000 Subject: [PATCH 0009/1431] Editor: Improve Archive template description. This changeset improves the description of the Archive template in the Site Editor to make it more accurate. Follow-up to [52331]. Props Chaton666, webaxones, mukesh27, audrasjb, SergeyBiryukov. Fixes #57001. git-svn-id: https://develop.svn.wordpress.org/trunk@54761 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/block-template-utils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/block-template-utils.php b/src/wp-includes/block-template-utils.php index d2795612edea0..802627873ef66 100644 --- a/src/wp-includes/block-template-utils.php +++ b/src/wp-includes/block-template-utils.php @@ -139,7 +139,7 @@ function get_default_block_template_types() { ), 'archive' => array( 'title' => _x( 'Archive', 'Template name' ), - 'description' => __( 'Displays post categories, tags, and other archives.' ), + 'description' => __( 'Displays posts by a category, tag, author, or date.' ), ), 'author' => array( 'title' => _x( 'Author', 'Template name' ), From be4f63f00fdcf02be9d677b5c63f9add375bd42c Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Mon, 7 Nov 2022 20:53:18 +0000 Subject: [PATCH 0010/1431] Tests: Restore blog switching in `update_posts_count()` test. The previous iteration of the test passed when run in isolation but failed when running the whole test suite. Restoring the `switch_to_blog()` call allows the test to pass again pending a deeper investigation. Follow-up to [54760]. See #57023. git-svn-id: https://develop.svn.wordpress.org/trunk@54762 602fd350-edb4-49c9-b593-d223f7449a82 --- .../tests/multisite/updatePostsCount.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/multisite/updatePostsCount.php b/tests/phpunit/tests/multisite/updatePostsCount.php index 850d9fff59474..270ac60c49901 100644 --- a/tests/phpunit/tests/multisite/updatePostsCount.php +++ b/tests/phpunit/tests/multisite/updatePostsCount.php @@ -21,10 +21,21 @@ class Tests_Multisite_UpdatePostsCount extends WP_UnitTestCase { * @covers ::_update_posts_count_on_delete */ public function test_update_posts_count() { + $blog_id = self::factory()->blog->create(); + switch_to_blog( $blog_id ); + $original_post_count = (int) get_site()->post_count; $post_id = self::factory()->post->create(); + $post_count_after_creating = get_site()->post_count; + + wp_delete_post( $post_id ); + + $post_count_after_deleting = get_site()->post_count; + + restore_current_blog(); + /* * Check that posts count is updated when a post is created: * add_action( 'transition_post_status', '_update_posts_count_on_transition_post_status', 10, 3 ); @@ -32,9 +43,7 @@ public function test_update_posts_count() { * Check that _update_posts_count_on_transition_post_status() is called on that filter, * which then calls update_posts_count() to update the count. */ - $this->assertSame( $original_post_count + 1, get_site()->post_count, 'Post count should be incremented by 1.' ); - - wp_delete_post( $post_id ); + $this->assertSame( $original_post_count + 1, $post_count_after_creating, 'Post count should be incremented by 1.' ); /* * Check that posts count is updated when a post is deleted: @@ -43,7 +52,7 @@ public function test_update_posts_count() { * Check that _update_posts_count_on_delete() is called on that filter, * which then calls update_posts_count() to update the count. */ - $this->assertSame( $original_post_count, get_site()->post_count, 'Post count should match the original count.' ); + $this->assertSame( $original_post_count, $post_count_after_deleting, 'Post count should match the original count.' ); } } From 057f2e64a5a51ccaae6b2c6cfff0da2ca362a042 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Tue, 8 Nov 2022 13:27:23 +0000 Subject: [PATCH 0011/1431] Docs: Document the usage of `$wpdb` global in `WP_Date_Query` methods. Follow-up to [25139], [29933], [38280], [38768]. Props upadalavipul. Fixes #57033. git-svn-id: https://develop.svn.wordpress.org/trunk@54765 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-date-query.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wp-includes/class-wp-date-query.php b/src/wp-includes/class-wp-date-query.php index 00a7554dea0b4..cf3bddc9ae369 100644 --- a/src/wp-includes/class-wp-date-query.php +++ b/src/wp-includes/class-wp-date-query.php @@ -473,6 +473,8 @@ public function validate_date_values( $date_query = array() ) { * * @since 3.7.0 * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param string $column The user-supplied column name. * @return string A validated column name value. */ @@ -699,6 +701,8 @@ protected function get_sql_for_subquery( $query ) { * * @since 4.1.0 * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param array $query Date query clause. * @param array $parent_query Parent query of the current date query. * @return string[] { @@ -961,6 +965,8 @@ public function build_mysql_datetime( $datetime, $default_to_max = false ) { * * @since 3.7.0 * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param string $column The column to query against. Needs to be pre-validated! * @param string $compare The comparison operator. Needs to be pre-validated! * @param int|null $hour Optional. An hour value (0-23). From 61f569e81a6eed7a5ad67747ba1559e10e7ce511 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Tue, 8 Nov 2022 17:29:48 +0000 Subject: [PATCH 0012/1431] Query: Don't attempt caching if running a WP_User_Query before plugins_loaded. In #55594 user meta caching was enabled by default when making a `WP_User_Query`. Previously, this was only enabled if a developer specifically queried for 'all_with_meta' fields. User meta caching is implemented using a pluggable function, `cache_users`. If a plugin runs a `WP_User_Query` before pluggable functions have been defined, this will now cause a fatal error. In this commit, a `function_exists` check is introduced to avoid calling `cache_users` if it's not defined. Additionally, a `_doing_it_wrong` notice is issued if the `WP_User_Query::query` method is called before the 'plugins_loaded' hook. Props carazo, subrataemfluence, oakesjosh, spacedmonkey, obenland, SergeyBiryukov, peterwilsoncc. Fixes #56952. git-svn-id: https://develop.svn.wordpress.org/trunk@54766 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-user-query.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 330882494e3b9..8fc1564bef01a 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -776,6 +776,18 @@ public function prepare_query( $query = array() ) { public function query() { global $wpdb; + if ( ! did_action( 'plugins_loaded' ) ) { + _doing_it_wrong( + 'WP_User_Query::query', + sprintf( + /* translators: %s: plugins_loaded */ + __( 'User queries should not be run before the %s hook.' ), + 'plugins_loaded' + ), + '6.1.1' + ); + } + $qv =& $this->query_vars; /** @@ -840,7 +852,9 @@ public function query() { $result->id = $result->ID; } } elseif ( 'all_with_meta' === $qv['fields'] || 'all' === $qv['fields'] ) { - cache_users( $this->results ); + if ( function_exists( 'cache_users' ) ) { + cache_users( $this->results ); + } $r = array(); foreach ( $this->results as $userid ) { From fba9680eda5865efcb258a6ed3aa05a94f83b886 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 9 Nov 2022 00:26:41 +0000 Subject: [PATCH 0013/1431] Query: Bypass caching for filtered `SELECT`s. Bypass caching within `WP_Query` when the `SELECT` clause has been modified via a filter. This prevents both cache key collisions and the returning of incomplete or unexpected results when the `SELECT` clause has been modified by an extender. Props pypwalters, claytoncollie, johnwatkins0, TimothyBlynJacobs, costdev, spacedmonkey, peterwilsoncc. Fixes #57012. git-svn-id: https://develop.svn.wordpress.org/trunk@54768 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 15 ++ tests/phpunit/tests/query/fieldsClause.php | 242 +++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 tests/phpunit/tests/query/fieldsClause.php diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 511c4a7eec81e..69efdf5e2adca 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -3103,8 +3103,23 @@ public function get_posts() { * cannot be cached. Note the space before `RAND` in the string * search, that to ensure against a collision with another * function. + * + * If `$fields` has been modified by the `posts_fields`, + * `posts_fields_request`, `post_clauses` or `posts_clauses_request` + * filters, then caching is disabled to prevent caching collisions. */ $id_query_is_cacheable = ! str_contains( strtoupper( $orderby ), ' RAND(' ); + + $cachable_field_values = array( + "{$wpdb->posts}.*", + "{$wpdb->posts}.ID, {$wpdb->posts}.post_parent", + "{$wpdb->posts}.ID", + ); + + if ( ! in_array( $fields, $cachable_field_values, true ) ) { + $id_query_is_cacheable = false; + } + if ( $q['cache_results'] && $id_query_is_cacheable ) { $new_request = str_replace( $fields, "{$wpdb->posts}.*", $this->request ); $cache_key = $this->generate_cache_key( $q, $new_request ); diff --git a/tests/phpunit/tests/query/fieldsClause.php b/tests/phpunit/tests/query/fieldsClause.php new file mode 100644 index 0000000000000..4522923a8a76f --- /dev/null +++ b/tests/phpunit/tests/query/fieldsClause.php @@ -0,0 +1,242 @@ +post->create_many( 5, array( 'post_type' => 'wptests_pt' ) ); + } + + public function set_up() { + parent::set_up(); + /* + * Re-register the CPT for use within each test. + * + * Custom post types are deregistered by the default tear_down method + * so need to be re-registered for each test as WP_Query calls + * get_post_types(). + */ + register_post_type( 'wptests_pt' ); + } + + /** + * Tests limiting the WP_Query fields to the ID and parent sub-set. + * + * @ticket 57012 + */ + public function test_should_limit_fields_to_id_and_parent_subset() { + $query_args = array( + 'post_type' => 'wptests_pt', + 'fields' => 'id=>parent', + ); + + $q = new WP_Query( $query_args ); + + $expected = array(); + foreach ( self::$post_ids as $post_id ) { + // Use array_shift to populate in the reverse order. + array_unshift( + $expected, + (object) array( + 'ID' => $post_id, + 'post_parent' => 0, + ) + ); + } + + $this->assertEquals( $expected, $q->posts, 'Posts property for first query is not of expected form.' ); + $this->assertSame( 5, $q->found_posts, 'Number of found posts is not five.' ); + $this->assertEquals( 1, $q->max_num_pages, 'Number of found pages is not one.' ); + + // Test the second query's results match. + $q2 = new WP_Query( $query_args ); + $this->assertEquals( $expected, $q2->posts, 'Posts property for second query is not in the expected form.' ); + } + + /** + * Tests limiting the WP_Query fields to the IDs only. + * + * @ticket 57012 + */ + public function test_should_limit_fields_to_ids() { + $query_args = array( + 'post_type' => 'wptests_pt', + 'fields' => 'ids', + ); + + $q = new WP_Query( $query_args ); + + $expected = array_reverse( self::$post_ids ); + + $this->assertEquals( $expected, $q->posts, 'Posts property for first query is not of expected form.' ); + $this->assertSame( 5, $q->found_posts, 'Number of found posts is not five.' ); + $this->assertEquals( 1, $q->max_num_pages, 'Number of found pages is not one.' ); + + // Test the second query's results match. + $q2 = new WP_Query( $query_args ); + $this->assertEquals( $expected, $q2->posts, 'Posts property for second query is not in the expected form.' ); + } + + /** + * Tests querying all fields via WP_Query. + * + * @ticket 57012 + */ + public function test_should_query_all_fields() { + $query_args = array( + 'post_type' => 'wptests_pt', + 'fields' => 'all', + ); + + $q = new WP_Query( $query_args ); + + $expected = array_map( 'get_post', array_reverse( self::$post_ids ) ); + + $this->assertEquals( $expected, $q->posts, 'Posts property for first query is not of expected form.' ); + $this->assertSame( 5, $q->found_posts, 'Number of found posts is not five.' ); + $this->assertEquals( 1, $q->max_num_pages, 'Number of found pages is not one.' ); + + // Test the second query's results match. + $q2 = new WP_Query( $query_args ); + $this->assertEquals( $expected, $q2->posts, 'Posts property for second query is not in the expected form.' ); + } + + /** + * Tests adding fields to WP_Query via filters when requesting the ID and parent sub-set. + * + * @ticket 57012 + */ + public function test_should_include_filtered_values_in_addition_to_id_and_parent_subset() { + add_filter( 'posts_fields', array( $this, 'filter_posts_fields' ) ); + add_filter( 'posts_clauses', array( $this, 'filter_posts_clauses' ) ); + + $query_args = array( + 'post_type' => 'wptests_pt', + 'fields' => 'id=>parent', + ); + + $q = new WP_Query( $query_args ); + + $expected = array(); + foreach ( self::$post_ids as $post_id ) { + // Use array_shift to populate in the reverse order. + array_unshift( + $expected, + (object) array( + 'ID' => $post_id, + 'post_parent' => 0, + 'test_post_fields' => 1, + 'test_post_clauses' => 2, + ) + ); + } + + $this->assertEquals( $expected, $q->posts, 'Posts property for first query is not of expected form.' ); + $this->assertSame( 5, $q->found_posts, 'Number of found posts is not five.' ); + $this->assertEquals( 1, $q->max_num_pages, 'Number of found pages is not one.' ); + + // Test the second query's results match. + $q2 = new WP_Query( $query_args ); + $this->assertEquals( $expected, $q2->posts, 'Posts property for second query is not in the expected form.' ); + } + + /** + * Tests adding fields to WP_Query via filters when requesting the ID field. + * + * @ticket 57012 + */ + public function test_should_include_filtered_values_in_addition_to_id() { + add_filter( 'posts_fields', array( $this, 'filter_posts_fields' ) ); + add_filter( 'posts_clauses', array( $this, 'filter_posts_clauses' ) ); + + $query_args = array( + 'post_type' => 'wptests_pt', + 'fields' => 'ids', + ); + + $q = new WP_Query( $query_args ); + + // Fields => ID does not include the additional fields. + $expected = array_reverse( self::$post_ids ); + + $this->assertEquals( $expected, $q->posts, 'Posts property for first query is not of expected form.' ); + $this->assertSame( 5, $q->found_posts, 'Number of found posts is not five.' ); + $this->assertEquals( 1, $q->max_num_pages, 'Number of found pages is not one.' ); + + // Test the second query's results match. + $q2 = new WP_Query( $query_args ); + $this->assertEquals( $expected, $q2->posts, 'Posts property for second query is not in the expected form.' ); + } + + /** + * Tests adding fields to WP_Query via filters when requesting all fields. + * + * @ticket 57012 + */ + public function test_should_include_filtered_values() { + add_filter( 'posts_fields', array( $this, 'filter_posts_fields' ) ); + add_filter( 'posts_clauses', array( $this, 'filter_posts_clauses' ) ); + + $query_args = array( + 'post_type' => 'wptests_pt', + 'fields' => 'all', + ); + + $q = new WP_Query( $query_args ); + + $expected = array_map( 'get_post', array_reverse( self::$post_ids ) ); + foreach ( $expected as $post ) { + $post->test_post_fields = 1; + $post->test_post_clauses = 2; + } + + $this->assertEquals( $expected, $q->posts, 'Posts property for first query is not of expected form.' ); + $this->assertSame( 5, $q->found_posts, 'Number of found posts is not five.' ); + $this->assertEquals( 1, $q->max_num_pages, 'Number of found pages is not one.' ); + + // Test the second query's results match. + $q2 = new WP_Query( $query_args ); + $this->assertEquals( $expected, $q2->posts, 'Posts property for second query is not in the expected form.' ); + } + + /** + * Filters the posts fields. + * + * @param string $fields The fields to SELECT. + * @return string The filtered fields. + */ + function filter_posts_fields( $fields ) { + return "$fields, 1 as test_post_fields"; + } + + /** + * Filters the posts clauses. + * + * @param array $clauses The WP_Query database clauses. + * @return array The filtered database clauses. + */ + function filter_posts_clauses( $clauses ) { + $clauses['fields'] .= ', 2 as test_post_clauses'; + return $clauses; + } +} From b8bf29746ad71447b4e9d5cf7458e269c6e25a8b Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Wed, 9 Nov 2022 00:28:33 +0000 Subject: [PATCH 0014/1431] Editor: Improve frontend performance for `get_default_block_editor_settings()`. The `wp_max_upload_size()` function can be expensive to call, especially for large sites or multisites. For the frontend usage of `get_default_block_editor_settings()` knowing the allowed upload size is typically unnecessary. This changeset adds a condition so that `wp_max_upload_size()` is only called if the current user can actually `upload_files`. It keeps the data present when it is actually needed while avoiding the execution overhead when it is not needed. Props janthiel, Clorith, flixos90, spacedmonkey. Fixes #56815. git-svn-id: https://develop.svn.wordpress.org/trunk@54769 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/block-editor.php | 11 ++++++++--- tests/phpunit/tests/blocks/editor.php | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 1c67e6cc7af27..901ba21a3f282 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -153,9 +153,14 @@ function get_allowed_block_types( $block_editor_context ) { */ function get_default_block_editor_settings() { // Media settings. - $max_upload_size = wp_max_upload_size(); - if ( ! $max_upload_size ) { - $max_upload_size = 0; + + // wp_max_upload_size() can be expensive, so only call it when relevant for the current user. + $max_upload_size = 0; + if ( current_user_can( 'upload_files' ) ) { + $max_upload_size = wp_max_upload_size(); + if ( ! $max_upload_size ) { + $max_upload_size = 0; + } } /** This filter is documented in wp-admin/includes/media.php */ diff --git a/tests/phpunit/tests/blocks/editor.php b/tests/phpunit/tests/blocks/editor.php index 53b8d96956379..076b3026fa0ea 100644 --- a/tests/phpunit/tests/blocks/editor.php +++ b/tests/phpunit/tests/blocks/editor.php @@ -301,6 +301,31 @@ public function test_get_default_block_editor_settings() { $this->assertTrue( $settings['__unstableGalleryWithImageBlocks'] ); } + /** + * @ticket 56815 + */ + public function test_get_default_block_editor_settings_max_upload_file_size() { + // Force the return value of wp_max_upload_size() to be 500. + add_filter( + 'upload_size_limit', + function() { + return 500; + } + ); + + // Expect 0 when user is not allowed to upload (as wp_max_upload_size() should not be called). + $settings = get_default_block_editor_settings(); + $this->assertSame( 0, $settings['maxUploadFileSize'] ); + + // Set up an administrator, as they can upload files. + $administrator = self::factory()->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $administrator ); + + // Expect the above 500 as the user is now allowed to upload. + $settings = get_default_block_editor_settings(); + $this->assertSame( 500, $settings['maxUploadFileSize'] ); + } + /** * @ticket 53397 */ From 6903cfd82288ba43d0f4e67f91c8aabb6eb66250 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 9 Nov 2022 00:38:14 +0000 Subject: [PATCH 0015/1431] Themes: Improve `WP_Query` call getting global styles. Change `orderby` clause used within `WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles` to `date` to match the `WP_Query` documentation for the parameter. Props miguelaxcar, johnbillion, JeffPaul, spacedmonkey, mxbclang, mukesh27. Fixes #56900. git-svn-id: https://develop.svn.wordpress.org/trunk@54770 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-theme-json-resolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index ee153eb657abe..cf14486f6770d 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -408,7 +408,7 @@ public static function get_user_data_from_wp_global_styles( $theme, $create_post $stylesheet = $theme->get_stylesheet(); $args = array( 'posts_per_page' => 1, - 'orderby' => 'post_date', + 'orderby' => 'date', 'order' => 'desc', 'post_type' => $post_type_filter, 'post_status' => $post_status_filter, From 4edede48ebc80089b8d1699e3402298d07cd042e Mon Sep 17 00:00:00 2001 From: peterwilsoncc Date: Wed, 9 Nov 2022 00:57:02 +0000 Subject: [PATCH 0016/1431] Query: Prevent ID only queries erroring when starting the loop. Ensure only full post objects are passed to `update_post_author_caches()` when called within `WP_Query::the_post()`. This prevents an error when starting the Loop for Queries initiated with a subset of fields or IDs only. Props konyoldeath, dd32, lozula, TimothyBlynJacobs, spacedmonkey, mxbclang, peterwilsoncc. Fixes #56948. git-svn-id: https://develop.svn.wordpress.org/trunk@54771 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 10 +++- src/wp-includes/post.php | 9 ++++ tests/phpunit/tests/query/cacheResults.php | 61 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 69efdf5e2adca..1e695bd0525a0 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -3586,7 +3586,15 @@ public function the_post() { global $post; if ( ! $this->in_the_loop ) { - update_post_author_caches( $this->posts ); + // Only prime the post cache for queries limited to the ID field. + $post_ids = array_filter( $this->posts, 'is_numeric' ); + // Exclude any falsey values, such as 0. + $post_ids = array_filter( $post_ids ); + if ( $post_ids ) { + _prime_post_caches( $post_ids, $this->query_vars['update_post_term_cache'], $this->query_vars['update_post_meta_cache'] ); + } + $post_objects = array_map( 'get_post', $this->posts ); + update_post_author_caches( $post_objects ); } $this->in_the_loop = true; diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index d7e7bfec990a1..6a7c637831328 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -7461,6 +7461,15 @@ function update_post_caches( &$posts, $post_type = 'post', $update_term_cache = * @param WP_Post[] $posts Array of post objects. */ function update_post_author_caches( $posts ) { + /* + * cache_users() is a pluggable function so is not available prior + * to the `plugins_loaded` hook firing. This is to ensure against + * fatal errors when the function is not available. + */ + if ( ! function_exists( 'cache_users' ) ) { + return; + } + $author_ids = wp_list_pluck( $posts, 'post_author' ); $author_ids = array_map( 'absint', $author_ids ); $author_ids = array_unique( array_filter( $author_ids ) ); diff --git a/tests/phpunit/tests/query/cacheResults.php b/tests/phpunit/tests/query/cacheResults.php index d133dd862c2ba..6e2dde1f0f0b9 100644 --- a/tests/phpunit/tests/query/cacheResults.php +++ b/tests/phpunit/tests/query/cacheResults.php @@ -1210,4 +1210,65 @@ public function data_query_cache_with_empty_result_set() { array( 'id=>parent', 'id=>parent' ), ); } + + /** + * Ensure starting the loop warms the author cache. + * + * @since 6.1.1 + * @ticket 56948 + * + * @covers WP_Query::the_post + * + * @dataProvider data_author_cache_warmed_by_the_loop + * + * @param string $fields Query fields. + */ + public function test_author_cache_warmed_by_the_loop( $fields ) { + // Update post author for the parent post. + self::factory()->post->update_object( self::$pages[0], array( 'post_author' => self::$author_id ) ); + + self::factory()->post->create( + array( + 'post_author' => self::$author_id, + 'post_parent' => self::$pages[0], + 'post_type' => 'page', + ) + ); + + $query_1 = new WP_Query( + array( + 'post_type' => 'page', + 'fields' => $fields, + 'author' => self::$author_id, + ) + ); + + // Start the loop. + $start_loop_queries = get_num_queries(); + $query_1->the_post(); + $num_loop_queries = get_num_queries() - $start_loop_queries; + $this->assertSame( 2, $num_loop_queries, 'Unexpected number of queries while initializing the loop.' ); + + $start_author_queries = get_num_queries(); + get_user_by( 'ID', self::$author_id ); + $num_author_queries = get_num_queries() - $start_author_queries; + $this->assertSame( 0, $num_author_queries, 'Author cache is not warmed by the loop.' ); + } + + /** + * Data provider for test_author_cache_warmed_by_the_loop + * + * @return array[] + */ + public function data_author_cache_warmed_by_the_loop() { + return array( + 'fields: empty' => array( '' ), + 'fields: all' => array( 'all' ), + 'fields: ids' => array( 'ids' ), + /* + * `id=>parent` is untested pending the resolution of an existing bug. + * See https://core.trac.wordpress.org/ticket/56992 + */ + ); + } } From bbf1a34454d468ad4b9f0143741ec505c78d0cd6 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 9 Nov 2022 04:06:47 +0000 Subject: [PATCH 0017/1431] Themes: Re-order valid link pseudo classes. Re-order the link pseudo classes to follow the long term LoVe (F)HA rule when set via `theme.json`. In order that the CSS cascade behaves in a predictable manner, it's recommended that the selectors follow the order `:visited`, `:focus`/`:hover`, `:active`. As order affects the specificity, this ensures the interaction states override the visited states. CSS specificity is really quite beautiful, although complex. Props mikachan, sabernhardt, davidbaumwald, mukesh27, Mamaduka, desrosj. Fixes #56928. git-svn-id: https://develop.svn.wordpress.org/trunk@54774 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-theme-json.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 555898370f863..d0fcdeb52a3c6 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -394,13 +394,18 @@ class WP_Theme_JSON { /** * Defines which pseudo selectors are enabled for which elements. * + * The order of the selectors should be: visited, hover, focus, active. + * This is to ensure that 'visited' has the lowest specificity + * and the other selectors can always overwrite it. + * + * See https://core.trac.wordpress.org/ticket/56928. * Note: this will affect both top-level and block-level elements. * * @since 6.1.0 */ const VALID_ELEMENT_PSEUDO_SELECTORS = array( - 'link' => array( ':hover', ':focus', ':active', ':visited' ), - 'button' => array( ':hover', ':focus', ':active', ':visited' ), + 'link' => array( ':visited', ':hover', ':focus', ':active' ), + 'button' => array( ':visited', ':hover', ':focus', ':active' ), ); /** From dec912b680a3187683b5caf4259a1f5e1f294d94 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Wed, 9 Nov 2022 09:17:16 +0000 Subject: [PATCH 0018/1431] Docs: Replace HTTP links with HTTPS in `class-pop3.php` docblocks and JS vendor readme file. Props rajeshraval786, hiren1094. See #57017, #56792. git-svn-id: https://develop.svn.wordpress.org/trunk@54775 602fd350-edb4-49c9-b593-d223f7449a82 --- src/js/_enqueues/vendor/README.md | 2 +- src/wp-includes/class-pop3.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/_enqueues/vendor/README.md b/src/js/_enqueues/vendor/README.md index 41503296ab0a5..4764f0b808e94 100644 --- a/src/js/_enqueues/vendor/README.md +++ b/src/js/_enqueues/vendor/README.md @@ -11,7 +11,7 @@ In this directory you'll find vendor JavaScript packages that cannot be installe - mediaelement: https://github.com/mediaelement/mediaelement - plupload: https://github.com/moxiecode/plupload - swfupload: https://github.com/WordPress/secure-swfupload -- thickbox: http://codylindley.com/thickbox/ +- thickbox: https://codylindley.com/thickbox/ - tinymce: https://www.tiny.cloud/get-tiny/self-hosted/ - Download "TinyMCE Dev Package". This package is needed because it includes the `compat3x` plugin. diff --git a/src/wp-includes/class-pop3.php b/src/wp-includes/class-pop3.php index 3c89fe50669d8..767b74391298e 100644 --- a/src/wp-includes/class-pop3.php +++ b/src/wp-includes/class-pop3.php @@ -11,7 +11,7 @@ * POP3 class * * @copyright 1999-2011 The SquirrelMail Project Team - * @license http://opensource.org/licenses/gpl-license.php GNU Public License + * @license https://opensource.org/licenses/gpl-license.php GNU Public License * @package plugins * @subpackage mail_fetch */ From 74fb46b54dcc416937cdd3ecc43d78fafe850346 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Wed, 9 Nov 2022 09:49:51 +0000 Subject: [PATCH 0019/1431] Docs: Fix `block_editor_rest_api_preload()` parameter type. This changeset fixes the `$preload_paths` parameter type for `block_editor_rest_api_preload()` and related hooks. This parameter expects an array of strings OR an array where the path is the first element (index 0) of this array. Props chouby. Fixes #56810. See #56792. git-svn-id: https://develop.svn.wordpress.org/trunk@54776 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/block-editor.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 901ba21a3f282..7af9f2a90062a 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -571,7 +571,7 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. * @global WP_Styles $wp_styles The WP_Styles object for printing styles. * - * @param string[] $preload_paths List of paths to preload. + * @param (string|string[])[] $preload_paths List of paths to preload. * @param WP_Block_Editor_Context $block_editor_context The current block editor context. */ function block_editor_rest_api_preload( array $preload_paths, $block_editor_context ) { @@ -582,7 +582,7 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont * * @since 5.8.0 * - * @param string[] $preload_paths Array of paths to preload. + * @param (string|string[])[] $preload_paths Array of paths to preload. * @param WP_Block_Editor_Context $block_editor_context The current block editor context. */ $preload_paths = apply_filters( 'block_editor_rest_api_preload_paths', $preload_paths, $block_editor_context ); @@ -598,8 +598,8 @@ function block_editor_rest_api_preload( array $preload_paths, $block_editor_cont * @since 5.0.0 * @deprecated 5.8.0 Use the {@see 'block_editor_rest_api_preload_paths'} filter instead. * - * @param string[] $preload_paths Array of paths to preload. - * @param WP_Post $selected_post Post being edited. + * @param (string|string[])[] $preload_paths Array of paths to preload. + * @param WP_Post $selected_post Post being edited. */ $preload_paths = apply_filters_deprecated( 'block_editor_preload_paths', array( $preload_paths, $selected_post ), '5.8.0', 'block_editor_rest_api_preload_paths' ); } From 3d5454875ba02936d20ef5fc67d5cb55d7eefebe Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Wed, 9 Nov 2022 10:25:54 +0000 Subject: [PATCH 0020/1431] Coding Standards: Use consistent spelling for "cacheable" in `WP_Query::get_posts()`. Follow-up to [53941], [54768]. See #57012. git-svn-id: https://develop.svn.wordpress.org/trunk@54777 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 1e695bd0525a0..1a9baa21cac77 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -3110,13 +3110,13 @@ public function get_posts() { */ $id_query_is_cacheable = ! str_contains( strtoupper( $orderby ), ' RAND(' ); - $cachable_field_values = array( + $cacheable_field_values = array( "{$wpdb->posts}.*", "{$wpdb->posts}.ID, {$wpdb->posts}.post_parent", "{$wpdb->posts}.ID", ); - if ( ! in_array( $fields, $cachable_field_values, true ) ) { + if ( ! in_array( $fields, $cacheable_field_values, true ) ) { $id_query_is_cacheable = false; } From 19d5bd0787c9daf1d8a67bf5c0a3e997abf1f89a Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 10 Nov 2022 00:44:10 +0000 Subject: [PATCH 0021/1431] Posts, Post Types: Revert `get_page_by_title()`'s use of `WP_Query`. Revert to legacy database query in `get_pages_by_title()`. Due to the lack of `orderby` clause in the previous database query, it is not possible to gain consistent results by converting the function to a `WP_Query` wrapper. Reverts [54271, 54242, 54234]. Props Bjorn2404, 10upsimon, dilipbheda, mukesh27, spacedmonkey, TimothyBlynJacobs, rjasdfiii, stentibbing, pbiron, pento. Fixes #57039, #56991. See #57041. git-svn-id: https://develop.svn.wordpress.org/trunk@54782 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/post.php | 49 ++- tests/phpunit/tests/post/getPageByTitle.php | 340 -------------------- 2 files changed, 33 insertions(+), 356 deletions(-) delete mode 100644 tests/phpunit/tests/post/getPageByTitle.php diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 6a7c637831328..381ee526b48a2 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5766,6 +5766,8 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { * @since 2.1.0 * @since 3.0.0 The `$post_type` parameter was added. * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param string $page_title Page title. * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which * correspond to a WP_Post object, an associative array, or a numeric array, @@ -5774,25 +5776,40 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { * @return WP_Post|array|null WP_Post (or array) on success, or null on failure. */ function get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) { - $args = array( - 'title' => $page_title, - 'post_type' => $post_type, - 'post_status' => get_post_stati(), - 'posts_per_page' => 1, - 'update_post_term_cache' => false, - 'update_post_meta_cache' => false, - 'no_found_rows' => true, - 'orderby' => 'post_date ID', - 'order' => 'ASC', - ); - $query = new WP_Query( $args ); - $pages = $query->posts; + global $wpdb; - if ( empty( $pages ) ) { - return null; + if ( is_array( $post_type ) ) { + $post_type = esc_sql( $post_type ); + $post_type_in_string = "'" . implode( "','", $post_type ) . "'"; + $sql = $wpdb->prepare( + " + SELECT ID + FROM $wpdb->posts + WHERE post_title = %s + AND post_type IN ($post_type_in_string) + ", + $page_title + ); + } else { + $sql = $wpdb->prepare( + " + SELECT ID + FROM $wpdb->posts + WHERE post_title = %s + AND post_type = %s + ", + $page_title, + $post_type + ); } - return get_post( $pages[0], $output ); + $page = $wpdb->get_var( $sql ); + + if ( $page ) { + return get_post( $page, $output ); + } + + return null; } /** diff --git a/tests/phpunit/tests/post/getPageByTitle.php b/tests/phpunit/tests/post/getPageByTitle.php deleted file mode 100644 index 1c94987d5ff20..0000000000000 --- a/tests/phpunit/tests/post/getPageByTitle.php +++ /dev/null @@ -1,340 +0,0 @@ -post->create_many( - 2, - array( - 'post_type' => 'page', - ) - ); - - // Fill the database with some attachments. - $factory->post->create_many( - 2, - array( - 'post_type' => 'attachment', - ) - ); - - // Fill the database with some test post types. - register_post_type( 'wptests_pt' ); - $factory->post->create_many( - 2, - array( - 'post_type' => 'wptests_pt', - ) - ); - } - - /** - * @ticket 36905 - */ - public function test_get_page_by_title_priority() { - $attachment = self::factory()->post->create_and_get( - array( - 'post_title' => 'some-other-page', - 'post_type' => 'attachment', - ) - ); - $page = self::factory()->post->create_and_get( - array( - 'post_title' => 'some-page', - 'post_type' => 'page', - ) - ); - - $this->assertEquals( $page, get_page_by_title( 'some-page' ), 'should return a post of the requested type before returning an attachment.' ); - - $this->assertEquals( $attachment, get_page_by_title( 'some-other-page', OBJECT, 'attachment' ), "will still select an attachment when a post of the requested type doesn't exist." ); - } - - /** - * @ticket 36905 - */ - public function test_should_match_top_level_page() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $page, $found->ID ); - } - - /** - * @ticket 36905 - * @ticket 56609 - */ - public function test_should_be_case_insensitive_match() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'Foo', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $page, $found->ID ); - } - - /** - * Test the oldest published post is matched first. - * - * Per the docs: in case of more than one post having the same title, - * it will check the oldest publication date, not the smallest ID. - * - * @ticket 36905 - * @ticket 56609 - */ - public function test_should_match_oldest_published_date_when_titles_match() { - self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - $old_page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - 'post_date' => '1984-01-11 05:00:00', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $old_page, $found->ID ); - } - - /** - * @ticket 36905 - */ - public function test_inherit() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - 'post_status' => 'inherit', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $page, $found->ID ); - } - - /** - * @ticket 36905 - */ - public function test_should_obey_post_type() { - register_post_type( 'wptests_pt' ); - - $page = self::factory()->post->create( - array( - 'post_type' => 'wptests_pt', - 'post_title' => 'foo', - ) - ); - - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return null, as post type does not match' ); - - $found = get_page_by_title( 'foo', OBJECT, 'wptests_pt' ); - $this->assertSame( $page, $found->ID, 'Should return find post, as post type does do match' ); - } - - - /** - * @ticket 36905 - */ - public function test_should_hit_cache() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertSame( $page, $found->ID, 'Should return find page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo' ); - $this->assertSame( $page, $found->ID, 'Should return find page on second run.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_bad_title_should_be_cached() { - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page on second run.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_bad_title_served_from_cache_should_not_fall_back_on_current_post() { - global $post; - - // Fake the global. - $post = self::factory()->post->create_and_get(); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page on second run.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_cache_should_not_match_post_in_different_post_type_with_same_title() { - register_post_type( 'wptests_pt' ); - - $p1 = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - $p2 = self::factory()->post->create( - array( - 'post_type' => 'wptests_pt', - 'post_title' => 'foo', - ) - ); - - // Prime cache for the page. - $found = get_page_by_title( 'foo' ); - $this->assertSame( $p1, $found->ID, 'Should find a page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo', OBJECT, 'wptests_pt' ); - $this->assertSame( $p2, $found->ID, 'Should find a post with post type wptests_pt.' ); - ++$num_queries; - $this->assertSame( $num_queries, get_num_queries(), 'Should result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_cache_should_be_invalidated_when_post_title_is_edited() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertSame( $page, $found->ID, 'Should find a page.' ); - - wp_update_post( - array( - 'ID' => $page, - 'post_title' => 'bar', - ) - ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'bar' ); - $this->assertSame( $page, $found->ID, 'Should find a page with the new title.' ); - ++$num_queries; - $this->assertSame( $num_queries, get_num_queries(), 'Should result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_output_param_should_be_obeyed_for_cached_value() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - - $num_queries = get_num_queries(); - $this->assertSame( $page, $found->ID, 'Should find a page.' ); - - $object = get_page_by_title( 'foo', OBJECT ); - $this->assertIsObject( $object, 'Should be an object.' ); - $this->assertSame( $page, $object->ID, 'Should match post id.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - - $array_n = get_page_by_title( 'foo', ARRAY_N ); - ++$num_queries; // Add one database query for loading of post metadata. - $this->assertIsArray( $array_n, 'Should be numbric array.' ); - $this->assertSame( $page, $array_n[0], 'Should match post id.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - - $array_a = get_page_by_title( 'foo', ARRAY_A ); - $this->assertIsArray( $array_a, 'Should be associative array.' ); - $this->assertSame( $page, $array_a['ID'], 'Should match post id.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * Ensure get_page_by_title() only runs the query once. - * - * @ticket 56721 - * @covers ::get_page_by_title - */ - public function test_should_not_run_query_more_than_once() { - $page = self::factory()->post->create_and_get( - array( - 'post_title' => 'some-page', - 'post_type' => 'page', - ) - ); - - // Use the `pre_get_posts` hook to ensure the query is only run once. - $ma = new MockAction(); - add_action( 'pre_get_posts', array( $ma, 'action' ) ); - - get_page_by_title( 'some-page' ); - $this->assertSame( 1, $ma->get_call_count(), 'Query does not run exactly once.' ); - } -} From b9304148f17278dbe9a4199a1143b083188f0d61 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 10 Nov 2022 02:59:56 +0000 Subject: [PATCH 0022/1431] Canonical: Protect against error for term not exists queries. Prevent term `NOT EXISTS` queries causing `redirect_canonical()` to throw a fatal error in PHP 8 and above, or a warning in earlier versions. This ensures the `tax_query`'s `terms` property both exists and is countable before attempting to count it. Props codesdnc, SergeyBiryukov, kadamwhite, costdev, miguelaxcar. Fixes #55955. git-svn-id: https://develop.svn.wordpress.org/trunk@54785 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/canonical.php | 4 +++- tests/phpunit/tests/canonical.php | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/canonical.php b/src/wp-includes/canonical.php index 9404ed6ea5d24..5e8efb0bae644 100644 --- a/src/wp-includes/canonical.php +++ b/src/wp-includes/canonical.php @@ -331,7 +331,9 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) { $term_count = 0; foreach ( $wp_query->tax_query->queried_terms as $tax_query ) { - $term_count += count( $tax_query['terms'] ); + if ( isset( $tax_query['terms'] ) && is_countable( $tax_query['terms'] ) ) { + $term_count += count( $tax_query['terms'] ); + } } $obj = $wp_query->get_queried_object(); diff --git a/tests/phpunit/tests/canonical.php b/tests/phpunit/tests/canonical.php index 63e5c4078bb51..6d16402112c21 100644 --- a/tests/phpunit/tests/canonical.php +++ b/tests/phpunit/tests/canonical.php @@ -375,4 +375,31 @@ public function test_utf8_query_keys_canonical() { delete_option( 'page_on_front' ); } + + /** + * Ensure NOT EXISTS queries do not trigger not-countable or undefined array key errors. + * + * @ticket 55955 + */ + public function test_feed_canonical_with_not_exists_query() { + // Set a NOT EXISTS tax_query on the global query. + $global_query = $GLOBALS['wp_query']; + $GLOBALS['wp_query'] = new WP_Query( + array( + 'post_type' => 'post', + 'tax_query' => array( + array( + 'taxonomy' => 'post_format', + 'operator' => 'NOT EXISTS', + ), + ), + ) + ); + + $url = redirect_canonical( get_term_feed_link( self::$terms['/category/parent/'] ), false ); + // Restore original global. + $GLOBALS['wp_query'] = $global_query; + + $this->assertNull( $url ); + } } From 2e375d47908ab85633cbe001ae7fa23d0b5fab6e Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Thu, 10 Nov 2022 09:38:58 +0000 Subject: [PATCH 0023/1431] Text Changes: Replace "Full site editing" with "Site Editor". This changeset replaces the various occurrences of "Full site editing" with "Site Editor" as it is the new official name of the feature. For more background about this change, see https://make.wordpress.org/updates/2022/11/04/site-editor-a-more-user-friendly-name/. Props audrasjb, peterwilsoncc, poena. Fixes #57026. git-svn-id: https://develop.svn.wordpress.org/trunk@54786 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/about.php | 2 +- src/wp-admin/includes/theme.php | 3 ++- src/wp-admin/site-editor.php | 2 +- src/wp-content/themes/twentytwentytwo/readme.txt | 2 +- src/wp-content/themes/twentytwentytwo/style.css | 2 +- src/wp-includes/block-template.php | 2 +- src/wp-includes/script-loader.php | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/wp-admin/about.php b/src/wp-admin/about.php index 87e321d00b41b..4fff85d2a34b5 100644 --- a/src/wp-admin/about.php +++ b/src/wp-admin/about.php @@ -214,7 +214,7 @@

a filter for block themes, and a pattern preview gives a better sense of what the theme might look like while exploring different themes and patterns.' ), esc_url( __( 'https://wordpress.org/themes/tags/full-site-editing/' ) ) ); diff --git a/src/wp-admin/includes/theme.php b/src/wp-admin/includes/theme.php index 020927a154960..3d88d94178f0a 100644 --- a/src/wp-admin/includes/theme.php +++ b/src/wp-admin/includes/theme.php @@ -299,6 +299,7 @@ function get_theme_update_available( $theme ) { * and 'Full Site Editing' features. * @since 5.5.0 Added 'Wide Blocks' layout option. * @since 5.8.1 Added 'Template Editing' feature. + * @since 6.2.0 Replaced 'Full Site Editing' feature name with 'Site Editor'. * * @param bool $api Optional. Whether try to fetch tags from the WordPress.org API. Defaults to true. * @return array Array of features keyed by category with translations keyed by slug. @@ -331,7 +332,7 @@ function get_theme_feature_list( $api = true ) { 'featured-image-header' => __( 'Featured Image Header' ), 'featured-images' => __( 'Featured Images' ), 'footer-widgets' => __( 'Footer Widgets' ), - 'full-site-editing' => __( 'Full Site Editing' ), + 'full-site-editing' => __( 'Site Editor' ), 'full-width-template' => __( 'Full Width Template' ), 'post-formats' => __( 'Post Formats' ), 'sticky-post' => __( 'Sticky Post' ), diff --git a/src/wp-admin/site-editor.php b/src/wp-admin/site-editor.php index 64ed4b9c27cad..d2880b0a4f271 100644 --- a/src/wp-admin/site-editor.php +++ b/src/wp-admin/site-editor.php @@ -20,7 +20,7 @@ } if ( ! ( current_theme_supports( 'block-template-parts' ) || wp_is_block_theme() ) ) { - wp_die( __( 'The theme you are currently using is not compatible with Full Site Editing.' ) ); + wp_die( __( 'The theme you are currently using is not compatible with the Site Editor.' ) ); } $is_template_part_editor = isset( $_GET['postType'] ) && 'wp_template_part' === sanitize_key( $_GET['postType'] ); diff --git a/src/wp-content/themes/twentytwentytwo/readme.txt b/src/wp-content/themes/twentytwentytwo/readme.txt index 20663b579f56c..72f079badfbbd 100644 --- a/src/wp-content/themes/twentytwentytwo/readme.txt +++ b/src/wp-content/themes/twentytwentytwo/readme.txt @@ -11,7 +11,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html Built on a solidly designed foundation, Twenty Twenty-Two embraces the idea that everyone deserves a truly unique website. The theme’s subtle styles are inspired by the diversity and versatility of birds: its typography is lightweight yet strong, its color palette is drawn from nature, and its layout elements sit gently on the page. -The true richness of Twenty Twenty-Two lies in its opportunity for customization. The theme is built to take advantage of the Full Site Editing features introduced in WordPress 5.9, which means that colors, typography, and the layout of every single page on your site can be customized to suit your vision. It also includes dozens of block patterns, opening the door to a wide range of professionally designed layouts in just a few clicks. +The true richness of Twenty Twenty-Two lies in its opportunity for customization. The theme is built to take advantage of the Site Editor features introduced in WordPress 5.9, which means that colors, typography, and the layout of every single page on your site can be customized to suit your vision. It also includes dozens of block patterns, opening the door to a wide range of professionally designed layouts in just a few clicks. Whether you’re building a single-page website, a blog, a business website, or a portfolio, Twenty Twenty-Two will help you create a site that is uniquely yours. diff --git a/src/wp-content/themes/twentytwentytwo/style.css b/src/wp-content/themes/twentytwentytwo/style.css index 0f46424d18c3a..f611fad6c141a 100644 --- a/src/wp-content/themes/twentytwentytwo/style.css +++ b/src/wp-content/themes/twentytwentytwo/style.css @@ -3,7 +3,7 @@ Theme Name: Twenty Twenty-Two Theme URI: https://wordpress.org/themes/twentytwentytwo/ Author: the WordPress team Author URI: https://wordpress.org/ -Description: Built on a solidly designed foundation, Twenty Twenty-Two embraces the idea that everyone deserves a truly unique website. The theme’s subtle styles are inspired by the diversity and versatility of birds: its typography is lightweight yet strong, its color palette is drawn from nature, and its layout elements sit gently on the page. The true richness of Twenty Twenty-Two lies in its opportunity for customization. The theme is built to take advantage of the Full Site Editing features introduced in WordPress 5.9, which means that colors, typography, and the layout of every single page on your site can be customized to suit your vision. It also includes dozens of block patterns, opening the door to a wide range of professionally designed layouts in just a few clicks. Whether you’re building a single-page website, a blog, a business website, or a portfolio, Twenty Twenty-Two will help you create a site that is uniquely yours. +Description: Built on a solidly designed foundation, Twenty Twenty-Two embraces the idea that everyone deserves a truly unique website. The theme’s subtle styles are inspired by the diversity and versatility of birds: its typography is lightweight yet strong, its color palette is drawn from nature, and its layout elements sit gently on the page. The true richness of Twenty Twenty-Two lies in its opportunity for customization. The theme is built to take advantage of the Site Editor features introduced in WordPress 5.9, which means that colors, typography, and the layout of every single page on your site can be customized to suit your vision. It also includes dozens of block patterns, opening the door to a wide range of professionally designed layouts in just a few clicks. Whether you’re building a single-page website, a blog, a business website, or a portfolio, Twenty Twenty-Two will help you create a site that is uniquely yours. Requires at least: 5.9 Tested up to: 6.1 Requires PHP: 5.6 diff --git a/src/wp-includes/block-template.php b/src/wp-includes/block-template.php index 0aa571117e899..38d8e2b729773 100644 --- a/src/wp-includes/block-template.php +++ b/src/wp-includes/block-template.php @@ -43,7 +43,7 @@ function _add_template_loader_filters() { * @param string $template Path to the template. See locate_template(). * @param string $type Sanitized filename without extension. * @param string[] $templates A list of template candidates, in descending order of priority. - * @return string The path to the Full Site Editing template canvas file, or the fallback PHP template. + * @return string The path to the Site Editor template canvas file, or the fallback PHP template. */ function locate_block_template( $template, $type, array $templates ) { global $_wp_current_template_content; diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index f22928dba1cda..956d4338efbd9 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -1287,7 +1287,7 @@ function wp_default_scripts( $scripts ) { 'invalidValue' => __( 'Invalid value.' ), 'blockThemeNotification' => sprintf( /* translators: 1: Link to Site Editor documentation on HelpHub, 2: HTML button. */ - __( 'Hurray! Your theme supports Full Site Editing with blocks. Tell me more. %2$s' ), + __( 'Hurray! Your theme supports Site Editing with blocks. Tell me more. %2$s' ), __( 'https://wordpress.org/support/article/site-editor/' ), sprintf( '', From 27760044b352e03d9d8d09f12a99272184094406 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Thu, 10 Nov 2022 11:31:15 +0000 Subject: [PATCH 0024/1431] Text Changes: Remove capitalization on "site editing". Follow-up to [54786]. Props ocean90. See #57026. git-svn-id: https://develop.svn.wordpress.org/trunk@54787 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 956d4338efbd9..27defd0659460 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -1287,7 +1287,7 @@ function wp_default_scripts( $scripts ) { 'invalidValue' => __( 'Invalid value.' ), 'blockThemeNotification' => sprintf( /* translators: 1: Link to Site Editor documentation on HelpHub, 2: HTML button. */ - __( 'Hurray! Your theme supports Site Editing with blocks. Tell me more. %2$s' ), + __( 'Hurray! Your theme supports site editing with blocks. Tell me more. %2$s' ), __( 'https://wordpress.org/support/article/site-editor/' ), sprintf( '', From 7dc6546df9e39c32cee7e0c4e78d44030625e33c Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Thu, 10 Nov 2022 12:08:49 +0000 Subject: [PATCH 0025/1431] Text Changes: Update `@since` mentions for [54786] changes. This updates the `@since` mention of `get_theme_feature_list()` as this changeset is going to be backported to 6.1.1. Follow-up to [54786]. See #57026. git-svn-id: https://develop.svn.wordpress.org/trunk@54788 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/theme.php b/src/wp-admin/includes/theme.php index 3d88d94178f0a..6fe18cdff1f2e 100644 --- a/src/wp-admin/includes/theme.php +++ b/src/wp-admin/includes/theme.php @@ -299,7 +299,7 @@ function get_theme_update_available( $theme ) { * and 'Full Site Editing' features. * @since 5.5.0 Added 'Wide Blocks' layout option. * @since 5.8.1 Added 'Template Editing' feature. - * @since 6.2.0 Replaced 'Full Site Editing' feature name with 'Site Editor'. + * @since 6.1.1 Replaced 'Full Site Editing' feature name with 'Site Editor'. * * @param bool $api Optional. Whether try to fetch tags from the WordPress.org API. Defaults to true. * @return array Array of features keyed by category with translations keyed by slug. From 551c3d6619b7e88742911ca2dd3aed77a71cb0dc Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Thu, 10 Nov 2022 12:20:48 +0000 Subject: [PATCH 0026/1431] Update/Install: Deactivate Gutenberg plugin version older than 14.1. Resolves a fatal error due to `get_template_hierarchy()` due to incompatible older Gutenberg versions. [54269] introduced this new function for 6.1. The function was introduced in Gutenberg 13.9.0. However, it was not guarded to protect the plugin from when the function was loaded in Core. Gutenberg 14.1.0 added the `function_exists()` guard to protect the plugin from the fatal error. Minimum compatible version: This commit changes the Gutenberg minimum compatible version number to 14.1. For versions older than 14.1, the plugin will deactivate when upgrading Core to 6.1 or newer. Function rename: Past commits renamed the upgrade function by changing Core's version number. This commit renames the function to be generic, i.e. `_upgrade_core_deactivate_incompatible_plugins()` and adopts the `@since [reason]` strategy to track historical changes to the function. Follow-up to [54269], [52199], [52166], [52165], [51180]. Props namithjawahar, hellofromTonya, azaozz, desrosj, ironprogrammer. Fixes #56985. git-svn-id: https://develop.svn.wordpress.org/trunk@54789 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/update-core.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/wp-admin/includes/update-core.php b/src/wp-admin/includes/update-core.php index af7ae29d7238a..85a4f48332fd4 100644 --- a/src/wp-admin/includes/update-core.php +++ b/src/wp-admin/includes/update-core.php @@ -1437,8 +1437,8 @@ function update_core( $from, $to ) { // Deactivate the REST API plugin if its version is 2.0 Beta 4 or lower. _upgrade_440_force_deactivate_incompatible_plugins(); - // Deactivate the Gutenberg plugin if its version is 11.8 or lower. - _upgrade_590_force_deactivate_incompatible_plugins(); + // Deactivate incompatible plugins. + _upgrade_core_deactivate_incompatible_plugins(); // Upgrade DB with separate request. /** This filter is documented in wp-admin/includes/update-core.php */ @@ -1637,14 +1637,16 @@ function _upgrade_440_force_deactivate_incompatible_plugins() { /** * @access private * @ignore - * @since 5.9.0 + * @since 5.8.0 + * @since 5.9.0 The minimum compatible version of Gutenberg is 11.9. + * @since 6.1.1 The minimum compatible version of Gutenberg is 14.1. */ -function _upgrade_590_force_deactivate_incompatible_plugins() { - if ( defined( 'GUTENBERG_VERSION' ) && version_compare( GUTENBERG_VERSION, '11.9', '<' ) ) { +function _upgrade_core_deactivate_incompatible_plugins() { + if ( defined( 'GUTENBERG_VERSION' ) && version_compare( GUTENBERG_VERSION, '14.1', '<' ) ) { $deactivated_gutenberg['gutenberg'] = array( 'plugin_name' => 'Gutenberg', 'version_deactivated' => GUTENBERG_VERSION, - 'version_compatible' => '11.9', + 'version_compatible' => '14.1', ); if ( is_plugin_active_for_network( 'gutenberg/gutenberg.php' ) ) { $deactivated_plugins = get_site_option( 'wp_force_deactivated_plugins', array() ); From 302ba4f45f96490f5947f324228f20e0cbe28583 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Thu, 10 Nov 2022 13:05:57 +0000 Subject: [PATCH 0027/1431] Tests: Correct the test for `get_blogaddress_by_id()` with a non-existing ID. Due to auto-increment, when running various test groups or classes separately, in this case running all of the tests under `phpunit/tests/multisite/` by including the `--filter Tests_Multisite` parameter, it is entirely possible for the blog ID 42 to actually exist, making the test's assumption incorrect. By using `PHP_INT_MAX` instead, we can avoid a collision with a fixture of another test. Follow-up to [31157]. See #56793. git-svn-id: https://develop.svn.wordpress.org/trunk@54791 602fd350-edb4-49c9-b593-d223f7449a82 --- tests/phpunit/tests/multisite/site.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/multisite/site.php b/tests/phpunit/tests/multisite/site.php index 990003cde9c48..6507fbbf22de3 100644 --- a/tests/phpunit/tests/multisite/site.php +++ b/tests/phpunit/tests/multisite/site.php @@ -687,10 +687,10 @@ public function test_get_blogaddress_by_id_with_valid_id() { } /** - * Tests returning the appropriate response for a invalid id given. + * Tests returning an empty string for a non-existing ID. */ public function test_get_blogaddress_by_id_with_invalid_id() { - $blogaddress = get_blogaddress_by_id( 42 ); + $blogaddress = get_blogaddress_by_id( PHP_INT_MAX ); $this->assertSame( '', $blogaddress ); } From e180b742cef7b5624d9f99a7e049304c51c494b7 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Thu, 10 Nov 2022 17:14:43 +0000 Subject: [PATCH 0028/1431] Docs: Improve globals documentation in `unregister_taxonomy()` and `wp_term_is_shared()`. Props upadalavipul, mukesh27. Fixes #57058. See #56792. git-svn-id: https://develop.svn.wordpress.org/trunk@54794 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/taxonomy.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 1dfcf4d9c5278..be19a141486d2 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -558,7 +558,6 @@ function register_taxonomy( $taxonomy, $object_type, $args = array() ) { * * @since 4.5.0 * - * @global WP $wp Current WordPress environment instance. * @global WP_Taxonomy[] $wp_taxonomies List of taxonomies. * * @param string $taxonomy Taxonomy name. @@ -4522,6 +4521,8 @@ function wp_get_split_term( $old_term_id, $taxonomy ) { * * @since 4.4.0 * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param int $term_id Term ID. * @return bool Returns false if a term is not shared between multiple taxonomies or * if splitting shared taxonomy terms is finished. From 1ab43acf4b50b406c48caf43e366cf95afea911c Mon Sep 17 00:00:00 2001 From: Dominik Schilling Date: Thu, 10 Nov 2022 19:38:20 +0000 Subject: [PATCH 0029/1431] I18N: Always pass `$locale` to `load_textdomain()`. In [53874] the optional `$locale` parameter was added to `load_textdomain()`. While most `load_textdomain()` calls in core were were updated, some were missed. Passing the original locale avoids the need to call `determine_locale()` by `load_textdomain()` which is used as a fallback. Props ocean90, swissspidy, desrosj. See #57060. git-svn-id: https://develop.svn.wordpress.org/trunk@54797 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/admin.php | 4 +++- src/wp-includes/functions.php | 2 +- src/wp-includes/load.php | 4 ++-- .../endpoints/class-wp-rest-site-health-controller.php | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/wp-admin/includes/admin.php b/src/wp-admin/includes/admin.php index c2c1890ec864c..ce2ec0c68b855 100644 --- a/src/wp-admin/includes/admin.php +++ b/src/wp-admin/includes/admin.php @@ -13,7 +13,9 @@ * some setup was skipped. Make sure the admin message catalog is loaded since * load_default_textdomain() will not have done so in this context. */ - load_textdomain( 'default', WP_LANG_DIR . '/admin-' . get_locale() . '.mo' ); + $admin_locale = get_locale(); + load_textdomain( 'default', WP_LANG_DIR . '/admin-' . $admin_locale . '.mo', $admin_locale ); + unset( $admin_locale ); } /** WordPress Administration Hooks */ diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 8e14894f8b079..a38c78ccacae0 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -6351,7 +6351,7 @@ function wp_timezone_choice( $selected_zone, $locale = null ) { $locale_loaded = $locale ? $locale : get_locale(); $mofile = WP_LANG_DIR . '/continents-cities-' . $locale_loaded . '.mo'; unload_textdomain( 'continents-cities' ); - load_textdomain( 'continents-cities', $mofile ); + load_textdomain( 'continents-cities', $mofile, $locale_loaded ); $mo_loaded = true; } diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 4986603e9ea77..bb0db65b942b8 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -1392,9 +1392,9 @@ function wp_load_translations_early() { foreach ( $locales as $locale ) { foreach ( $locations as $location ) { if ( file_exists( $location . '/' . $locale . '.mo' ) ) { - load_textdomain( 'default', $location . '/' . $locale . '.mo' ); + load_textdomain( 'default', $location . '/' . $locale . '.mo', $locale ); if ( defined( 'WP_SETUP_CONFIG' ) && file_exists( $location . '/admin-' . $locale . '.mo' ) ) { - load_textdomain( 'default', $location . '/admin-' . $locale . '.mo' ); + load_textdomain( 'default', $location . '/admin-' . $locale . '.mo', $locale ); } break 2; } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php index 954165b825866..43e6676eff0ea 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php @@ -336,7 +336,7 @@ protected function load_admin_textdomain() { // Accounts for inner REST API requests in the admin. if ( ! is_admin() ) { $locale = determine_locale(); - load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo" ); + load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo", $locale ); } } From 8368eef8e00e3f9ab8a2bf2ca7093b07e05040b5 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Thu, 10 Nov 2022 22:14:53 +0000 Subject: [PATCH 0030/1431] Editor: Avoid running certain logic around `theme.json` parsing unnecessarily for classic themes. Here's what it does: * Do not load and parse `theme-i18n.json` schema if the theme does not have a `theme.json` file. * Fix the variable caching layer around the theme's `theme.json` parsing so that a parent's theme `theme.json` is cached as well. * Do not run a `WP_Query` for global styles for a user when the theme does not have a `theme.json`. In a basic WordPress setup, this changeset improves `wp_head` execution time for classic themes by 10%, and overall response time for both block themes and classic themes by 4%. This may seem like a small win, but 4% reduced overall response time is actually quite a bit for one change, and it is worth mentioning that this is just one of several other little performance tweaks which are being worked on to improve performance of `theme.json` parsing further. Props flixos90, manuilov, oandregal, peterwilsoncc, spacedmonkey. Fixes #56945. git-svn-id: https://develop.svn.wordpress.org/trunk@54799 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-theme-json-resolver.php | 54 ++++++---- src/wp-includes/class-wp-theme-json.php | 3 +- .../tests/theme/wpThemeJsonResolver.php | 98 ++++++++++++++----- 3 files changed, 112 insertions(+), 43 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index cf14486f6770d..c166283af30f1 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -246,8 +246,13 @@ public static function get_theme_data( $deprecated = array(), $options = array() $options = wp_parse_args( $options, array( 'with_supports' => true ) ); if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) { - $theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) ); - $theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) ); + $theme_json_file = static::get_file_path_from_theme( 'theme.json' ); + if ( '' !== $theme_json_file ) { + $theme_json_data = static::read_json_file( $theme_json_file ); + $theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) ); + } else { + $theme_json_data = array(); + } /** * Filters the data provided by the theme for global styles and settings. @@ -259,20 +264,23 @@ public static function get_theme_data( $deprecated = array(), $options = array() $theme_json = apply_filters( 'wp_theme_json_data_theme', new WP_Theme_JSON_Data( $theme_json_data, 'theme' ) ); $theme_json_data = $theme_json->get_data(); static::$theme = new WP_Theme_JSON( $theme_json_data ); - } - - if ( wp_get_theme()->parent() ) { - // Get parent theme.json. - $parent_theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json', true ) ); - $parent_theme_json_data = static::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) ); - $parent_theme = new WP_Theme_JSON( $parent_theme_json_data ); - /* - * Merge the child theme.json into the parent theme.json. - * The child theme takes precedence over the parent. - */ - $parent_theme->merge( static::$theme ); - static::$theme = $parent_theme; + if ( wp_get_theme()->parent() ) { + // Get parent theme.json. + $parent_theme_json_file = static::get_file_path_from_theme( 'theme.json', true ); + if ( '' !== $parent_theme_json_file ) { + $parent_theme_json_data = static::read_json_file( $parent_theme_json_file ); + $parent_theme_json_data = static::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) ); + $parent_theme = new WP_Theme_JSON( $parent_theme_json_data ); + + /* + * Merge the child theme.json into the parent theme.json. + * The child theme takes precedence over the parent. + */ + $parent_theme->merge( static::$theme ); + static::$theme = $parent_theme; + } + } } if ( ! $options['with_supports'] ) { @@ -403,6 +411,18 @@ public static function get_user_data_from_wp_global_styles( $theme, $create_post if ( ! $theme instanceof WP_Theme ) { $theme = wp_get_theme(); } + + /* + * Bail early if the theme does not support a theme.json. + * + * Since WP_Theme_JSON_Resolver::theme_has_support() only supports the active + * theme, the extra condition for whether $theme is the active theme is + * present here. + */ + if ( $theme->get_stylesheet() === get_stylesheet() && ! static::theme_has_support() ) { + return array(); + } + $user_cpt = array(); $post_type_filter = 'wp_global_styles'; $stylesheet = $theme->get_stylesheet(); @@ -582,8 +602,8 @@ public static function get_user_global_styles_post_id() { public static function theme_has_support() { if ( ! isset( static::$theme_has_support ) ) { static::$theme_has_support = ( - is_readable( static::get_file_path_from_theme( 'theme.json' ) ) || - is_readable( static::get_file_path_from_theme( 'theme.json', true ) ) + static::get_file_path_from_theme( 'theme.json' ) !== '' || + static::get_file_path_from_theme( 'theme.json', true ) !== '' ); } diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index d0fcdeb52a3c6..df5bd20d84917 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -2939,7 +2939,8 @@ public function get_data() { public function set_spacing_sizes() { $spacing_scale = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'spacingScale' ), array() ); - if ( ! is_numeric( $spacing_scale['steps'] ) + if ( ! isset( $spacing_scale['steps'] ) + || ! is_numeric( $spacing_scale['steps'] ) || ! isset( $spacing_scale['mediumStep'] ) || ! isset( $spacing_scale['unit'] ) || ! isset( $spacing_scale['operator'] ) diff --git a/tests/phpunit/tests/theme/wpThemeJsonResolver.php b/tests/phpunit/tests/theme/wpThemeJsonResolver.php index 524fade2d8f85..1cbc519255cf4 100644 --- a/tests/phpunit/tests/theme/wpThemeJsonResolver.php +++ b/tests/phpunit/tests/theme/wpThemeJsonResolver.php @@ -33,13 +33,6 @@ class Tests_Theme_wpThemeJsonResolver extends WP_UnitTestCase { */ private $orig_theme_dir; - /** - * Queries. - * - * @var array - */ - private $queries = array(); - /** * WP_Theme_JSON_Resolver::$blocks_cache property. * @@ -105,7 +98,7 @@ public function set_up() { add_filter( 'theme_root', array( $this, 'filter_set_theme_root' ) ); add_filter( 'stylesheet_root', array( $this, 'filter_set_theme_root' ) ); add_filter( 'template_root', array( $this, 'filter_set_theme_root' ) ); - $this->queries = array(); + // Clear caches. wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); @@ -129,13 +122,6 @@ public function filter_set_locale_to_polish() { return 'pl_PL'; } - function filter_db_query( $query ) { - if ( preg_match( '#post_type = \'wp_global_styles\'#', $query ) ) { - $this->queries[] = $query; - } - return $query; - } - /** * @ticket 52991 * @ticket 54336 @@ -634,17 +620,26 @@ function test_merges_child_theme_json_into_parent_theme_json() { * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles */ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries() { + // Switch to a theme that does have support. + switch_theme( 'block-theme' ); wp_set_current_user( self::$administrator_id ); $theme = wp_get_theme(); WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); - add_filter( 'query', array( $this, 'filter_db_query' ) ); - $query_count = count( $this->queries ); + $global_styles_query_count = 0; + add_filter( + 'query', + function( $query ) use ( &$global_styles_query_count ) { + if ( preg_match( '#post_type = \'wp_global_styles\'#', $query ) ) { + $global_styles_query_count++; + } + return $query; + } + ); for ( $i = 0; $i < 3; $i++ ) { WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); WP_Theme_JSON_Resolver::clean_cached_data(); } - $query_count = count( $this->queries ) - $query_count; - $this->assertSame( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' ); + $this->assertSame( 0, $global_styles_query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' ); $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); @@ -652,40 +647,64 @@ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries( $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true ); $this->assertNotEmpty( $user_cpt, 'User CPT is expected not to be empty.' ); - $query_count = count( $this->queries ); + $global_styles_query_count = 0; for ( $i = 0; $i < 3; $i ++ ) { $new_user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); WP_Theme_JSON_Resolver::clean_cached_data(); $this->assertSameSets( $user_cpt, $new_user_cpt, "User CPTs do not match on run {$i}." ); } - $query_count = count( $this->queries ) - $query_count; - $this->assertSame( 1, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type after creation.' ); + $this->assertSame( 1, $global_styles_query_count, 'Unexpected SQL queries detected for the wp_global_style post type after creation.' ); } /** * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles */ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries_for_logged_out_users() { + // Switch to a theme that does have support. + switch_theme( 'block-theme' ); $theme = wp_get_theme(); WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); - add_filter( 'query', array( $this, 'filter_db_query' ) ); - $query_count = count( $this->queries ); + $query_count = get_num_queries(); for ( $i = 0; $i < 3; $i++ ) { WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); WP_Theme_JSON_Resolver::clean_cached_data(); } - $query_count = count( $this->queries ) - $query_count; + $query_count = get_num_queries() - $query_count; $this->assertSame( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type prior to creation.' ); $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); } + /** + * @ticket 56945 + * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles + */ + function test_get_user_data_from_wp_global_styles_does_not_run_for_theme_without_support() { + // The 'default' theme does not support theme.json. + switch_theme( 'default' ); + wp_set_current_user( self::$administrator_id ); + $theme = wp_get_theme(); + + $start_queries = get_num_queries(); + + // When theme.json is not supported, the method should not run a query and always return an empty result. + $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); + $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); + $this->assertSame( 0, get_num_queries() - $start_queries, 'Unexpected SQL query detected for theme without theme.json support.' ); + + $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true ); + $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); + $this->assertSame( 0, get_num_queries() - $start_queries, 'Unexpected SQL query detected for theme without theme.json support.' ); + } + /** * @ticket 55392 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles */ function test_get_user_data_from_wp_global_styles_does_exist() { + // Switch to a theme that does have support. + switch_theme( 'block-theme' ); $theme = wp_get_theme(); $post1 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true ); $this->assertIsArray( $post1 ); @@ -701,6 +720,8 @@ function test_get_user_data_from_wp_global_styles_does_exist() { * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles */ function test_get_user_data_from_wp_global_styles_create_post() { + // Switch to a theme that does have support. + switch_theme( 'block-theme' ); $theme = wp_get_theme( 'testing' ); $post1 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); $this->assertIsArray( $post1 ); @@ -718,6 +739,8 @@ function test_get_user_data_from_wp_global_styles_create_post() { * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles */ function test_get_user_data_from_wp_global_styles_filter_state() { + // Switch to a theme that does have support. + switch_theme( 'block-theme' ); $theme = wp_get_theme( 'foo' ); $post1 = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true, array( 'publish' ) ); $this->assertIsArray( $post1 ); @@ -749,4 +772,29 @@ function test_get_theme_data_theme_supports_overrides_theme_json() { $line_height = $current_settings['typography']['lineHeight']; $this->assertTrue( $line_height, 'lineHeight setting after add_theme_support() should be true.' ); } + + /** + * @ticket 56945 + * @covers WP_Theme_JSON_Resolver::get_theme_data + */ + function test_get_theme_data_does_not_parse_theme_json_if_not_present() { + // The 'default' theme does not support theme.json. + switch_theme( 'default' ); + + $theme_json_resolver = new WP_Theme_JSON_Resolver(); + + // Force-unset $i18n_schema property to "unload" translation schema. + $property = new ReflectionProperty( $theme_json_resolver, 'i18n_schema' ); + $property->setAccessible( true ); + $property->setValue( null ); + + // A completely empty theme.json data set still has the 'version' key when parsed. + $empty_theme_json = array( 'version' => WP_Theme_JSON::LATEST_SCHEMA ); + + // Call using 'with_supports' set to false, so that the method only considers theme.json. + $theme_data = $theme_json_resolver->get_theme_data( array(), array( 'with_supports' => false ) ); + $this->assertInstanceOf( 'WP_Theme_JSON', $theme_data, 'Theme data should be an instance of WP_Theme_JSON.' ); + $this->assertSame( $empty_theme_json, $theme_data->get_raw_data(), 'Theme data should be empty without theme support.' ); + $this->assertNull( $property->getValue(), 'Theme i18n schema should not have been loaded without theme support.' ); + } } From ac84f67ee4be248b73c31b92006adecfe14dc2e3 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 11 Nov 2022 00:04:58 +0000 Subject: [PATCH 0031/1431] Menus: Apply `menu-item-has-children` class in sub-menus. Ensure the `menu-item-has-children` class is added to sub-menu items when `wp_nav_menu()` is called with the `depth` parameter specified to a non-zero value. Follow up to [54478]. Props davidvongries, fpodhorsky, hellofromTonya, innovext, larsmqller, LeonidasMilossis, mattkeys, mukesh27, nuvoPoint, ocean90, outrankjames, petitphp, SergeyBiryukov, sippis, webmandesign. Fixes #56946. See #28620. git-svn-id: https://develop.svn.wordpress.org/trunk@54801 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/nav-menu-template.php | 14 +++---- tests/phpunit/tests/menu/wp-nav-menu.php | 47 ++++++++++++++++++++---- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/nav-menu-template.php b/src/wp-includes/nav-menu-template.php index df478a5fa4f61..0673bf36ed44f 100644 --- a/src/wp-includes/nav-menu-template.php +++ b/src/wp-includes/nav-menu-template.php @@ -204,14 +204,14 @@ function wp_nav_menu( $args = array() ) { if ( $menu_item->menu_item_parent ) { $menu_items_with_children[ $menu_item->menu_item_parent ] = 1; } + } - // Calculate the depth of each menu item with children - foreach ( $menu_items_with_children as $menu_item_key => &$menu_item_depth ) { - $menu_item_parent = $menu_items_tree[ $menu_item_key ]; - while ( $menu_item_parent ) { - $menu_item_depth = $menu_item_depth + 1; - $menu_item_parent = $menu_items_tree[ $menu_item_parent ]; - } + // Calculate the depth of each menu item with children. + foreach ( $menu_items_with_children as $menu_item_key => &$menu_item_depth ) { + $menu_item_parent = $menu_items_tree[ $menu_item_key ]; + while ( $menu_item_parent ) { + $menu_item_depth = $menu_item_depth + 1; + $menu_item_parent = $menu_items_tree[ $menu_item_parent ]; } } diff --git a/tests/phpunit/tests/menu/wp-nav-menu.php b/tests/phpunit/tests/menu/wp-nav-menu.php index 60a523bfd1710..a077766567bc3 100644 --- a/tests/phpunit/tests/menu/wp-nav-menu.php +++ b/tests/phpunit/tests/menu/wp-nav-menu.php @@ -11,6 +11,7 @@ class Tests_Menu_wpNavMenu extends WP_UnitTestCase { static $lvl0_menu_item = 0; static $lvl1_menu_item = 0; static $lvl2_menu_item = 0; + static $lvl3_menu_item = 0; public static function set_up_before_class() { parent::set_up_before_class(); @@ -53,6 +54,18 @@ public static function set_up_before_class() { ) ); + // Create lvl3 menu item. + self::$lvl3_menu_item = wp_update_nav_menu_item( + self::$menu_id, + 0, + array( + 'menu-item-title' => 'Lvl3 menu item', + 'menu-item-url' => '#', + 'menu-item-parent-id' => self::$lvl2_menu_item, + 'menu-item-status' => 'publish', + ) + ); + /* * This filter is used to prevent reusing a menu item ID more that once. * It caused the tests to fail after the first one since the IDs are missing @@ -81,6 +94,7 @@ public static function tear_down_after_class() { * when displaying the menu without specifying a custom depth. * * @ticket 28620 + * @ticket 56946 */ public function test_wp_nav_menu_should_have_has_children_class_without_custom_depth() { @@ -112,11 +126,20 @@ public function test_wp_nav_menu_should_have_has_children_class_without_custom_d $this->assertStringContainsString( sprintf( - '

-
+