From 1637791aefdfa2a5169c5b81175655a7c540fcfa Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 16 Sep 2024 17:54:08 +0000 Subject: [PATCH 001/453] HTML API: Prevent infinite loop in foreign content reprocessing step. An infinite loop was discovered in specific situations within foreign content inside the HTML Processor when a given node inside foreign content must be handled in the rules for the current insertion mode. This patch resolves the loop by handling those nodes directly instead of reprocessing the node, which previously was redirecting control flow back to where the loop started. Developed in https://github.com/wordpress/wordpress-develop/7347 Discussed in https://core.trac.wordpress.org/ticket/61656 Follow-up to [58868]. Props jonsurrell. See #61576. git-svn-id: https://develop.svn.wordpress.org/trunk@59024 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/html-api/class-wp-html-processor.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php index 28e5c150a8635..cf4f8d7b86b49 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -4501,7 +4501,7 @@ private function step_in_foreign_content(): bool { $this->state->stack_of_open_elements->pop(); } - return $this->step( self::REPROCESS_CURRENT_NODE ); + goto in_foreign_content_process_in_current_insertion_mode; } /* @@ -4577,6 +4577,7 @@ private function step_in_foreign_content(): bool { goto in_foreign_content_end_tag_loop; } + in_foreign_content_process_in_current_insertion_mode: switch ( $this->state->insertion_mode ) { case WP_HTML_Processor_State::INSERTION_MODE_INITIAL: return $this->step_initial(); From ca64c851f7d73a48fa19a5d54467d45fc66a1098 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 16 Sep 2024 20:15:17 +0000 Subject: [PATCH 002/453] HTML API: Update html5lib test runner to support new features. This patch updates the html5lib test runner following the merge of changes opening up a full HTML parser and additional fragment contents. It makes no Core code changes, but allows a more tests to complete which previously failed due to incomplete test runner support.. Developed in https://github.com/wordpress/wordpress-develop/pull/7346 Discussed in https://core.trac.wordpress.org/ticket/61646 Props jonsurrell. See #61646. git-svn-id: https://develop.svn.wordpress.org/trunk@59025 602fd350-edb4-49c9-b593-d223f7449a82 --- .../html-api/wpHtmlProcessorHtml5lib.php | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorHtml5lib.php b/tests/phpunit/tests/html-api/wpHtmlProcessorHtml5lib.php index 54d60f8c78a66..4862ba981e6f0 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorHtml5lib.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorHtml5lib.php @@ -21,6 +21,8 @@ * @group html-api-html5lib-tests */ class Tests_HtmlApi_Html5lib extends WP_UnitTestCase { + const TREE_INDENT = ' '; + /** * Skip specific tests that may not be supported or have known issues. */ @@ -49,9 +51,9 @@ class Tests_HtmlApi_Html5lib extends WP_UnitTestCase { * * @dataProvider data_external_html5lib_tests * - * @param string $fragment_context Context element in which to parse HTML, such as BODY or SVG. - * @param string $html Given test HTML. - * @param string $expected_tree Tree structure of parsed HTML. + * @param string|null $fragment_context Context element in which to parse HTML, such as BODY or SVG. + * @param string $html Given test HTML. + * @param string $expected_tree Tree structure of parsed HTML. */ public function test_parse( ?string $fragment_context, string $html, string $expected_tree ) { try { @@ -170,9 +172,8 @@ private static function build_tree_representation( ?string $fragment_context, st * and requires adjustment to initial parameters. * The full parser will not. */ - $output = $fragment_context ? "\n \n \n" : ''; - $indent_level = $fragment_context ? 2 : 0; - $indent = ' '; + $output = ''; + $indent_level = 0; $was_text = null; $text_node = ''; @@ -225,7 +226,7 @@ private static function build_tree_representation( ?string $fragment_context, st ++$indent_level; } - $output .= str_repeat( $indent, $tag_indent ) . "<{$tag_name}>\n"; + $output .= str_repeat( self::TREE_INDENT, $tag_indent ) . "<{$tag_name}>\n"; $attribute_names = $processor->get_attribute_names_with_prefix( '' ); if ( $attribute_names ) { @@ -278,18 +279,18 @@ static function ( $a, $b ) { if ( true === $val ) { $val = ''; } - $output .= str_repeat( $indent, $tag_indent + 1 ) . "{$display_name}=\"{$val}\"\n"; + $output .= str_repeat( self::TREE_INDENT, $tag_indent + 1 ) . "{$display_name}=\"{$val}\"\n"; } } // Self-contained tags contain their inner contents as modifiable text. $modifiable_text = $processor->get_modifiable_text(); if ( '' !== $modifiable_text ) { - $output .= str_repeat( $indent, $tag_indent + 1 ) . "\"{$modifiable_text}\"\n"; + $output .= str_repeat( self::TREE_INDENT, $tag_indent + 1 ) . "\"{$modifiable_text}\"\n"; } if ( 'html' === $namespace && 'TEMPLATE' === $token_name ) { - $output .= str_repeat( $indent, $indent_level ) . "content\n"; + $output .= str_repeat( self::TREE_INDENT, $indent_level ) . "content\n"; ++$indent_level; } @@ -303,14 +304,14 @@ static function ( $a, $b ) { } $was_text = true; if ( '' === $text_node ) { - $text_node .= str_repeat( $indent, $indent_level ) . '"'; + $text_node .= str_repeat( self::TREE_INDENT, $indent_level ) . '"'; } $text_node .= $text_content; break; case '#funky-comment': // Comments must be "<" then "!-- " then the data then " -->". - $output .= str_repeat( $indent, $indent_level ) . "\n"; + $output .= str_repeat( self::TREE_INDENT, $indent_level ) . "\n"; break; case '#comment': @@ -333,7 +334,7 @@ static function ( $a, $b ) { throw new Error( "Unhandled comment type for tree construction: {$processor->get_comment_type()}" ); } // Comments must be "<" then "!-- " then the data then " -->". - $output .= str_repeat( $indent, $indent_level ) . "\n"; + $output .= str_repeat( self::TREE_INDENT, $indent_level ) . "\n"; break; default: @@ -449,7 +450,7 @@ public static function parse_html5_dat_testfile( $filename ) { * context element as context. */ case 'document-fragment': - $test_context_element = explode( ' ', $line )[0]; + $test_context_element = trim( $line ); break; /* From 8b8fb62a066e34862a8bb30498d1e1b5e18290f0 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Mon, 16 Sep 2024 22:16:46 +0000 Subject: [PATCH 003/453] Bundled Themes: Add missing `initial-scale` value in viewport meta tag. The viewport meta should include `initial-scale=1.0` to ensure that high DPI/mobile display works as expected. Includes standardizing on `1.0` vs. `1` for consistency. References: * [https://css-tricks.com/probably-use-initial-scale1/ CSS-Tricks: Probably Use initial-scale=1] * [https://www.sitepoint.com/community/t/is-it-necessary-to-include-initial-scale-1-0-in-the-meta-viewport-tag/455119 SitePoint Forums: Is it necessary to include initial-scale=1.0 in the meta viewport tag?] Props dhruvang21, sabernhardt, kkmuffme, mukesh27, swissspidy, SergeyBiryukov. See #61988. git-svn-id: https://develop.svn.wordpress.org/trunk@59026 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-content/themes/twentyeleven/header.php | 2 +- src/wp-content/themes/twentyfifteen/header.php | 2 +- src/wp-content/themes/twentyfourteen/header.php | 2 +- src/wp-content/themes/twentynineteen/header.php | 2 +- src/wp-content/themes/twentyseventeen/header.php | 2 +- src/wp-content/themes/twentysixteen/header.php | 2 +- src/wp-content/themes/twentythirteen/header.php | 2 +- src/wp-content/themes/twentytwelve/header.php | 2 +- src/wp-content/themes/twentytwenty/header.php | 2 +- src/wp-content/themes/twentytwentyone/header.php | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wp-content/themes/twentyeleven/header.php b/src/wp-content/themes/twentyeleven/header.php index 98af9ff362922..006ffb764519c 100644 --- a/src/wp-content/themes/twentyeleven/header.php +++ b/src/wp-content/themes/twentyeleven/header.php @@ -23,7 +23,7 @@ - + <?php // Print the <title> tag based on what is being viewed. diff --git a/src/wp-content/themes/twentyfifteen/header.php b/src/wp-content/themes/twentyfifteen/header.php index a92b12b80ffe0..06bd599e09ba8 100644 --- a/src/wp-content/themes/twentyfifteen/header.php +++ b/src/wp-content/themes/twentyfifteen/header.php @@ -12,7 +12,7 @@ <html <?php language_attributes(); ?> class="no-js"> <head> <meta charset="<?php bloginfo( 'charset' ); ?>"> - <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="profile" href="https://gmpg.org/xfn/11"> <link rel="pingback" href="<?php echo esc_url( get_bloginfo( 'pingback_url' ) ); ?>"> <?php wp_head(); ?> diff --git a/src/wp-content/themes/twentyfourteen/header.php b/src/wp-content/themes/twentyfourteen/header.php index 10b31fbf36902..be9bc76c3b9a9 100644 --- a/src/wp-content/themes/twentyfourteen/header.php +++ b/src/wp-content/themes/twentyfourteen/header.php @@ -20,7 +20,7 @@ <!--<![endif]--> <head> <meta charset="<?php bloginfo( 'charset' ); ?>"> - <meta name="viewport" content="width=device-width"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php wp_title( '|', true, 'right' ); ?> diff --git a/src/wp-content/themes/twentynineteen/header.php b/src/wp-content/themes/twentynineteen/header.php index 11b6c0998e468..119417460cbf0 100644 --- a/src/wp-content/themes/twentynineteen/header.php +++ b/src/wp-content/themes/twentynineteen/header.php @@ -14,7 +14,7 @@ > - + diff --git a/src/wp-content/themes/twentyseventeen/header.php b/src/wp-content/themes/twentyseventeen/header.php index 9ff5c9d4151ce..92b4c9971d558 100644 --- a/src/wp-content/themes/twentyseventeen/header.php +++ b/src/wp-content/themes/twentyseventeen/header.php @@ -16,7 +16,7 @@ class="no-js no-svg"> - + diff --git a/src/wp-content/themes/twentysixteen/header.php b/src/wp-content/themes/twentysixteen/header.php index 73b05335c262d..22c683146c015 100644 --- a/src/wp-content/themes/twentysixteen/header.php +++ b/src/wp-content/themes/twentysixteen/header.php @@ -13,7 +13,7 @@ class="no-js"> - + diff --git a/src/wp-content/themes/twentythirteen/header.php b/src/wp-content/themes/twentythirteen/header.php index db2e19c89cbdb..e008648a48eb1 100644 --- a/src/wp-content/themes/twentythirteen/header.php +++ b/src/wp-content/themes/twentythirteen/header.php @@ -12,7 +12,7 @@ > - + <?php wp_title( '|', true, 'right' ); ?> diff --git a/src/wp-content/themes/twentytwelve/header.php b/src/wp-content/themes/twentytwelve/header.php index 13fbebead96ab..be0300801c114 100644 --- a/src/wp-content/themes/twentytwelve/header.php +++ b/src/wp-content/themes/twentytwelve/header.php @@ -20,7 +20,7 @@ - + <?php wp_title( '|', true, 'right' ); ?> diff --git a/src/wp-content/themes/twentytwenty/header.php b/src/wp-content/themes/twentytwenty/header.php index d85a9700e225c..26f63a70ea560 100644 --- a/src/wp-content/themes/twentytwenty/header.php +++ b/src/wp-content/themes/twentytwenty/header.php @@ -16,7 +16,7 @@ - + diff --git a/src/wp-content/themes/twentytwentyone/header.php b/src/wp-content/themes/twentytwentyone/header.php index a601a2aa6bca8..9713accf4e82e 100644 --- a/src/wp-content/themes/twentytwentyone/header.php +++ b/src/wp-content/themes/twentytwentyone/header.php @@ -16,7 +16,7 @@ > - + From 7dad836c7ff293879da0cdcce98472b5d85d3708 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Tue, 17 Sep 2024 00:01:09 +0000 Subject: [PATCH 004/453] General: Add missing `initial-scale` value in viewport meta tags. The viewport meta should include `initial-scale=1.0` to ensure that high DPI/mobile display works as expected. References: * [https://css-tricks.com/probably-use-initial-scale1/ CSS-Tricks: Probably Use initial-scale=1] * [https://www.sitepoint.com/community/t/is-it-necessary-to-include-initial-scale-1-0-in-the-meta-viewport-tag/455119 SitePoint Forums: Is it necessary to include initial-scale=1.0 in the meta viewport tag?] Follow-up to [59026]. Props dhruvang21, sabernhardt, kkmuffme, mukesh27, narenin, swissspidy, SergeyBiryukov. Fixes #61988. git-svn-id: https://develop.svn.wordpress.org/trunk@59027 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/install.php | 2 +- src/wp-admin/maint/repair.php | 2 +- src/wp-admin/setup-config.php | 2 +- src/wp-admin/upgrade.php | 2 +- src/wp-includes/functions.php | 2 +- src/wp-login.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wp-admin/install.php b/src/wp-admin/install.php index 34d5d4c7f7ab7..e81331acfc82e 100644 --- a/src/wp-admin/install.php +++ b/src/wp-admin/install.php @@ -67,7 +67,7 @@ function display_header( $body_classes = '' ) { > - + <?php _e( 'WordPress › Installation' ); ?> diff --git a/src/wp-admin/maint/repair.php b/src/wp-admin/maint/repair.php index 1c0f6ffa985eb..2104ccd3abb43 100644 --- a/src/wp-admin/maint/repair.php +++ b/src/wp-admin/maint/repair.php @@ -14,7 +14,7 @@ > - + <?php _e( 'WordPress › Database Repair' ); ?> diff --git a/src/wp-admin/setup-config.php b/src/wp-admin/setup-config.php index b394de867928f..2d1ff715d3d23 100644 --- a/src/wp-admin/setup-config.php +++ b/src/wp-admin/setup-config.php @@ -105,7 +105,7 @@ function setup_config_display_header( $body_classes = array() ) { > - + <?php _e( 'WordPress › Setup Configuration File' ); ?> diff --git a/src/wp-admin/upgrade.php b/src/wp-admin/upgrade.php index 414fbb28d1946..9edadd9be2f1c 100644 --- a/src/wp-admin/upgrade.php +++ b/src/wp-admin/upgrade.php @@ -59,7 +59,7 @@ > - + <?php _e( 'WordPress › Update' ); ?> diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 0619f44f30d4e..67544bc889e2c 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -3868,7 +3868,7 @@ function _default_wp_die_handler( $message, $title = '', $args = array() ) { > - + - + Date: Tue, 17 Sep 2024 16:58:10 +0000 Subject: [PATCH 005/453] Taxonomy: Remove redundant `$taxonomies` value from cache keys used for `WP_Term_Query`. Props niravsherasiya7707, spacedmonkey. Fixes #59594. See #35381. git-svn-id: https://develop.svn.wordpress.org/trunk@59028 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-term-query.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-term-query.php b/src/wp-includes/class-wp-term-query.php index 25baaa8dd11f3..77a3b9d23f804 100644 --- a/src/wp-includes/class-wp-term-query.php +++ b/src/wp-includes/class-wp-term-query.php @@ -1170,12 +1170,11 @@ protected function generate_cache_key( array $args, $sql ) { if ( 'count' !== $args['fields'] && 'all_with_object_id' !== $args['fields'] ) { $cache_args['fields'] = 'all'; } - $taxonomies = (array) $args['taxonomy']; // Replace wpdb placeholder in the SQL statement used by the cache key. $sql = $wpdb->remove_placeholder_escape( $sql ); - $key = md5( serialize( $cache_args ) . serialize( $taxonomies ) . $sql ); + $key = md5( serialize( $cache_args ) . $sql ); $last_changed = wp_cache_get_last_changed( 'terms' ); return "get_terms:$key:$last_changed"; } From 70c7962fee55cd10ffb2ab35c19c7d78359bef07 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 17 Sep 2024 20:56:03 +0000 Subject: [PATCH 006/453] I18N: Add a new way to determine whether a translation is available. A new `has_translation()` function can be used to determine whether a translation exists for a given string. Props louiswol94, swissspidy, drzraf, ckanitz, tomhine, mchirag2002, samuelsilvapt. Fixes #52696. git-svn-id: https://develop.svn.wordpress.org/trunk@59029 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/l10n.php | 14 ++++++ .../l10n/class-wp-translation-controller.php | 18 +++++++ .../tests/l10n/wpTranslationController.php | 47 +++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 038b9160499ce..1f3532a40ded5 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -1988,3 +1988,17 @@ function wp_get_word_count_type() { return $wp_locale->get_word_count_type(); } + +/** + * Returns a boolean to indicate whether a translation exists for a given string with optional text domain and locale. + * + * @since 6.7.0 + * + * @param string $singular Singular translation to check. + * @param string $textdomain Optional. Text domain. Default 'default'. + * @param ?string $locale Optional. Locale. Default current locale. + * @return bool True if the translation exists, false otherwise. + */ +function has_translation( string $singular, string $textdomain = 'default', ?string $locale = null ): bool { + return WP_Translation_Controller::get_instance()->has_translation( $singular, $textdomain, $locale ); +} diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index 295e3fcac79a2..c68cf32add6d6 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -434,4 +434,22 @@ protected function get_files( string $textdomain = 'default', ?string $locale = return $this->loaded_translations[ $locale ][ $textdomain ] ?? array(); } + + /** + * Returns a boolean to indicate whether a translation exists for a given string with optional text domain and locale. + * + * @since 6.7.0 + * + * @param string $singular Singular translation to check. + * @param string $textdomain Optional. Text domain. Default 'default'. + * @param ?string $locale Optional. Locale. Default current locale. + * @return bool True if the translation exists, false otherwise. + */ + public function has_translation( string $singular, string $textdomain = 'default', ?string $locale = null ): bool { + if ( null === $locale ) { + $locale = $this->current_locale; + } + + return false !== $this->locate_translation( $singular, $textdomain, $locale ); + } } diff --git a/tests/phpunit/tests/l10n/wpTranslationController.php b/tests/phpunit/tests/l10n/wpTranslationController.php index 6c8fb6dc9d300..00afeaa095cea 100644 --- a/tests/phpunit/tests/l10n/wpTranslationController.php +++ b/tests/phpunit/tests/l10n/wpTranslationController.php @@ -361,4 +361,51 @@ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain $this->assertSame( 'Este es un plugin dummy', $actual ); $this->assertSame( 'This is a dummy plugin', $after ); } + + /** + * @ticket 52696 + * @covers ::has_translation + */ + public function test_has_translation_with_existing_translation() { + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + $this->assertTrue( WP_Translation_Controller::get_instance()->has_translation( 'baba', 'wp-tests-domain', 'en_US' ) ); + } + + /** + * @ticket 52696 + * @covers ::has_translation + */ + public function test_has_translation_with_no_translation() { + $this->assertFalse( WP_Translation_Controller::get_instance()->has_translation( 'Goodbye', 'wp-tests-domain', 'en_US' ) ); + } + + /** + * @ticket 52696 + * @covers ::has_translation + */ + public function test_has_translation_with_different_textdomain() { + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + $this->assertFalse( WP_Translation_Controller::get_instance()->has_translation( 'baba', 'custom-domain', 'en_US' ) ); + } + + /** + * @ticket 52696 + * @covers ::has_translation + */ + public function test_has_translation_with_different_locale() { + switch_to_locale( 'es_ES' ); + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + $actual = WP_Translation_Controller::get_instance()->has_translation( 'baba', 'wp-tests-domain', 'es_ES' ); + restore_previous_locale(); + $this->assertTrue( $actual ); + } + + /** + * @ticket 52696 + * @covers ::has_translation + */ + public function test_has_translation_with_no_locale_provided() { + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + $this->assertTrue( WP_Translation_Controller::get_instance()->has_translation( 'baba', 'wp-tests-domain' ) ); + } } From cdd137e9977ee7a2c276e462b61d55c8cb60a58e Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers Date: Tue, 17 Sep 2024 21:06:30 +0000 Subject: [PATCH 007/453] External Libraries: Update PHPass library. This updates the PHPass library to version `0.5.4` while maintaining the adjustments introduced in [30466]. Props jrf. Fixes #62058. git-svn-id: https://develop.svn.wordpress.org/trunk@59030 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-phpass.php | 50 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/wp-includes/class-phpass.php b/src/wp-includes/class-phpass.php index 055925b20f7f6..f8f659648e893 100644 --- a/src/wp-includes/class-phpass.php +++ b/src/wp-includes/class-phpass.php @@ -10,7 +10,7 @@ # # Portable PHP password hashing framework. # -# Version 0.5 / WordPress. +# Version 0.5.4 / WordPress. # # Written by Solar Designer in 2004-2006 and placed in # the public domain. Revised in subsequent years, still public domain. @@ -51,15 +51,17 @@ function __construct($iteration_count_log2, $portable_hashes) { $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) { $iteration_count_log2 = 8; + } $this->iteration_count_log2 = $iteration_count_log2; $this->portable_hashes = $portable_hashes; $this->random_state = microtime(); - if (function_exists('getmypid')) + if (function_exists('getmypid')) { $this->random_state .= getmypid(); + } } function PasswordHash($iteration_count_log2, $portable_hashes) @@ -96,16 +98,20 @@ function encode64($input, $count) do { $value = ord($input[$i++]); $output .= $this->itoa64[$value & 0x3f]; - if ($i < $count) + if ($i < $count) { $value |= ord($input[$i]) << 8; + } $output .= $this->itoa64[($value >> 6) & 0x3f]; - if ($i++ >= $count) + if ($i++ >= $count) { break; - if ($i < $count) + } + if ($i < $count) { $value |= ord($input[$i]) << 16; + } $output .= $this->itoa64[($value >> 12) & 0x3f]; - if ($i++ >= $count) + if ($i++ >= $count) { break; + } $output .= $this->itoa64[($value >> 18) & 0x3f]; } while ($i < $count); @@ -115,8 +121,8 @@ function encode64($input, $count) function gensalt_private($input) { $output = '$P$'; - $output .= $this->itoa64[min($this->iteration_count_log2 + - ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->itoa64[min($this->iteration_count_log2 + 5, + 30)]; $output .= $this->encode64($input, 6); return $output; @@ -125,23 +131,27 @@ function gensalt_private($input) function crypt_private($password, $setting) { $output = '*0'; - if (substr($setting, 0, 2) === $output) + if (substr($setting, 0, 2) === $output) { $output = '*1'; + } $id = substr($setting, 0, 3); # We use "$P$", phpBB3 uses "$H$" for the same thing - if ($id !== '$P$' && $id !== '$H$') + if ($id !== '$P$' && $id !== '$H$') { return $output; + } $count_log2 = strpos($this->itoa64, $setting[3]); - if ($count_log2 < 7 || $count_log2 > 30) + if ($count_log2 < 7 || $count_log2 > 30) { return $output; + } $count = 1 << $count_log2; $salt = substr($setting, 4, 8); - if (strlen($salt) !== 8) + if (strlen($salt) !== 8) { return $output; + } # We were kind of forced to use MD5 here since it's the only # cryptographic primitive that was available in all versions @@ -174,7 +184,7 @@ function gensalt_blowfish($input) $output = '$2a$'; $output .= chr((int)(ord('0') + $this->iteration_count_log2 / 10)); - $output .= chr((ord('0') + $this->iteration_count_log2 % 10)); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); $output .= '$'; $i = 0; @@ -213,17 +223,20 @@ function HashPassword($password) $random = $this->get_random_bytes(16); $hash = crypt($password, $this->gensalt_blowfish($random)); - if (strlen($hash) === 60) + if (strlen($hash) === 60) { return $hash; + } } - if (strlen($random) < 6) + if (strlen($random) < 6) { $random = $this->get_random_bytes(6); + } $hash = $this->crypt_private($password, $this->gensalt_private($random)); - if (strlen($hash) === 34) + if (strlen($hash) === 34) { return $hash; + } # Returning '*' on error is safe here, but would _not_ be safe # in a crypt(3)-like function used _both_ for generating new @@ -238,8 +251,9 @@ function CheckPassword($password, $stored_hash) } $hash = $this->crypt_private($password, $stored_hash); - if ($hash[0] === '*') + if ($hash[0] === '*') { $hash = crypt($password, $stored_hash); + } # This is not constant-time. In order to keep the code simple, # for timing safety we currently rely on the salts being From f79ad14e03162fe7c82bc63bc8137fa974ed87cc Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Tue, 17 Sep 2024 21:31:14 +0000 Subject: [PATCH 008/453] Plugins: Correct the item schema for the plugins REST API endpoint. The `author` property contains the string name of the plugin author. Props narenin. Fixes #61920 git-svn-id: https://develop.svn.wordpress.org/trunk@59031 602fd350-edb4-49c9-b593-d223f7449a82 --- .../rest-api/endpoints/class-wp-rest-plugins-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-plugins-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-plugins-controller.php index 8d24b60db26cb..d0dd5fce4cd9c 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-plugins-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-plugins-controller.php @@ -913,7 +913,7 @@ public function get_item_schema() { ), 'author' => array( 'description' => __( 'The plugin author.' ), - 'type' => 'object', + 'type' => 'string', 'readonly' => true, 'context' => array( 'view', 'edit' ), ), From 7b8e4451f4d28102585bdf9573ce0fb193a917f9 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Tue, 17 Sep 2024 21:50:38 +0000 Subject: [PATCH 009/453] REST API: Automatically populate targetHints for the Allow header. The REST API uses the "Allow" header to communicate what methods a user is authorized to perform on a resource. This works great when operating on a single item route, but can break down when needing to determine authorization over a collection of items. This commit uses the "targetHints" property of JSON Hyper Schema to provide access to the "allow" header for "self" links. This alleviates needing to make a separate network request for each item in a collection. Props mamaduka, noisysocks, peterwilsoncc, spacedmonkey, swissspidy, timothyblynjacobs, tyxla, youknowriad. Fixes #61739. git-svn-id: https://develop.svn.wordpress.org/trunk@59032 602fd350-edb4-49c9-b593-d223f7449a82 --- .../rest-api/class-wp-rest-server.php | 64 +++++++- tests/phpunit/tests/rest-api/rest-server.php | 155 +++++++++++++++++- tests/qunit/fixtures/wp-api-generated.js | 87 +++++++++- 3 files changed, 295 insertions(+), 11 deletions(-) diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php index 0eedb0396bcc4..17d620f6fb6e0 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -636,13 +636,75 @@ public static function get_response_links( $response ) { foreach ( $items as $item ) { $attributes = $item['attributes']; $attributes['href'] = $item['href']; - $data[ $rel ][] = $attributes; + + if ( 'self' !== $rel ) { + $data[ $rel ][] = $attributes; + continue; + } + + $target_hints = self::get_target_hints_for_link( $attributes ); + if ( $target_hints ) { + $attributes['targetHints'] = $target_hints; + } + + $data[ $rel ][] = $attributes; } } return $data; } + /** + * Gets the target links for a REST API Link. + * + * @since 6.7.0 + * + * @param array $link + * + * @return array|null + */ + protected static function get_target_hints_for_link( $link ) { + // Prefer targetHints that were specifically designated by the developer. + if ( isset( $link['targetHints']['allow'] ) ) { + return null; + } + + $request = WP_REST_Request::from_url( $link['href'] ); + if ( ! $request ) { + return null; + } + + $server = rest_get_server(); + $match = $server->match_request_to_handler( $request ); + + if ( is_wp_error( $match ) ) { + return null; + } + + if ( is_wp_error( $request->has_valid_params() ) ) { + return null; + } + + if ( is_wp_error( $request->sanitize_params() ) ) { + return null; + } + + $target_hints = array(); + + $response = new WP_REST_Response(); + $response->set_matched_route( $match[0] ); + $response->set_matched_handler( $match[1] ); + $headers = rest_send_allow_header( $response, $server, $request )->get_headers(); + + foreach ( $headers as $name => $value ) { + $name = WP_REST_Request::canonicalize_header_name( $name ); + + $target_hints[ $name ] = array_map( 'trim', explode( ',', $value ) ); + } + + return $target_hints; + } + /** * Retrieves the CURIEs (compact URIs) used for relations. * diff --git a/tests/phpunit/tests/rest-api/rest-server.php b/tests/phpunit/tests/rest-api/rest-server.php index 7bcc6d68e7497..378b51f606cc9 100644 --- a/tests/phpunit/tests/rest-api/rest-server.php +++ b/tests/phpunit/tests/rest-api/rest-server.php @@ -9,6 +9,8 @@ */ class Tests_REST_Server extends WP_Test_REST_TestCase { protected static $icon_id; + protected static $admin_id; + protected static $post_id; /** * Called before setting up all tests. @@ -21,12 +23,20 @@ public static function set_up_before_class() { } public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { - $filename = DIR_TESTDATA . '/images/test-image-large.jpg'; - self::$icon_id = $factory->attachment->create_upload_object( $filename ); + $filename = DIR_TESTDATA . '/images/test-image-large.jpg'; + self::$icon_id = $factory->attachment->create_upload_object( $filename ); + self::$admin_id = $factory->user->create( + array( + 'role' => 'administrator', + ) + ); + self::$post_id = $factory->post->create(); } public static function tear_down_after_class() { wp_delete_attachment( self::$icon_id, true ); + self::delete_user( self::$admin_id ); + wp_delete_post( self::$post_id ); parent::tear_down_after_class(); } @@ -2431,6 +2441,147 @@ public function test_rest_allowed_cors_headers_filter_receives_request_object() $this->assertSame( '/test-allowed-cors-headers', $mock_hook->get_events()[0]['args'][1]->get_route() ); } + /** + * @ticket 61739 + */ + public function test_validates_request_when_building_target_hints() { + register_rest_route( + 'test-ns/v1', + '/test/(?P\d+)', + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => static function () { + return new \WP_REST_Response(); + }, + 'permission_callback' => '__return_true', + 'args' => array( + 'id' => array( + 'type' => 'integer', + ), + ), + ), + ) + ); + + $response = new WP_REST_Response(); + $response->add_link( 'self', rest_url( 'test-ns/v1/test/garbage' ) ); + + $links = rest_get_server()::get_response_links( $response ); + + $this->assertArrayHasKey( 'self', $links ); + $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] ); + } + + /** + * @ticket 61739 + */ + public function test_sanitizes_request_when_building_target_hints() { + $validated_param = null; + register_rest_route( + 'test-ns/v1', + '/test/(?P\d+)', + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => static function () { + return new \WP_REST_Response(); + }, + 'permission_callback' => function ( WP_REST_Request $request ) use ( &$validated_param ) { + $validated_param = $request['id']; + + return true; + }, + 'args' => array( + 'id' => array( + 'type' => 'integer', + ), + ), + ), + ) + ); + + $response = new WP_REST_Response(); + $response->add_link( 'self', rest_url( 'test-ns/v1/test/5' ) ); + + $links = rest_get_server()::get_response_links( $response ); + + $this->assertArrayHasKey( 'self', $links ); + $this->assertArrayHasKey( 'targetHints', $links['self'][0] ); + $this->assertIsInt( $validated_param ); + } + + /** + * @ticket 61739 + */ + public function test_populates_target_hints_for_administrator() { + wp_set_current_user( self::$admin_id ); + $response = rest_do_request( '/wp/v2/posts' ); + $post = $response->get_data()[0]; + + $link = $post['_links']['self'][0]; + $this->assertArrayHasKey( 'targetHints', $link ); + $this->assertArrayHasKey( 'allow', $link['targetHints'] ); + $this->assertSame( array( 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' ), $link['targetHints']['allow'] ); + } + + /** + * @ticket 61739 + */ + public function test_populates_target_hints_for_logged_out_user() { + $response = rest_do_request( '/wp/v2/posts' ); + $post = $response->get_data()[0]; + + $link = $post['_links']['self'][0]; + $this->assertArrayHasKey( 'targetHints', $link ); + $this->assertArrayHasKey( 'allow', $link['targetHints'] ); + $this->assertSame( array( 'GET' ), $link['targetHints']['allow'] ); + } + + /** + * @ticket 61739 + */ + public function test_does_not_error_on_invalid_urls() { + $response = new WP_REST_Response(); + $response->add_link( 'self', 'this is not a real URL' ); + + $links = rest_get_server()::get_response_links( $response ); + $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] ); + } + + /** + * @ticket 61739 + */ + public function test_does_not_error_on_bad_rest_api_routes() { + $response = new WP_REST_Response(); + $response->add_link( 'self', rest_url( '/this/is/not/a/real/route' ) ); + + $links = rest_get_server()::get_response_links( $response ); + $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] ); + } + + /** + * @ticket 61739 + */ + public function test_prefers_developer_defined_target_hints() { + $response = new WP_REST_Response(); + $response->add_link( + 'self', + '/wp/v2/posts/' . self::$post_id, + array( + 'targetHints' => array( + 'allow' => array( 'GET', 'PUT' ), + ), + ) + ); + + $links = rest_get_server()::get_response_links( $response ); + $link = $links['self'][0]; + $this->assertArrayHasKey( 'targetHints', $link ); + $this->assertArrayHasKey( 'allow', $link['targetHints'] ); + $this->assertSame( array( 'GET', 'PUT' ), $link['targetHints']['allow'] ); + } + public function _validate_as_integer_123( $value, $request, $key ) { if ( ! is_int( $value ) ) { return new WP_Error( 'some-error', 'This is not valid!' ); diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 7e97f19b18588..b3917a35eee93 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -12334,7 +12334,16 @@ mockedApiResponse.PostsCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/posts/4" + "href": "http://example.org/index.php?rest_route=/wp/v2/posts/4", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ @@ -12641,7 +12650,16 @@ mockedApiResponse.PagesCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/pages/7" + "href": "http://example.org/index.php?rest_route=/wp/v2/pages/7", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ @@ -12932,7 +12950,16 @@ mockedApiResponse.MediaCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/media/10" + "href": "http://example.org/index.php?rest_route=/wp/v2/media/10", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ @@ -13629,7 +13656,15 @@ mockedApiResponse.CategoriesCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/categories/1" + "href": "http://example.org/index.php?rest_route=/wp/v2/categories/1", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH" + ] + } } ], "collection": [ @@ -13694,7 +13729,16 @@ mockedApiResponse.TagsCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/tags/2" + "href": "http://example.org/index.php?rest_route=/wp/v2/tags/2", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ @@ -13758,7 +13802,16 @@ mockedApiResponse.UsersCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/users/1" + "href": "http://example.org/index.php?rest_route=/wp/v2/users/1", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ @@ -13786,7 +13839,16 @@ mockedApiResponse.UsersCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/users/2" + "href": "http://example.org/index.php?rest_route=/wp/v2/users/2", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ @@ -13859,7 +13921,16 @@ mockedApiResponse.CommentsCollection = [ "_links": { "self": [ { - "href": "http://example.org/index.php?rest_route=/wp/v2/comments/2" + "href": "http://example.org/index.php?rest_route=/wp/v2/comments/2", + "targetHints": { + "allow": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ] + } } ], "collection": [ From af0437b080a1c512816883728bde9a75f70081bf Mon Sep 17 00:00:00 2001 From: David Baumwald Date: Tue, 17 Sep 2024 21:52:54 +0000 Subject: [PATCH 010/453] Script Loader: Remove unused array_merge. This change removes an unused `array_merge` that was added in [44265]. Props kkmuffme, SergeyBiryukov, akshat2802. Fixes #61754. git-svn-id: https://develop.svn.wordpress.org/trunk@59033 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/script-loader.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index c9eb40d1c5b50..9adfb379316dd 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -609,10 +609,6 @@ function wp_tinymce_inline_scripts() { $tinymce_settings['wpeditimage_disable_captions'] = true; } - if ( ! empty( $editor_settings['tinymce'] ) && is_array( $editor_settings['tinymce'] ) ) { - array_merge( $tinymce_settings, $editor_settings['tinymce'] ); - } - /** This filter is documented in wp-includes/class-wp-editor.php */ $tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' ); From a9d76fab5641acdd3724a25989979cc51f22b953 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Tue, 17 Sep 2024 21:56:18 +0000 Subject: [PATCH 011/453] REST API: Support exact search in the REST API posts endpoint. This changeset adds support for a new `search_semantics` enum query parameter that can be passed alongside the `search` string parameter. At this point, it only supports "exact" as possible value, but an enum is used for forward compatibility with potential enhancements like "sentence" search support. If `search_semantics=exact` is passed, it will look for an exact match rather than do a full text search, which for some use-cases is more appropriate and more performant. Props mehulkaklotar, timothyblynjacobs, jimmyh61, ironprogrammer, johnregan3, mukesh27, costdev. Fixes #56350. git-svn-id: https://develop.svn.wordpress.org/trunk@59034 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-posts-controller.php | 13 ++++ .../rest-api/rest-attachments-controller.php | 1 + .../tests/rest-api/rest-pages-controller.php | 1 + .../tests/rest-api/rest-posts-controller.php | 59 +++++++++++++++++ tests/qunit/fixtures/wp-api-generated.js | 64 +++++++++++++++++++ 5 files changed, 138 insertions(+) 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 d6afdef470af2..11bc499fc6720 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 @@ -337,6 +337,13 @@ public function get_items( $request ) { } } + if ( + isset( $registered['search_semantics'], $request['search_semantics'] ) + && 'exact' === $request['search_semantics'] + ) { + $args['exact'] = true; + } + $args = $this->prepare_tax_query( $args, $request ); // Force the post_type argument, since it's not a user input variable. @@ -2886,6 +2893,12 @@ public function get_collection_params() { ); } + $query_params['search_semantics'] = array( + 'description' => __( 'How to interpret the search input.' ), + 'type' => 'string', + 'enum' => array( 'exact' ), + ); + $query_params['offset'] = array( 'description' => __( 'Offset the result set by a specific number of items.' ), 'type' => 'integer', diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 737c32b8e1ab1..55ea686d22f25 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -229,6 +229,7 @@ public function test_registered_query_params() { 'per_page', 'search', 'search_columns', + 'search_semantics', 'slug', 'status', ), diff --git a/tests/phpunit/tests/rest-api/rest-pages-controller.php b/tests/phpunit/tests/rest-api/rest-pages-controller.php index 209229256a11f..9717a7fcda1c6 100644 --- a/tests/phpunit/tests/rest-api/rest-pages-controller.php +++ b/tests/phpunit/tests/rest-api/rest-pages-controller.php @@ -85,6 +85,7 @@ public function test_registered_query_params() { 'per_page', 'search', 'search_columns', + 'search_semantics', 'slug', 'status', ), diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index 8b81b9f648651..3085d066fc6ae 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -206,6 +206,7 @@ public function test_registered_query_params() { 'per_page', 'search', 'search_columns', + 'search_semantics', 'slug', 'status', 'sticky', @@ -765,6 +766,64 @@ public function test_get_items_status_without_permissions() { } } + /** + * @ticket 56350 + * + * @dataProvider data_get_items_exact_search + * + * @param string $search_term The search term. + * @param bool $exact_search Whether the search is an exact or general search. + * @param int $expected The expected number of matching posts. + */ + public function test_get_items_exact_search( $search_term, $exact_search, $expected ) { + self::factory()->post->create( + array( + 'post_title' => 'Rye', + 'post_content' => 'This is a post about Rye Bread', + ) + ); + + self::factory()->post->create( + array( + 'post_title' => 'Types of Bread', + 'post_content' => 'Types of bread are White and Rye Bread', + ) + ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request['search'] = $search_term; + if ( $exact_search ) { + $request['search_semantics'] = 'exact'; + } + $response = rest_get_server()->dispatch( $request ); + $this->assertCount( $expected, $response->get_data() ); + } + + /** + * Data provider for test_get_items_exact_search(). + * + * @return array[] + */ + public function data_get_items_exact_search() { + return array( + 'general search, one exact match and one partial match' => array( + 'search_term' => 'Rye', + 'exact_search' => false, + 'expected' => 2, + ), + 'exact search, one exact match and one partial match' => array( + 'search_term' => 'Rye', + 'exact_search' => true, + 'expected' => 1, + ), + 'exact search, no match and one partial match' => array( + 'search_term' => 'Rye Bread', + 'exact_search' => true, + 'expected' => 0, + ), + ); + } + public function test_get_items_order_and_orderby() { self::factory()->post->create( array( diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index b3917a35eee93..d0d0a24a73e6e 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -362,6 +362,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -1719,6 +1727,14 @@ mockedApiResponse.Schema = { "type": "integer", "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -2820,6 +2836,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -3571,6 +3595,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -4382,6 +4414,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -6995,6 +7035,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -7812,6 +7860,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", @@ -8017,6 +8073,14 @@ mockedApiResponse.Schema = { "default": [], "required": false }, + "search_semantics": { + "description": "How to interpret the search input.", + "type": "string", + "enum": [ + "exact" + ], + "required": false + }, "offset": { "description": "Offset the result set by a specific number of items.", "type": "integer", From d3d02c44ebdddc11b5c53b6fc6938e801b3d41f8 Mon Sep 17 00:00:00 2001 From: Anthony Burchell Date: Tue, 17 Sep 2024 21:56:43 +0000 Subject: [PATCH 012/453] Media: Add Ctrl/Command + Enter shortcut to insert selected Media Library items. Adds a Ctrl/Command + Enter keyboard shortcut to insert the currently selected single media or multiple media items when selecting in the Media Library modal. Props poena, hirschferkel, antpb, joedolson, skobe, rcreators, plaidharper. Fixes #60369. git-svn-id: https://develop.svn.wordpress.org/trunk@59035 602fd350-edb4-49c9-b593-d223f7449a82 --- src/js/media/views/attachment.js | 5 +++++ src/js/media/views/modal.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/js/media/views/attachment.js b/src/js/media/views/attachment.js index 6f9f7f5001640..7359570b3fb2f 100644 --- a/src/js/media/views/attachment.js +++ b/src/js/media/views/attachment.js @@ -199,6 +199,11 @@ Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{ method = 'toggle'; } + // Avoid toggles when the command or control key is pressed with the enter key to prevent deselecting the last selected attachment. + if ( ( event.metaKey || event.ctrlKey ) && ( 13 === event.keyCode || 10 === event.keyCode ) ) { + return; + } + this.toggleSelection({ method: method }); diff --git a/src/js/media/views/modal.js b/src/js/media/views/modal.js index 8ef4fbb6e1eac..e3d9ad1701c86 100644 --- a/src/js/media/views/modal.js +++ b/src/js/media/views/modal.js @@ -180,6 +180,29 @@ Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{ this.escape(); }, + /** + * Handles the selection of attachments when the command or control key is pressed with the enter key. + * + * @since 6.7 + * + * @param {Object} event The keydown event object. + */ + selectHandler: function( event ) { + var selection = this.controller.state().get( 'selection' ); + + if ( ! selection.length > 0 ) { + return; + } + + if ( 'insert' === this.controller.options.state ) { + this.controller.trigger( 'insert', selection ); + } else { + this.controller.trigger( 'select', selection ); + event.preventDefault(); + this.escape(); + } + }, + /** * @param {Array|Object} content Views to register to '.media-modal-content' * @return {wp.media.view.Modal} Returns itself to allow chaining. @@ -214,6 +237,13 @@ Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{ this.escape(); event.stopImmediatePropagation(); } + + // Select the attachment when command or control and enter are pressed. + if ( ( 13 === event.which || 10 === event.which ) && ( event.metaKey || event.ctrlKey ) ) { + this.selectHandler( event ); + event.stopImmediatePropagation(); + } + } }); From 15b7d2a86885a6c83b520277f97b1debe31048fb Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 17 Sep 2024 22:17:41 +0000 Subject: [PATCH 013/453] REST API: Only check password value in query parameters while checking post permissions. The `password` property which gets sent as part of a request POST body while setting a post's password should not be checked when calculating post visibility permissions. That value in the request body is intended to update the post, not to authenticate, and may be malformed or an invalid non-string type which would cause a fatal when checking against the hashed post password value. Query parameter `?password=` values are the correct interface to check, and are also guaranteed to be strings. Props mlf20, devansh016, antonvlasenko, TimothyBlynJacobs, kadamwhite. Fixes #61837. git-svn-id: https://develop.svn.wordpress.org/trunk@59036 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-posts-controller.php | 4 +- .../tests/rest-api/rest-posts-controller.php | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) 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 11bc499fc6720..8aec375bc8bb8 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 @@ -504,9 +504,9 @@ public function get_item_permissions_check( $request ) { ); } - if ( $post && ! empty( $request['password'] ) ) { + if ( $post && ! empty( $request->get_query_params()['password'] ) ) { // Check post password, and return error if invalid. - if ( ! hash_equals( $post->post_password, $request['password'] ) ) { + if ( ! hash_equals( $post->post_password, $request->get_query_params()['password'] ) ) { return new WP_Error( 'rest_post_incorrect_password', __( 'Incorrect post password.' ), diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index 3085d066fc6ae..9b697fe2efd3d 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -2232,6 +2232,51 @@ public function test_get_post_with_password_without_permission() { $this->assertTrue( $data['excerpt']['protected'] ); } + /** + * @ticket 61837 + */ + public function test_get_item_permissions_check_while_updating_password() { + $endpoint = new WP_REST_Posts_Controller( 'post' ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) ); + $request->set_url_params( array( 'id' => self::$post_id ) ); + $request->set_body_params( + $this->set_post_data( + array( + 'id' => self::$post_id, + 'password' => '123', + ) + ) + ); + $permission = $endpoint->get_item_permissions_check( $request ); + + // Password provided in POST data, should not be used as authentication. + $this->assertNotWPError( $permission, 'Password in post body should be ignored by permissions check.' ); + $this->assertTrue( $permission ); + } + + /** + * @ticket 61837 + */ + public function test_get_item_permissions_check_while_updating_password_with_invalid_type() { + $endpoint = new WP_REST_Posts_Controller( 'post' ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) ); + $request->set_url_params( array( 'id' => self::$post_id ) ); + $request->set_body_params( + $this->set_post_data( + array( + 'id' => self::$post_id, + 'password' => 123, + ) + ) + ); + $permission = $endpoint->get_item_permissions_check( $request ); + + $this->assertNotWPError( $permission, 'Password in post body should be ignored by permissions check even when it is an invalid type.' ); + $this->assertTrue( $permission ); + } + /** * The post response should not have `block_version` when in view context. * From 98a9f6481afe3dc764f0ca5bb4f108cefe7203e0 Mon Sep 17 00:00:00 2001 From: Anthony Burchell Date: Tue, 17 Sep 2024 22:24:43 +0000 Subject: [PATCH 014/453] Coding Standards: Avoid using confusing `!` condition in Media Library selection check. Checks that value is now equal or less than or equal to 0 which has the same result as the previous confusing `!` usage. Props kadamwhite, drjosh07. See #60369. git-svn-id: https://develop.svn.wordpress.org/trunk@59037 602fd350-edb4-49c9-b593-d223f7449a82 --- src/js/media/views/modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/media/views/modal.js b/src/js/media/views/modal.js index e3d9ad1701c86..cfc396bf8039b 100644 --- a/src/js/media/views/modal.js +++ b/src/js/media/views/modal.js @@ -190,7 +190,7 @@ Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{ selectHandler: function( event ) { var selection = this.controller.state().get( 'selection' ); - if ( ! selection.length > 0 ) { + if ( selection.length <= 0 ) { return; } From 3cd3a00c76102913590ade96a44003deb01664c3 Mon Sep 17 00:00:00 2001 From: Timothy Jacobs Date: Tue, 17 Sep 2024 22:25:03 +0000 Subject: [PATCH 015/453] Build Tools: Allow easier customization of the .env file. The .env file allows for configuring how the WordPress Local environment should be configured. However, because the file is version controlled, developers must be careful not to commit their modifications. This commit renames the .env file to be .env.example. During env start, the .env.example file is copied to .env if it does not exist. This allows for contributors to continue using the project without thinking about .env and to make changes when needed. This brings WordPress Core into the dotenv project guidelines. Props johnbillion, afragen, h71, desrosj. Fixes #52668. git-svn-id: https://develop.svn.wordpress.org/trunk@59038 602fd350-edb4-49c9-b593-d223f7449a82 --- .env => .env.example | 0 .gitignore | 1 + tools/local-env/scripts/start.js | 14 ++++++++++++++ 3 files changed, 15 insertions(+) rename .env => .env.example (100%) diff --git a/.env b/.env.example similarity index 100% rename from .env rename to .env.example diff --git a/.gitignore b/.gitignore index 44c3769ee314d..f35d743ea866f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # gitignore file for WordPress Core # Configuration files with possibly sensitive information +.env wp-config.php wp-tests-config.php .htaccess diff --git a/tools/local-env/scripts/start.js b/tools/local-env/scripts/start.js index b929dce0b69a8..22bb65ca8426d 100644 --- a/tools/local-env/scripts/start.js +++ b/tools/local-env/scripts/start.js @@ -2,6 +2,20 @@ const dotenv = require( 'dotenv' ); const dotenvExpand = require( 'dotenv-expand' ); const { execSync } = require( 'child_process' ); +try { + execSync( 'test -f .env', { stdio: 'inherit' } ); +} catch ( e ) { + // test exits with a status code of 1 if the test fails. + // Alert the user on any other failure. + if ( e.status !== 1 ) { + throw e; + } + + // The file does not exist, copy over the default example file. + execSync( 'cp .env.example .env', { stdio: 'inherit' } ); +} + + dotenvExpand.expand( dotenv.config() ); // Check if the Docker service is running. From c1520f3684dcd4647ef789b2f47aa4850a4ce871 Mon Sep 17 00:00:00 2001 From: Aaron Jorbin Date: Tue, 17 Sep 2024 22:39:58 +0000 Subject: [PATCH 016/453] Bootstrap/Load: Ensure uses of set_time_limit are documented why. `set_time_limit` can cause unexpected behavior so it general should be avoided. There are instances though where they should be used so those instances should be properly documented. Props Rcrayno, ryan, kurtpayne, jorbin. Fixes #21521. See #19487. git-svn-id: https://develop.svn.wordpress.org/trunk@59039 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/ajax-actions.php | 1 + src/wp-admin/includes/class-wp-upgrader.php | 1 + src/wp-admin/includes/update-core.php | 1 + src/wp-includes/class-pop3.php | 2 ++ src/wp-includes/comment.php | 1 + src/wp-includes/deprecated.php | 1 + 6 files changed, 7 insertions(+) diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 62feaffc6d077..75d7a610006af 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -3556,6 +3556,7 @@ function wp_ajax_get_revision_diffs() { $return = array(); + // Removes the script timeout limit by setting it to 0 allowing ample time for diff UI setup. if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 0 ); } diff --git a/src/wp-admin/includes/class-wp-upgrader.php b/src/wp-admin/includes/class-wp-upgrader.php index d3ca761c3786d..67996aef3f12a 100644 --- a/src/wp-admin/includes/class-wp-upgrader.php +++ b/src/wp-admin/includes/class-wp-upgrader.php @@ -524,6 +524,7 @@ public function install_package( $args = array() ) { $destination = $args['destination']; $clear_destination = $args['clear_destination']; + // Give the upgrade an additional 300 seconds(5 minutes) to ensure the install doesn't prematurely timeout having used up the maximum script execution time upacking and downloading in WP_Upgrader->run. if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 300 ); } diff --git a/src/wp-admin/includes/update-core.php b/src/wp-admin/includes/update-core.php index edda202cc573c..8b29771cedaae 100644 --- a/src/wp-admin/includes/update-core.php +++ b/src/wp-admin/includes/update-core.php @@ -962,6 +962,7 @@ function update_core( $from, $to ) { global $wp_filesystem, $_old_files, $_old_requests_files, $_new_bundled_files, $wpdb; if ( function_exists( 'set_time_limit' ) ) { + // Gives core update script time an additional 300 seconds(5 minutes) to finish updating large files or run on slower servers. set_time_limit( 300 ); } diff --git a/src/wp-includes/class-pop3.php b/src/wp-includes/class-pop3.php index 7a72b1caf81a0..b4eab75044169 100644 --- a/src/wp-includes/class-pop3.php +++ b/src/wp-includes/class-pop3.php @@ -60,6 +60,7 @@ function __construct ( $server = '', $timeout = '' ) { settype($timeout,"integer"); $this->TIMEOUT = $timeout; if(function_exists("set_time_limit")){ + // Extends POP3 request timeout to specified TIMEOUT property. set_time_limit($timeout); } } @@ -75,6 +76,7 @@ public function POP3( $server = '', $timeout = '' ) { function update_timer () { if(function_exists("set_time_limit")){ + // Allows additional extension of POP3 request timeout to specified TIMEOUT property when update_timer is called. set_time_limit($this->TIMEOUT); } return true; diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index e4cc901df943c..d336399117284 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -3116,6 +3116,7 @@ function pingback( $content, $post ) { if ( $pingback_server_url ) { if ( function_exists( 'set_time_limit' ) ) { + // Allows an additional 60 seconds for each pingback to complete. set_time_limit( 60 ); } diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php index 8b7d84b63e911..e692772f272ae 100644 --- a/src/wp-includes/deprecated.php +++ b/src/wp-includes/deprecated.php @@ -3673,6 +3673,7 @@ function post_permalink( $post = 0 ) { function wp_get_http( $url, $file_path = false, $red = 1 ) { _deprecated_function( __FUNCTION__, '4.4.0', 'WP_Http' ); + // Adds an additional 60 seconds to the script timeout to ensure the remote request has enough time. if ( function_exists( 'set_time_limit' ) ) { @set_time_limit( 60 ); } From 71c69dad2e05fd51c508b1311805083943dc5398 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 17 Sep 2024 23:22:43 +0000 Subject: [PATCH 017/453] REST API: Allow posts to be published with a publication date of midnight 1970-01-01. Explicitly checks date parsing return values for `false`, so that `0` (the value returned for the UNIX epoch of `1970-01-01 00:00:00`) is correctly treated as a valid timestamp. It should be valid to create a post dated to any point in history. Props emmanuel78, sabernhardt, siliconforks, drjosh07, antpb, TimothyBlynJacobs. Fixes #60184. git-svn-id: https://develop.svn.wordpress.org/trunk@59040 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/rest-api.php | 7 +++++-- .../phpunit/tests/rest-api/rest-schema-validation.php | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 9b023e97ece1b..242b317ca1fc7 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -1304,6 +1304,9 @@ function rest_get_avatar_sizes() { /** * Parses an RFC3339 time into a Unix timestamp. * + * Explicitly check for `false` to detect failure, as zero is a valid return + * value on success. + * * @since 4.4.0 * * @param string $date RFC3339 timestamp. @@ -1369,7 +1372,7 @@ function rest_get_date_with_gmt( $date, $is_utc = false ) { $date = rest_parse_date( $date ); - if ( empty( $date ) ) { + if ( false === $date ) { return null; } @@ -2259,7 +2262,7 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) { break; case 'date-time': - if ( ! rest_parse_date( $value ) ) { + if ( false === rest_parse_date( $value ) ) { return new WP_Error( 'rest_invalid_date', __( 'Invalid date.' ) ); } break; diff --git a/tests/phpunit/tests/rest-api/rest-schema-validation.php b/tests/phpunit/tests/rest-api/rest-schema-validation.php index 67f01356d93da..ce8875c3e9339 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-validation.php +++ b/tests/phpunit/tests/rest-api/rest-schema-validation.php @@ -1020,6 +1020,17 @@ public function test_nullable_date() { $this->assertSame( 'Invalid date.', $error->get_error_message() ); } + /** + * @ticket 60184 + */ + public function test_epoch() { + $schema = array( + 'type' => 'string', + 'format' => 'date-time', + ); + $this->assertTrue( rest_validate_value_from_schema( '1970-01-01T00:00:00Z', $schema ) ); + } + public function test_object_or_string() { $schema = array( 'type' => array( 'object', 'string' ), From f9aeb0bdc14b251b7f1497771ce34fdeea7578a4 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Tue, 17 Sep 2024 23:26:03 +0000 Subject: [PATCH 018/453] Accessibility: Add border around menus and submenus in high contrast mode. Add outlines and borders to mark the boundaries between the admin navigation menu and content and around adminbar submenus that are visible when Windows High Contrast Mode is enabled. This clarifies the page structure and makes high contrast mode easier to use. Props wildworks, hbhalodia, sabernhardt, joedolson, rcreators. Fixes #61616. git-svn-id: https://develop.svn.wordpress.org/trunk@59041 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/admin-menu.css | 19 +++++++++++++------ src/wp-includes/css/admin-bar.css | 6 +++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/wp-admin/css/admin-menu.css b/src/wp-admin/css/admin-menu.css index 0dc59c6e6b2c2..e65597cafb41e 100644 --- a/src/wp-admin/css/admin-menu.css +++ b/src/wp-admin/css/admin-menu.css @@ -11,6 +11,9 @@ top: 0; bottom: -120px; z-index: 1; /* positive z-index to avoid elastic scrolling woes in Safari */ + + /* Only visible in Windows High Contrast mode */ + outline: 1px solid transparent; } .php-error #adminmenuback { @@ -205,7 +208,8 @@ .folded #adminmenu .wp-has-current-submenu .wp-submenu { min-width: 160px; width: auto; - border-left: 5px solid transparent; + border: 1px solid transparent; + border-left-width: 5px; } #adminmenu .wp-submenu li.current, @@ -415,8 +419,8 @@ ul#adminmenu > li.current > a.current:after { font-weight: 400; font-size: 14px; padding: 5px 4px 5px 11px; - margin: -7px 0 4px -5px; - border-width: 3px 0 3px 5px; + margin: -8px -1px 4px -5px; + border-width: 3px 1px 3px 5px; border-style: solid; border-color: transparent; } @@ -581,8 +585,10 @@ li#wp-admin-bar-menu-toggle { } .auto-fold #adminmenu .wp-has-current-submenu .wp-submenu { - min-width: 150px; + min-width: 160px; width: auto; + border: 1px solid transparent; + border-left-width: 5px; } .auto-fold #adminmenu .wp-has-current-submenu li > a { @@ -771,8 +777,9 @@ li#wp-admin-bar-menu-toggle { } #adminmenu .wp-not-current-submenu .wp-submenu, - .folded #adminmenu .wp-has-current-submenu .wp-submenu { - border-left: none; + .folded #adminmenu .wp-has-current-submenu .wp-submenu, + .auto-fold #adminmenu .wp-has-current-submenu .wp-submenu { + border: none; } /* Remove submenu headers and adjust sub meu*/ diff --git a/src/wp-includes/css/admin-bar.css b/src/wp-includes/css/admin-bar.css index c4e8ba3afaa6e..b3f13b51db42c 100644 --- a/src/wp-includes/css/admin-bar.css +++ b/src/wp-includes/css/admin-bar.css @@ -97,6 +97,8 @@ html:lang(he-il) .rtl #wpadminbar * { min-width: 600px; /* match the min-width of the body in wp-admin/css/common.css */ z-index: 99999; background: #1d2327; + /* Only visible in Windows High Contrast mode */ + outline: 1px solid transparent; } #wpadminbar .ab-sub-wrapper, @@ -195,6 +197,8 @@ html:lang(he-il) .rtl #wpadminbar * { #wpadminbar.nojs li:hover > .ab-sub-wrapper, #wpadminbar li.hover > .ab-sub-wrapper { display: block; + /* Only visible in Windows High Contrast mode */ + outline: 1px solid transparent; } #wpadminbar .menupop li:hover > .ab-sub-wrapper, @@ -906,7 +910,7 @@ html:lang(he-il) .rtl #wpadminbar * { /* New Content */ #wpadminbar #wp-admin-bar-new-content .ab-icon:before { top: 0; - line-height: 1.33333333; + line-height: 1.26; height: 46px !important; text-align: center; width: 52px; From 8f6ec896348f83e63fa239f376d5010b85d24794 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 17 Sep 2024 23:26:22 +0000 Subject: [PATCH 019/453] Media: improve speed of AVIF image generation. Set the AVIF encoder to work faster by raising heic:speed to 7 from the default of 5. AVIF generation time is reduced by up to 20% with minimal impact on image size. Props: adamsilverstein, erikyo, mukesh27, yguyon, felixarntz, jzern. Fixes #61758. git-svn-id: https://develop.svn.wordpress.org/trunk@59042 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-image-editor-imagick.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 819be5e09017c..72b1e42d41e9c 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -219,6 +219,11 @@ public function set_quality( $quality = null ) { $this->image->setImageCompressionQuality( $quality ); } break; + case 'image/avif': + // Set the AVIF encoder to work faster, with minimal impact on image size. + $this->image->setOption( 'heic:speed', 7 ); + $this->image->setImageCompressionQuality( $quality ); + break; default: $this->image->setImageCompressionQuality( $quality ); } From 36f6f6450175e9b5e2213aa9c4e44eb6e10f18c8 Mon Sep 17 00:00:00 2001 From: Helen Hou-Sandi Date: Tue, 17 Sep 2024 23:48:26 +0000 Subject: [PATCH 020/453] Bootstrap/Load: Give more context and warning about editing compat.php. As indicated by name, this is a compatibility file which warrants more care to begin with, but it's still worth warning folks about how narrow function availability is in this file. Props jorbin, dmsnell, helen. See #61694. git-svn-id: https://develop.svn.wordpress.org/trunk@59043 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/compat.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php index 030a288d823ca..531c76fe1b2b3 100644 --- a/src/wp-includes/compat.php +++ b/src/wp-includes/compat.php @@ -2,6 +2,11 @@ /** * WordPress implementation for PHP functions either missing from older PHP versions or not included by default. * + * This file is loaded extremely early and the functions can be relied upon by drop-ins. + * Ergo, please ensure you do not rely on external functions when writing code for this file. + * Only use functions built into PHP or are defined in this file and have adequate testing + * and error suppression to ensure the file will run correctly and not break websites. + * * @package PHP * @access private */ From e9bb88d8c2da2c5ea1831ecbd7909ae19ba29708 Mon Sep 17 00:00:00 2001 From: Aaron Jorbin Date: Tue, 17 Sep 2024 23:56:10 +0000 Subject: [PATCH 021/453] Bootstrap/Load: Add documentation warning about updating `$table_prefix`. Props bjerke-johannessen, swissspidy, SergeyBiryukov, morganestes, stevenlinx, jorbin. Fixes #34189. git-svn-id: https://develop.svn.wordpress.org/trunk@59044 602fd350-edb4-49c9-b593-d223f7449a82 --- wp-config-sample.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wp-config-sample.php b/wp-config-sample.php index bdea5cd755496..039c16f696991 100644 --- a/wp-config-sample.php +++ b/wp-config-sample.php @@ -64,6 +64,12 @@ * * You can have multiple installations in one database if you give each * a unique prefix. Only numbers, letters, and underscores please! + * + * At the installation time, DB tables names with $table_prefix are created. + * Changing this value after WordPress is installed will make your site think + * it has not been installed. + * + * @link https://developer.wordpress.org/advanced-administration/wordpress/wp-config/#table-prefix */ $table_prefix = 'wp_'; From b0809808e21827e4f6d0325695f37b67211067d5 Mon Sep 17 00:00:00 2001 From: Drew Jaynes Date: Wed, 18 Sep 2024 00:00:08 +0000 Subject: [PATCH 022/453] Docs: Add possible filter names to the hook docs for the following filters in `sanitize_post_field()`: - `edit_{$field}` - `{$field_no_prefix}_edit_pre` - `edit_post_{$field}` - `pre_{$field}` - `{$field_no_prefix}_save_pre` - `pre_post_{$field}` - `{$field}_pre` - `{$field}` - `post_{$field}` Props johnbillion, DrewAPicture. Fixes #50654 git-svn-id: https://develop.svn.wordpress.org/trunk@59045 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/post.php | 180 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 725433939e060..8812957bf215e 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -2896,7 +2896,23 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { * Filters the value of a specific post field to edit. * * The dynamic portion of the hook name, `$field`, refers to the post - * field name. + * field name. Possible filter names include: + * + * - `edit_post_author` + * - `edit_post_date` + * - `edit_post_date_gmt` + * - `edit_post_content` + * - `edit_post_title` + * - `edit_post_excerpt` + * - `edit_post_status` + * - `edit_post_password` + * - `edit_post_name` + * - `edit_post_modified` + * - `edit_post_modified_gmt` + * - `edit_post_content_filtered` + * - `edit_post_parent` + * - `edit_post_type` + * - `edit_post_mime_type` * * @since 2.3.0 * @@ -2908,8 +2924,26 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { /** * Filters the value of a specific post field to edit. * - * The dynamic portion of the hook name, `$field_no_prefix`, refers to - * the post field name. + * Only applied to post fields with a name which is prefixed with `post_`. + * + * The dynamic portion of the hook name, `$field_no_prefix`, refers to the + * post field name minus the `post_` prefix. Possible filter names include: + * + * - `author_edit_pre` + * - `date_edit_pre` + * - `date_gmt_edit_pre` + * - `content_edit_pre` + * - `title_edit_pre` + * - `excerpt_edit_pre` + * - `status_edit_pre` + * - `password_edit_pre` + * - `name_edit_pre` + * - `modified_edit_pre` + * - `modified_gmt_edit_pre` + * - `content_filtered_edit_pre` + * - `parent_edit_pre` + * - `type_edit_pre` + * - `mime_type_edit_pre` * * @since 2.3.0 * @@ -2918,6 +2952,24 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { */ $value = apply_filters( "{$field_no_prefix}_edit_pre", $value, $post_id ); } else { + /** + * Filters the value of a specific post field to edit. + * + * Only applied to post fields not prefixed with `post_`. + * + * The dynamic portion of the hook name, `$field`, refers to the + * post field name. Possible filter names include: + * + * - `edit_post_ID` + * - `edit_post_ping_status` + * - `edit_post_pinged` + * - `edit_post_to_ping` + * - `edit_post_comment_count` + * - `edit_post_comment_status` + * - `edit_post_guid` + * - `edit_post_menu_order` + * @since + */ $value = apply_filters( "edit_post_{$field}", $value, $post_id ); } @@ -2936,8 +2988,26 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { /** * Filters the value of a specific post field before saving. * + * Only applied to post fields with a name which is prefixed with `post_`. + * * The dynamic portion of the hook name, `$field`, refers to the post - * field name. + * field name. Possible filter names include: + * + * - `pre_post_author` + * - `pre_post_date` + * - `pre_post_date_gmt` + * - `pre_post_content` + * - `pre_post_title` + * - `pre_post_excerpt` + * - `pre_post_status` + * - `pre_post_password` + * - `pre_post_name` + * - `pre_post_modified` + * - `pre_post_modified_gmt` + * - `pre_post_content_filtered` + * - `pre_post_parent` + * - `pre_post_type` + * - `pre_post_mime_type` * * @since 2.3.0 * @@ -2948,8 +3018,26 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { /** * Filters the value of a specific field before saving. * - * The dynamic portion of the hook name, `$field_no_prefix`, refers - * to the post field name. + * Only applied to post fields with a name which is prefixed with `post_`. + * + * The dynamic portion of the hook name, `$field_no_prefix`, refers to the + * post field name minus the `post_` prefix. Possible filter names include: + * + * - `author_save_pre` + * - `date_save_pre` + * - `date_gmt_save_pre` + * - `content_save_pre` + * - `title_save_pre` + * - `excerpt_save_pre` + * - `status_save_pre` + * - `password_save_pre` + * - `name_save_pre` + * - `modified_save_pre` + * - `modified_gmt_save_pre` + * - `content_filtered_save_pre` + * - `parent_save_pre` + * - `type_save_pre` + * - `mime_type_save_pre` * * @since 2.3.0 * @@ -2957,13 +3045,45 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { */ $value = apply_filters( "{$field_no_prefix}_save_pre", $value ); } else { + /** + * Filters the value of a specific field before saving. + * + * Only applied to post fields with a name which is prefixed with `post_`. + * + * The dynamic portion of the hook name, `$field_no_prefix`, refers to the + * post field name minus the `post_` prefix. Possible filter names include: + * + * - `pre_post_ID` + * - `pre_post_comment_status` + * - `pre_post_ping_status` + * - `pre_post_to_ping` + * - `pre_post_pinged` + * - `pre_post_guid` + * - `pre_post_menu_order` + * - `pre_post_comment_count` + * + * @since 2.3.0 + * + * @param mixed $value Value of the post field. + */ $value = apply_filters( "pre_post_{$field}", $value ); /** * Filters the value of a specific post field before saving. * + * Only applied to post fields with a name which is *not* prefixed with `post_`. + * * The dynamic portion of the hook name, `$field`, refers to the post - * field name. + * field name. Possible filter names include: + * + * - `ID_pre` + * - `comment_status_pre` + * - `ping_status_pre` + * - `to_ping_pre` + * - `pinged_pre` + * - `guid_pre` + * - `menu_order_pre` + * - `comment_count_pre` * * @since 2.3.0 * @@ -2979,8 +3099,26 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { /** * Filters the value of a specific post field for display. * + * Only applied to post fields with a name which is prefixed with `post_`. + * * The dynamic portion of the hook name, `$field`, refers to the post - * field name. + * field name. Possible filter names include: + * + * - `post_author` + * - `post_date` + * - `post_date_gmt` + * - `post_content` + * - `post_title` + * - `post_excerpt` + * - `post_status` + * - `post_password` + * - `post_name` + * - `post_modified` + * - `post_modified_gmt` + * - `post_content_filtered` + * - `post_parent` + * - `post_type` + * - `post_mime_type` * * @since 2.3.0 * @@ -2992,6 +3130,31 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { */ $value = apply_filters( "{$field}", $value, $post_id, $context ); } else { + /** + * Filters the value of a specific post field for display. + * + * Only applied to post fields name which is *not* prefixed with `post_`. + * + * The dynamic portion of the hook name, `$field`, refers to the post + * field name. Possible filter names include: + * + * - `post_ID` + * - `post_comment_status` + * - `post_ping_status` + * - `post_to_ping` + * - `post_pinged` + * - `post_guid` + * - `post_menu_order` + * - `post_comment_count` + * + * @since 2.3.0 + * + * @param mixed $value Value of the unprefixed post field. + * @param int $post_id Post ID + * @param string $context Context for how to sanitize the field. + * Accepts 'raw', 'edit', 'db', 'display', + * 'attribute', or 'js'. Default 'display'. + */ $value = apply_filters( "post_{$field}", $value, $post_id, $context ); } @@ -3006,7 +3169,6 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { if ( in_array( $field, $int_fields, true ) ) { $value = (int) $value; } - return $value; } From 5d0753bb6e7c29adc8e6171ee21338cb5ade297b Mon Sep 17 00:00:00 2001 From: Jeremy Felt Date: Wed, 18 Sep 2024 00:12:52 +0000 Subject: [PATCH 023/453] Application Passwords: Add copy button when adding new password. Props circlecube, dhruvang21, ironprogrammer, desrosj. Fixes #62019. git-svn-id: https://develop.svn.wordpress.org/trunk@59046 602fd350-edb4-49c9-b593-d223f7449a82 --- src/js/_enqueues/admin/user-profile.js | 27 +++++++++++++++++-- src/wp-admin/css/common.css | 5 ++++ src/wp-admin/css/forms.css | 1 + src/wp-admin/user-edit.php | 2 ++ src/wp-includes/script-loader.php | 2 +- .../profile/applications-passwords.test.js | 7 ++++- 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/js/_enqueues/admin/user-profile.js b/src/js/_enqueues/admin/user-profile.js index 54215c7b420b8..2aebe62a91f94 100644 --- a/src/js/_enqueues/admin/user-profile.js +++ b/src/js/_enqueues/admin/user-profile.js @@ -2,11 +2,12 @@ * @output wp-admin/js/user-profile.js */ -/* global ajaxurl, pwsL10n, userProfileL10n */ +/* global ajaxurl, pwsL10n, userProfileL10n, ClipboardJS */ (function($) { var updateLock = false, isSubmitting = false, __ = wp.i18n.__, + clipboard = new ClipboardJS( '.application-password-display .copy-button' ), $pass1Row, $pass1, $pass2, @@ -18,7 +19,8 @@ currentPass, $form, originalFormContent, - $passwordWrapper; + $passwordWrapper, + successTimeout; function generatePassword() { if ( typeof zxcvbn !== 'function' ) { @@ -346,6 +348,27 @@ } } + // Debug information copy section. + clipboard.on( 'success', function( e ) { + var triggerElement = $( e.trigger ), + successElement = $( '.success', triggerElement.closest( '.application-password-display' ) ); + + // Clear the selection and move focus back to the trigger. + e.clearSelection(); + + // Show success visual feedback. + clearTimeout( successTimeout ); + successElement.removeClass( 'hidden' ); + + // Hide success visual feedback after 3 seconds since last success. + successTimeout = setTimeout( function() { + successElement.addClass( 'hidden' ); + }, 3000 ); + + // Handle success audible feedback. + wp.a11y.speak( __( 'Application password has been copied to your clipboard.' ) ); + } ); + $( function() { var $colorpicker, $stylesheet, user_id, current_user_id, select = $( '#display_name' ), diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css index 287f2ee7b4958..8d5486e797606 100644 --- a/src/wp-admin/css/common.css +++ b/src/wp-admin/css/common.css @@ -916,6 +916,11 @@ a#remove-post-thumbnail:hover, border: none; } +.application-password-display .success { + color: #007017; + margin-left: 0.5rem; +} + /*------------------------------------------------------------------------------ 3.0 - Actions ------------------------------------------------------------------------------*/ diff --git a/src/wp-admin/css/forms.css b/src/wp-admin/css/forms.css index 41d592d148811..ffe2c1711e7a1 100644 --- a/src/wp-admin/css/forms.css +++ b/src/wp-admin/css/forms.css @@ -1002,6 +1002,7 @@ table.form-table td .updated p { } .application-password-display input.code { + margin-bottom: 6px; width: 19em; } diff --git a/src/wp-admin/user-edit.php b/src/wp-admin/user-edit.php index 860a0a6e07340..55ed2749f6447 100644 --- a/src/wp-admin/user-edit.php +++ b/src/wp-admin/user-edit.php @@ -984,6 +984,8 @@ ?> + +