diff options
-rw-r--r-- | .git-blame-ignore-revs | 3 | ||||
-rw-r--r-- | src/wp-admin/network/site-new.php | 2 | ||||
-rw-r--r-- | src/wp-content/themes/twentytwenty/functions.php | 6 | ||||
-rw-r--r-- | src/wp-includes/blocks.php | 21 | ||||
-rw-r--r-- | src/wp-includes/class-wp-customize-manager.php | 36 | ||||
-rw-r--r-- | src/wp-includes/class-wp-theme-json.php | 7 | ||||
-rw-r--r-- | src/wp-includes/comment-template.php | 8 | ||||
-rw-r--r-- | src/wp-includes/comment.php | 29 | ||||
-rw-r--r-- | src/wp-includes/embed.php | 2 | ||||
-rw-r--r-- | src/wp-includes/formatting.php | 12 | ||||
-rw-r--r-- | src/wp-includes/kses.php | 28 | ||||
-rw-r--r-- | src/wp-includes/media.php | 24 | ||||
-rw-r--r-- | src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php | 11 | ||||
-rw-r--r-- | src/wp-includes/version.php | 2 | ||||
-rw-r--r-- | tests/phpunit/tests/formatting/sanitizeFileName.php | 12 | ||||
-rw-r--r-- | tests/phpunit/tests/media.php | 47 | ||||
-rw-r--r-- | tests/phpunit/tests/rest-api/rest-application-passwords-controller.php | 43 | ||||
-rw-r--r-- | tests/qunit/fixtures/wp-api-generated.js | 26 |
18 files changed, 246 insertions, 73 deletions
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 77c4e990b0..054a175385 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -59,3 +59,6 @@ a96fa164b00ed51c7c0481574834cff92ab9b1f0 # [60043] 1aa6da693ad739b78752a55d154cd48cb757b90b # [60047] d44e1c2ce2dc638e89ed6a1d02b1cfadb8a15fe7 # [60048] a18719e7ea49ab7ac0091e076840cb7efdf51cc5 # [60049] + +# 6.9 Coding Standards +cbb6519119276ceba4279eaee73ab66294ebd820 # [60402] diff --git a/src/wp-admin/network/site-new.php b/src/wp-admin/network/site-new.php index a3b0919155..d1b0576113 100644 --- a/src/wp-admin/network/site-new.php +++ b/src/wp-admin/network/site-new.php @@ -204,7 +204,7 @@ if ( ! empty( $messages ) ) { } ?> <p><?php echo wp_required_field_message(); ?></p> -<form method="post" action="<?php echo esc_url( network_admin_url( 'site-new.php?action=add-site' ) ); ?>" novalidate="novalidate"> +<form method="post" enctype="multipart/form-data" action="<?php echo esc_url( network_admin_url( 'site-new.php?action=add-site' ) ); ?>" novalidate="novalidate"> <?php wp_nonce_field( 'add-blog', '_wpnonce_add-blog' ); ?> <table class="form-table" role="presentation"> <tr class="form-field form-required"> diff --git a/src/wp-content/themes/twentytwenty/functions.php b/src/wp-content/themes/twentytwenty/functions.php index 96945243fa..a3ca940c4d 100644 --- a/src/wp-content/themes/twentytwenty/functions.php +++ b/src/wp-content/themes/twentytwenty/functions.php @@ -361,7 +361,11 @@ if ( ! function_exists( 'wp_body_open' ) ) { * @since Twenty Twenty 1.0 */ function wp_body_open() { - /** This action is documented in wp-includes/general-template.php */ + /** + * Triggered after the opening <body> tag. + * + * @since Twenty Twenty 1.0 + */ do_action( 'wp_body_open' ); } } diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 3d18b37b83..56410779fe 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -2408,8 +2408,27 @@ function do_blocks( $content ) { $top_level_block_count = count( $blocks ); $output = ''; + /** + * Parsed blocks consist of a list of top-level blocks. Those top-level + * blocks may themselves contain nested inner blocks. However, every + * top-level block is rendered independently, meaning there are no data + * dependencies between them. + * + * Ideally, therefore, the parser would only need to parse one complete + * top-level block at a time, render it, and move on. Unfortunately, this + * is not possible with {@see \parse_blocks()} because it must parse the + * entire given document at once. + * + * While the current implementation prevents this optimization, it’s still + * possible to reduce the peak memory use when calls to `render_block()` + * on those top-level blocks are memory-heavy (which many of them are). + * By setting each parsed block to `NULL` after rendering it, any memory + * allocated during the render will be freed and reused for the next block. + * Before making this change, that memory was retained and would lead to + * out-of-memory crashes for certain posts that now run with this change. + */ for ( $i = 0; $i < $top_level_block_count; $i++ ) { - $output .= render_block( $blocks[ $i ] ); + $output .= render_block( $blocks[ $i ] ); $blocks[ $i ] = null; } diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 51c88ef5fc..0d41d4a09b 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -3165,27 +3165,25 @@ final class WP_Customize_Manager { return; } - if ( $changeset_post_id ) { - if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) { - wp_send_json_error( - array( - 'code' => 'changeset_trash_unauthorized', - 'message' => __( 'Unable to trash changes.' ), - ) - ); - } + if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) { + wp_send_json_error( + array( + 'code' => 'changeset_trash_unauthorized', + 'message' => __( 'Unable to trash changes.' ), + ) + ); + } - $lock_user = (int) wp_check_post_lock( $changeset_post_id ); + $lock_user = (int) wp_check_post_lock( $changeset_post_id ); - if ( $lock_user && get_current_user_id() !== $lock_user ) { - wp_send_json_error( - array( - 'code' => 'changeset_locked', - 'message' => __( 'Changeset is being edited by other user.' ), - 'lockUser' => $this->get_lock_user_data( $lock_user ), - ) - ); - } + if ( $lock_user && get_current_user_id() !== $lock_user ) { + wp_send_json_error( + array( + 'code' => 'changeset_locked', + 'message' => __( 'Changeset is being edited by other user.' ), + 'lockUser' => $this->get_lock_user_data( $lock_user ), + ) + ); } if ( 'trash' === get_post_status( $changeset_post_id ) ) { diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index f3f015ccd3..588aeaa89e 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -2781,6 +2781,7 @@ class WP_Theme_JSON { if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { $node_path = array( 'styles', 'blocks', $name, 'elements', $element ); + if ( $include_node_paths_only ) { $nodes[] = array( 'path' => $node_path, @@ -2798,12 +2799,6 @@ class WP_Theme_JSON { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) { $node_path = array( 'styles', 'blocks', $name, 'elements', $element ); - if ( $include_node_paths_only ) { - $nodes[] = array( - 'path' => $node_path, - ); - continue; - } $nodes[] = array( 'path' => $node_path, diff --git a/src/wp-includes/comment-template.php b/src/wp-includes/comment-template.php index f74249c81b..59f89f3a84 100644 --- a/src/wp-includes/comment-template.php +++ b/src/wp-includes/comment-template.php @@ -834,12 +834,8 @@ function get_comment_link( $comment = null, $args = array() ) { if ( $cpage && get_option( 'page_comments' ) ) { if ( $wp_rewrite->using_permalinks() ) { - if ( $cpage ) { - $comment_link = trailingslashit( $comment_link ) . $wp_rewrite->comments_pagination_base . '-' . $cpage; - } - - $comment_link = user_trailingslashit( $comment_link, 'comment' ); - } elseif ( $cpage ) { + $comment_link = trailingslashit( $comment_link ) . $wp_rewrite->comments_pagination_base . '-' . $cpage; + } else { $comment_link = add_query_arg( 'cpage', $cpage, $comment_link ); } } diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 9bd6fb2171..aabe9f60db 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -3060,22 +3060,19 @@ function do_trackbacks( $post ) { $post_title = apply_filters( 'the_title', $post->post_title, $post->ID ); $post_title = strip_tags( $post_title ); - if ( $to_ping ) { - foreach ( (array) $to_ping as $tb_ping ) { - $tb_ping = trim( $tb_ping ); - if ( ! in_array( $tb_ping, $pinged, true ) ) { - trackback( $tb_ping, $post_title, $excerpt, $post->ID ); - $pinged[] = $tb_ping; - } else { - $wpdb->query( - $wpdb->prepare( - "UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, - '')) WHERE ID = %d", - $tb_ping, - $post->ID - ) - ); - } + foreach ( (array) $to_ping as $tb_ping ) { + $tb_ping = trim( $tb_ping ); + if ( ! in_array( $tb_ping, $pinged, true ) ) { + trackback( $tb_ping, $post_title, $excerpt, $post->ID ); + $pinged[] = $tb_ping; + } else { + $wpdb->query( + $wpdb->prepare( + "UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", + $tb_ping, + $post->ID + ) + ); } } } diff --git a/src/wp-includes/embed.php b/src/wp-includes/embed.php index a3c23be931..c38a079003 100644 --- a/src/wp-includes/embed.php +++ b/src/wp-includes/embed.php @@ -765,7 +765,7 @@ function wp_oembed_ensure_format( $format ) { * @param WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`. * @param WP_REST_Request $request Request used to generate the response. * @param WP_REST_Server $server Server instance. - * @return true + * @return bool True if the request was served, false otherwise. */ function _oembed_rest_pre_serve_request( $served, $result, $request, $server ) { $params = $request->get_params(); diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 4033571edf..234d71a2a1 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2035,7 +2035,17 @@ function sanitize_file_name( $filename ) { } if ( $utf8_pcre ) { - $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename ); + /** + * Replace all whitespace characters with a basic space (U+0020). + * + * The “Zs” in the pattern selects characters in the `Space_Separator` + * category, which is what Unicode considers space characters. + * + * @see https://www.unicode.org/reports/tr44/#General_Category_Values + * @see https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-6/#G17548 + * @see https://www.php.net/manual/en/regexp.reference.unicode.php + */ + $filename = preg_replace( '#\p{Zs}#siu', ' ', $filename ); } /** diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index ebb4a761b1..28bbce222a 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -2083,18 +2083,38 @@ function wp_kses_normalize_entities3( $matches ) { /** * Determines if a Unicode codepoint is valid. * + * The definition of a valid Unicode codepoint is taken from the XML definition: + * + * > Characters + * > + * > … + * > Legal characters are tab, carriage return, line feed, and the legal characters of + * > Unicode and ISO/IEC 10646. + * > … + * > Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + * * @since 2.7.0 * + * @see https://www.w3.org/TR/xml/#charsets + * * @param int $i Unicode codepoint. * @return bool Whether or not the codepoint is a valid Unicode codepoint. */ function valid_unicode( $i ) { $i = (int) $i; - return ( 0x9 === $i || 0xa === $i || 0xd === $i || - ( 0x20 <= $i && $i <= 0xd7ff ) || - ( 0xe000 <= $i && $i <= 0xfffd ) || - ( 0x10000 <= $i && $i <= 0x10ffff ) + return ( + 0x9 === $i || // U+0009 HORIZONTAL TABULATION (HT) + 0xA === $i || // U+000A LINE FEED (LF) + 0xD === $i || // U+000D CARRIAGE RETURN (CR) + /* + * The valid Unicode characters according to the XML specification: + * + * > any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. + */ + ( 0x20 <= $i && $i <= 0xD7FF ) || + ( 0xE000 <= $i && $i <= 0xFFFD ) || + ( 0x10000 <= $i && $i <= 0x10FFFF ) ); } diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 5d95b0a188..ef3610c56f 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1070,7 +1070,6 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f list( $src, $width, $height ) = $image; $attachment = get_post( $attachment_id ); - $hwstring = image_hwstring( $width, $height ); $size_class = $size; if ( is_array( $size_class ) ) { @@ -1090,15 +1089,14 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f * * @param string $context The context. Default 'wp_get_attachment_image'. */ - $context = apply_filters( 'wp_get_attachment_image_context', 'wp_get_attachment_image' ); - $attr = wp_parse_args( $attr, $default_attr ); + $context = apply_filters( 'wp_get_attachment_image_context', 'wp_get_attachment_image' ); + $attr = wp_parse_args( $attr, $default_attr ); + $attr['width'] = $width; + $attr['height'] = $height; - $loading_attr = $attr; - $loading_attr['width'] = $width; - $loading_attr['height'] = $height; $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', - $loading_attr, + $attr, $context ); @@ -1169,8 +1167,16 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f */ $attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment, $size ); - $attr = array_map( 'esc_attr', $attr ); - $html = rtrim( "<img $hwstring" ); + if ( isset( $attr['height'] ) && is_numeric( $attr['height'] ) ) { + $height = absint( $attr['height'] ); + } + if ( isset( $attr['width'] ) && is_numeric( $attr['width'] ) ) { + $width = absint( $attr['width'] ); + } + unset( $attr['height'], $attr['width'] ); + $attr = array_map( 'esc_attr', $attr ); + $hwstring = image_hwstring( $width, $height ); + $html = rtrim( "<img $hwstring" ); foreach ( $attr as $name => $value ) { $html .= " $name=" . '"' . $value . '"'; diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php index b0ac65a647..767917d6f6 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php @@ -802,7 +802,16 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller { 'app_id' => array( 'description' => __( 'A UUID provided by the application to uniquely identify it. It is recommended to use an UUID v5 with the URL or DNS namespace.' ), 'type' => 'string', - 'format' => 'uuid', + 'oneOf' => array( + array( + 'type' => 'string', + 'format' => 'uuid', + ), + array( + 'type' => 'string', + 'enum' => array( '' ), + ), + ), 'context' => array( 'view', 'edit', 'embed' ), ), 'name' => array( diff --git a/src/wp-includes/version.php b/src/wp-includes/version.php index 3b43518692..c7ee55fc6e 100644 --- a/src/wp-includes/version.php +++ b/src/wp-includes/version.php @@ -30,7 +30,7 @@ $wp_db_version = 58975; * * @global string $tinymce_version */ -$tinymce_version = '49110-20201110'; +$tinymce_version = '49110-20250317'; /** * Holds the minimum required PHP version. diff --git a/tests/phpunit/tests/formatting/sanitizeFileName.php b/tests/phpunit/tests/formatting/sanitizeFileName.php index d0fac9e232..d0e366f121 100644 --- a/tests/phpunit/tests/formatting/sanitizeFileName.php +++ b/tests/phpunit/tests/formatting/sanitizeFileName.php @@ -35,13 +35,17 @@ class Tests_Formatting_SanitizeFileName extends WP_UnitTestCase { * Test that spaces are correctly replaced with dashes. * * @ticket 16330 + * @ticket 62995 */ public function test_replaces_spaces() { $urls = array( - 'unencoded space.png' => 'unencoded-space.png', - 'encoded-space.jpg' => 'encoded-space.jpg', - 'plus+space.jpg' => 'plusspace.jpg', - 'multi %20 +space.png' => 'multi-20-space.png', + 'unencoded space.png' => 'unencoded-space.png', + 'encoded-space.jpg' => 'encoded-space.jpg', + 'plus+space.jpg' => 'plusspace.jpg', + 'multi %20 +space.png' => 'multi-20-space.png', + "Screenshot 2025-02-19 at 2.17.33\u{202F}PM.png" => 'Screenshot-2025-02-19-at-2.17.33-PM.png', + "Filename with non-breaking\u{00A0}space.txt" => 'Filename-with-non-breaking-space.txt', + "Filename with thin\u{2009}space.txt" => 'Filename-with-thin-space.txt', ); foreach ( $urls as $test => $expected ) { diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index a1e70c2ff1..902c704b39 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -1583,6 +1583,53 @@ EOF; $this->assertSame( $expected, $output ); } + /** + * @ticket 14110 + */ + public function test_wp_get_attachment_image_filter_with_height_width() { + $mock_action = new MockAction(); + add_filter( 'wp_get_attachment_image_attributes', array( $mock_action, 'filter' ) ); + wp_get_attachment_image( self::$large_id ); + $args = $mock_action->get_args(); + $this->assertArrayHasKey( '0', $args, 'First argument should be an array.' ); + $this->assertArrayHasKey( '0', $args[0], 'First argument should be an array.' ); + $this->assertArrayHasKey( 'width', $args[0][0], 'Width should be set.' ); + $this->assertArrayHasKey( 'height', $args[0][0], 'Height should be set.' ); + } + + /** + * @ticket 14110 + */ + public function test_wp_get_attachment_image_filter_change_height_width() { + add_filter( + 'wp_get_attachment_image_attributes', + static function ( $args ) { + $args['height'] = '999'; + $args['width'] = '999'; + return $args; + } + ); + $output = wp_get_attachment_image( self::$large_id ); + $this->assertStringContainsString( 'width="999"', $output, 'Width should be changed.' ); + $this->assertStringContainsString( 'height="999"', $output, 'Height should be changed.' ); + } + + /** + * @ticket 14110 + */ + public function test_wp_get_attachment_image_filter_unset_height_width() { + add_filter( + 'wp_get_attachment_image_attributes', + static function ( $args ) { + unset( $args['height'], $args['width'] ); + return $args; + } + ); + $output = wp_get_attachment_image( self::$large_id ); + $this->assertStringContainsString( 'width="150"', $output, 'Width should not be changed.' ); + $this->assertStringContainsString( 'height="150"', $output, 'Height should not be changed.' ); + } + public function filter_wp_get_attachment_image() { return 'Override wp_get_attachment_image'; } diff --git a/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php b/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php index 7a06bb006d..060a5c0912 100644 --- a/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php +++ b/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php @@ -848,6 +848,49 @@ class WP_Test_REST_Application_Passwords_Controller extends WP_Test_REST_Control } /** + * @ticket 53692 + */ + public function test_create_item_with_empty_app_id() { + wp_set_current_user( self::$admin ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/users/me/application-passwords' ); + $request->set_body_params( + array( + 'name' => 'Test', + 'app_id' => '', + ) + ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 201, $response->get_status() ); + $this->assertSame( '', $data['app_id'] ); + } + + /** + * @ticket 53692 + */ + public function test_create_item_with_uuid_app_id() { + wp_set_current_user( self::$admin ); + + $uuid = wp_generate_uuid4(); + $request = new WP_REST_Request( 'POST', '/wp/v2/users/me/application-passwords' ); + $request->set_body_params( + array( + 'name' => 'Test', + 'app_id' => $uuid, + ) + ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 201, $response->get_status() ); + $this->assertSame( $uuid, $data['app_id'] ); + } + + /** * Checks the password response matches the expected format. * * @since 5.6.0 diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 72c3c1dc8b..6626758a8a 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -10053,7 +10053,18 @@ mockedApiResponse.Schema = { "app_id": { "description": "A UUID provided by the application to uniquely identify it. It is recommended to use an UUID v5 with the URL or DNS namespace.", "type": "string", - "format": "uuid", + "oneOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ], "required": false }, "name": { @@ -10137,7 +10148,18 @@ mockedApiResponse.Schema = { "app_id": { "description": "A UUID provided by the application to uniquely identify it. It is recommended to use an UUID v5 with the URL or DNS namespace.", "type": "string", - "format": "uuid", + "oneOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ], "required": false }, "name": { |