diff --git a/src/js/media/views/button/select-mode-toggle.js b/src/js/media/views/button/select-mode-toggle.js index 2c2335bcd56f6..858c0e16cb2e4 100644 --- a/src/js/media/views/button/select-mode-toggle.js +++ b/src/js/media/views/button/select-mode-toggle.js @@ -40,7 +40,7 @@ SelectModeToggle = Button.extend(/** @lends wp.media.view.SelectModeToggle.proto render: function() { Button.prototype.render.apply( this, arguments ); - this.$el.addClass( 'select-mode-toggle-button' ); + this.$el.addClass( 'select-mode-toggle-button button-compact' ); return this; }, diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css index 63e960b482c00..f48b8048c76e5 100644 --- a/src/wp-admin/css/common.css +++ b/src/wp-admin/css/common.css @@ -1417,6 +1417,17 @@ th.action-links { padding: 0; width: 50%; } + + .wp-filter .search-form input[type="search"] { + min-height: 40px; + padding: 0 12px; + } + + .wp-filter .search-form select, + .wp-filter .filter-items select { + min-height: 40px; + padding: 0 24px 0 12px; + } } @media only screen and (max-width: 320px) { diff --git a/src/wp-admin/css/forms.css b/src/wp-admin/css/forms.css index f2e5f9dcc5b53..c17d038c5d2c6 100644 --- a/src/wp-admin/css/forms.css +++ b/src/wp-admin/css/forms.css @@ -1798,9 +1798,10 @@ table.form-table td .updated p { p.search-box input[type="search"], p.search-box input[type="text"] { min-height: 40px; + padding: 0 12px; } - p.search-box input[type="submit"] { + p.search-box input[type="submit"].button { margin-bottom: 10px; } @@ -1944,6 +1945,7 @@ table.form-table td .updated p { .options-general-php input[type="text"].small-text { max-width: 6.25em; margin: 0; + min-height: 40px; } /* Privacy Policy settings screen */ diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css index 56d88d6801ddb..4eaa682127cc0 100644 --- a/src/wp-admin/css/list-tables.css +++ b/src/wp-admin/css/list-tables.css @@ -1537,13 +1537,6 @@ div.action-links, margin: 0; /* Override existing margins */ } -/* Use compact size for space-constrained plugin cards */ -.plugin-action-buttons li .button { - min-height: 32px; - line-height: 2.30769231; /* 30px for 32px min-height */ - padding: 0 12px; -} - .plugin-card h3 { margin: 0 12px 16px 0; font-size: 18px; diff --git a/src/wp-admin/css/media.css b/src/wp-admin/css/media.css index 805868a286304..5a169cfde9e01 100644 --- a/src/wp-admin/css/media.css +++ b/src/wp-admin/css/media.css @@ -374,6 +374,10 @@ .find-box-inside { bottom: 57px; } + + #delete_all { + margin-bottom: 0; + } } @media screen and (max-width: 660px) { @@ -1396,7 +1400,6 @@ audio, video { .wp-filter p.search-box { float: none; width: 100%; - margin-bottom: 20px; display: flex; flex-wrap: nowrap; column-gap: 0; @@ -1413,6 +1416,20 @@ audio, video { top: 46px; right: 10px; } + + .media-frame.mode-grid .media-toolbar select { + min-height: 40px; + padding: 0 24px 0 12px; + } + + .media-frame.mode-grid .media-toolbar input[type="search"] { + min-height: 40px; + padding: 0 12px; + } + + .wp-filter p.search-box { + margin-bottom: 0; + } } @media only screen and (max-width: 600px) { diff --git a/src/wp-admin/css/themes.css b/src/wp-admin/css/themes.css index 4a644974c50c4..e7eadd7f6941d 100644 --- a/src/wp-admin/css/themes.css +++ b/src/wp-admin/css/themes.css @@ -119,9 +119,6 @@ body.js .theme-browser.search-loading { .theme-browser .theme .theme-actions .button { float: none; margin-left: 3px; - min-height: 32px; - line-height: 2.30769231; /* 30px for 32px min-height */ - padding: 0 12px; } .theme-browser .theme .theme-actions .button.updated-message::before, @@ -1968,6 +1965,8 @@ body.full-overlay-active { .theme-install-overlay .wp-full-overlay-header .button { float: right; margin: 7px 10px 0 0; /* Vertically center 32px button in 45px header */ + min-height: 32px; + line-height: 2.30769231; /* 30px for 32px height with 13px font */ } .theme-install-overlay .wp-full-overlay-sidebar { diff --git a/src/wp-admin/includes/class-wp-media-list-table.php b/src/wp-admin/includes/class-wp-media-list-table.php index 572dc154de639..152ca94c6800d 100644 --- a/src/wp-admin/includes/class-wp-media-list-table.php +++ b/src/wp-admin/includes/class-wp-media-list-table.php @@ -247,7 +247,7 @@ protected function extra_tablenav( $which ) { if ( $this->is_trash && $this->has_items() && current_user_can( 'edit_others_posts' ) ) { - submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false ); + submit_button( __( 'Empty Trash' ), 'apply button-compact', 'delete_all', false ); } ?> @@ -351,7 +351,7 @@ public function views() { ?> - +

diff --git a/src/wp-admin/includes/plugin-install.php b/src/wp-admin/includes/plugin-install.php index 7607b8823bb99..6b1a615a57ab8 100644 --- a/src/wp-admin/includes/plugin-install.php +++ b/src/wp-admin/includes/plugin-install.php @@ -940,7 +940,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible if ( $status['url'] ) { if ( $compatible_php && $compatible_wp && $all_plugin_dependencies_installed && ! empty( $data->download_link ) ) { $button = sprintf( - '%s', + '%s', esc_attr( $data->slug ), esc_url( $status['url'] ), /* translators: %s: Plugin name and version. */ @@ -950,7 +950,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible ); } else { $button = sprintf( - '', + '', _x( 'Install Now', 'plugin' ) ); } @@ -961,7 +961,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible if ( $status['url'] ) { if ( $compatible_php && $compatible_wp ) { $button = sprintf( - '%s', + '%s', esc_attr( $status['file'] ), esc_attr( $data->slug ), esc_url( $status['url'] ), @@ -972,7 +972,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible ); } else { $button = sprintf( - '', + '', _x( 'Update Now', 'plugin' ) ); } @@ -983,7 +983,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible case 'newer_installed': if ( is_plugin_active( $status['file'] ) ) { $button = sprintf( - '', + '', _x( 'Active', 'plugin' ) ); } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) { @@ -1008,7 +1008,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible } $button = sprintf( - '%6$s', + '%6$s', esc_url( $activate_url ), esc_attr( $name ), esc_attr( $data->slug ), @@ -1018,13 +1018,13 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible ); } else { $button = sprintf( - '', + '', is_network_admin() ? _x( 'Network Activate', 'plugin' ) : _x( 'Activate', 'plugin' ) ); } } else { $button = sprintf( - '', + '', _x( 'Installed', 'plugin' ) ); } diff --git a/src/wp-admin/theme-install.php b/src/wp-admin/theme-install.php index 5723f8e6244cc..fc24334abff85 100644 --- a/src/wp-admin/theme-install.php +++ b/src/wp-admin/theme-install.php @@ -408,21 +408,21 @@ ?> <# if ( data.activate_url ) { #> <# if ( ! data.active ) { #> - + <# } else { #> - + <# } #> <# } #> <# if ( data.customize_url ) { #> <# if ( ! data.active ) { #> <# if ( ! data.block_theme ) { #> - + <# } #> <# } else { #> - + <# } #> <# } else { #> - + <# } #> <# } else { #> <# if ( data.activate_url ) { #> - + <# } #> <# if ( data.customize_url ) { #> - + <# } else { #> - + <# } #> <# } #> <# } else { #> @@ -444,15 +444,15 @@ /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Install %s', 'theme' ), '{{ data.name }}' ); ?> - - + + <# } else { #> - - + + <# } #> <# } #> @@ -487,18 +487,18 @@ $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); ?> <# if ( ! data.active ) { #> - + <# } else { #> - + <# } #> <# } else { #> - + <# } #> <# } else { #> <# if ( data.compatible_wp && data.compatible_php ) { #> - + <# } else { #> - + <# } #> <# } #> diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php index ca9f52b2a164f..a9f24765ce742 100644 --- a/src/wp-admin/themes.php +++ b/src/wp-admin/themes.php @@ -381,18 +381,18 @@ $menu_hook = get_plugin_page_hook( $submenu[ $item[2] ][0][2], $item[2] ); if ( file_exists( WP_PLUGIN_DIR . "/{$submenu[$item[2]][0][2]}" ) || ! empty( $menu_hook ) ) { - $current_theme_actions[] = "{$item[0]}"; + $current_theme_actions[] = "{$item[0]}"; } else { - $current_theme_actions[] = "{$item[0]}"; + $current_theme_actions[] = "{$item[0]}"; } } elseif ( ! empty( $item[2] ) && current_user_can( $item[1] ) ) { $menu_file = $item[2]; if ( current_user_can( 'customize' ) ) { if ( 'custom-header' === $menu_file ) { - $current_theme_actions[] = "{$item[0]}"; + $current_theme_actions[] = "{$item[0]}"; } elseif ( 'custom-background' === $menu_file ) { - $current_theme_actions[] = "{$item[0]}"; + $current_theme_actions[] = "{$item[0]}"; } } @@ -402,9 +402,9 @@ } if ( file_exists( ABSPATH . "wp-admin/$menu_file" ) ) { - $current_theme_actions[] = "{$item[0]}"; + $current_theme_actions[] = "{$item[0]}"; } else { - $current_theme_actions[] = "{$item[0]}"; + $current_theme_actions[] = "{$item[0]}"; } } } @@ -609,7 +609,7 @@ /* translators: %s: Theme name. */ $customize_aria_label = sprintf( _x( 'Customize %s', 'theme' ), $theme['name'] ); ?> - @@ -619,7 +619,7 @@ /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -630,7 +630,7 @@ /* translators: %s: Theme name. */ $live_preview_aria_label = sprintf( _x( 'Live Preview %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -640,7 +640,7 @@ /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Cannot Activate %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -649,7 +649,7 @@ /* translators: %s: Theme name. */ $live_preview_aria_label = sprintf( _x( 'Live Preview %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1001,7 +1001,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $customize_aria_label = sprintf( _x( 'Customize %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1012,7 +1012,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1021,7 +1021,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $live_preview_aria_label = sprintf( _x( 'Live Preview %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1030,7 +1030,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Cannot Activate %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1038,7 +1038,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $live_preview_aria_label = sprintf( _x( 'Live Preview %s', 'theme' ), '{{ data.name }}' ); ?> - <# } #> @@ -1251,7 +1251,7 @@ function wp_theme_auto_update_setting_template() {
- @@ -1263,7 +1263,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $live_preview_aria_label = sprintf( _x( 'Live Preview %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1273,7 +1273,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Activate %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1283,7 +1283,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $live_preview_aria_label = sprintf( _x( 'Live Preview %s', 'theme' ), '{{ data.name }}' ); ?> - @@ -1292,7 +1292,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Cannot Activate %s', 'theme' ), '{{ data.name }}' ); ?> - <# } #> @@ -1304,7 +1304,7 @@ function wp_theme_auto_update_setting_template() { /* translators: %s: Theme name. */ $aria_label = sprintf( _x( 'Delete %s', 'theme' ), '{{ data.name }}' ); ?> - diff --git a/src/wp-includes/block-bindings.php b/src/wp-includes/block-bindings.php index 268bb6afa66bb..5a20c023149d7 100644 --- a/src/wp-includes/block-bindings.php +++ b/src/wp-includes/block-bindings.php @@ -134,6 +134,7 @@ function get_block_bindings_source( string $source_name ) { * Retrieves the list of block attributes supported by block bindings. * * @since 6.9.0 + * @since 7.1.0 Added support for the List Item block. * * @param string $block_type The block type whose supported attributes are being retrieved. * @return array The list of block attributes that are supported by block bindings. @@ -142,6 +143,7 @@ function get_block_bindings_supported_attributes( $block_type ) { $block_bindings_supported_attributes = array( 'core/paragraph' => array( 'content' ), 'core/heading' => array( 'content' ), + 'core/list-item' => array( 'content' ), 'core/image' => array( 'id', 'url', 'title', 'alt', 'caption' ), 'core/button' => array( 'url', 'text', 'linkTarget', 'rel' ), 'core/post-date' => array( 'datetime' ), diff --git a/src/wp-includes/block-supports/elements.php b/src/wp-includes/block-supports/elements.php index 54b96aa1dc064..d765a2c2b4b5a 100644 --- a/src/wp-includes/block-supports/elements.php +++ b/src/wp-includes/block-supports/elements.php @@ -12,11 +12,10 @@ * @since 6.0.0 * @access private * - * @param array $block Block object. * @return string The unique class name. */ -function wp_get_elements_class_name( $block ) { - return 'wp-elements-' . md5( serialize( $block ) ); +function wp_get_elements_class_name(): string { + return wp_unique_prefixed_id( 'wp-elements-' ); } /** @@ -109,6 +108,29 @@ function wp_should_add_elements_class_name( $block, $options ) { * * @param array $parsed_block The parsed block. * @return array The same parsed block with elements classname added if appropriate. + * + * @phpstan-param array{ + * blockName: string, + * attrs: array{ + * className?: string, + * style?: array{ + * elements?: array, + * ... + * }>, + * }, + * ... + * }, + * ... + * } $parsed_block + * @phpstan-return array{ + * blockName: string, + * attrs: array{ + * className?: string, + * ... + * }, + * ... + * } */ function wp_render_elements_support_styles( $parsed_block ) { /* @@ -129,9 +151,12 @@ function wp_render_elements_support_styles( $parsed_block ) { ); } - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] ); - $element_block_styles = $parsed_block['attrs']['style']['elements'] ?? null; + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] ); + if ( ! $block_type ) { + return $parsed_block; + } + $element_block_styles = $parsed_block['attrs']['style']['elements'] ?? null; if ( ! $element_block_styles ) { return $parsed_block; } @@ -157,7 +182,7 @@ function wp_render_elements_support_styles( $parsed_block ) { return $parsed_block; } - $class_name = wp_get_elements_class_name( $parsed_block ); + $class_name = wp_get_elements_class_name(); $updated_class_name = isset( $parsed_block['attrs']['className'] ) ? $parsed_block['attrs']['className'] . " $class_name" : $class_name; _wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name ); @@ -197,7 +222,7 @@ function wp_render_elements_support_styles( $parsed_block ) { ) ); - if ( isset( $element_style_object[':hover'] ) ) { + if ( isset( $element_style_object[':hover'], $element_config['hover_selector'] ) ) { wp_style_engine_get_styles( $element_style_object[':hover'], array( diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index f82739fc5d91d..b096610c1b04f 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -115,6 +115,34 @@ protected function register( $icon_name, $icon_properties ) { return false; } + if ( preg_match( '/[A-Z]/', $icon_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Icon names must not contain uppercase characters.' ), + '7.1.0' + ); + return false; + } + + $name_matcher = '/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/'; + if ( ! preg_match( $name_matcher, $icon_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Icon names must contain a namespace prefix. Example: my-plugin/my-custom-icon' ), + '7.1.0' + ); + return false; + } + + if ( $this->is_registered( $icon_name ) ) { + _doing_it_wrong( + __METHOD__, + __( 'Icon is already registered.' ), + '7.1.0' + ); + return false; + } + $allowed_keys = array_fill_keys( array( 'label', 'content', 'filePath' ), 1 ); foreach ( array_keys( $icon_properties ) as $key ) { if ( ! array_key_exists( $key, $allowed_keys ) ) { diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index d89d366d71baf..3d93b5bd8a2c1 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -144,11 +144,11 @@ function_exists( 'imagecreatefromavif' ) && ( 'image/avif' === wp_get_image_mime * * @since 3.5.0 * - * @param int $width - * @param int $height + * @param int|null $width Image width. + * @param int|null $height Image height. * @return true */ - protected function update_size( $width = false, $height = false ) { + protected function update_size( $width = null, $height = null ) { if ( ! $width ) { $width = imagesx( $this->image ); } diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 19b27ba12e2ae..2cb3a694c567b 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -247,12 +247,13 @@ public function set_quality( $quality = null, $dims = array() ) { * * @since 3.5.0 * - * @param int $width - * @param int $height + * @param int|null $width Image width. + * @param int|null $height Image height. * @return true|WP_Error */ protected function update_size( $width = null, $height = null ) { $size = null; + if ( ! $width || ! $height ) { try { $size = $this->image->getImageGeometry(); diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index d7fe151a0d94a..8401117836ebc 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -201,8 +201,8 @@ public function get_size() { * * @since 3.5.0 * - * @param int $width - * @param int $height + * @param int|null $width Image width. + * @param int|null $height Image height. * @return true */ protected function update_size( $width = null, $height = null ) { diff --git a/src/wp-includes/compat-utf8.php b/src/wp-includes/compat-utf8.php index e1cab36ea3244..5fa8cde158789 100644 --- a/src/wp-includes/compat-utf8.php +++ b/src/wp-includes/compat-utf8.php @@ -65,7 +65,7 @@ function _wp_scan_utf8( string $bytes, int &$at, int &$invalid_length, ?int $max "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" . " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f", $i, - $end - $i + min( $end - $i, $max_count - $count ) ); if ( $count + $ascii_byte_count >= $max_count ) { diff --git a/src/wp-includes/css/wp-embed-template.css b/src/wp-includes/css/wp-embed-template.css index 6118ab4d2b6d1..9bf606589a773 100644 --- a/src/wp-includes/css/wp-embed-template.css +++ b/src/wp-includes/css/wp-embed-template.css @@ -217,7 +217,7 @@ p.wp-embed-heading { .wp-embed-share-dialog-open:focus .dashicons, .wp-embed-share-dialog-close:focus .dashicons { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9); + box-shadow: 0 0 0 1.5px #3858e9; /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; border-radius: 100%; diff --git a/src/wp-includes/embed.php b/src/wp-includes/embed.php index 636f6acd94e6b..10558caed6f8c 100644 --- a/src/wp-includes/embed.php +++ b/src/wp-includes/embed.php @@ -1233,7 +1233,7 @@ function print_embed_sharing_dialog() { * @since 4.5.0 */ function the_embed_site_title(): void { - $fallback_icon_url = includes_url( 'images/w-logo-blue.png' ); + $fallback_icon_url = includes_url( 'images/w-logo-gray-white-bg.svg' ); $site_icon_url = get_site_icon_url( 32, $fallback_icon_url ); $icon_img = ''; diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index c297864859aa4..d7d2ff3fed89a 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -1752,7 +1752,7 @@ function do_favicon() { */ do_action( 'do_faviconico' ); - wp_redirect( get_site_icon_url( 32, includes_url( 'images/w-logo-blue-white-bg.png' ) ) ); + wp_redirect( get_site_icon_url( 32, includes_url( 'images/w-logo-gray-white-bg.png' ) ) ); exit; } diff --git a/src/wp-includes/images/w-logo-gray-white-bg.png b/src/wp-includes/images/w-logo-gray-white-bg.png new file mode 100644 index 0000000000000..0adbfad427550 Binary files /dev/null and b/src/wp-includes/images/w-logo-gray-white-bg.png differ diff --git a/src/wp-includes/images/w-logo-gray-white-bg.svg b/src/wp-includes/images/w-logo-gray-white-bg.svg new file mode 100644 index 0000000000000..6cb698ca9a607 --- /dev/null +++ b/src/wp-includes/images/w-logo-gray-white-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/phpunit/tests/block-bindings/render.php b/tests/phpunit/tests/block-bindings/render.php index 77b0975105dc5..172bdff71315d 100644 --- a/tests/phpunit/tests/block-bindings/render.php +++ b/tests/phpunit/tests/block-bindings/render.php @@ -122,6 +122,16 @@ public function data_update_block_with_value_from_source() { , '

test source value

', ), + 'list item block' => array( + 'content', + << +
  • This should not appear
  • + +HTML + , + '
  • test source value
  • ', + ), ); } diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php index 007ba8312e495..ad60844813188 100644 --- a/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php +++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupport.php @@ -159,6 +159,70 @@ public function test_elements_block_support_class_with_invalid_elements_prefix() ); } + /** + * Tests that duplicate blocks get distinct elements class names + * on their rendered markup to avoid CSS cascade conflicts. + * + * @ticket 65435 + * + * @covers ::wp_get_elements_class_name + */ + public function test_elements_block_support_class_with_duplicate_blocks(): void { + $this->test_block_name = 'test/element-block-supports'; + + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( + 'style' => array( + 'type' => 'object', + ), + ), + 'supports' => array( + 'color' => array( + 'link' => true, + ), + ), + ) + ); + + $block = array( + 'blockName' => $this->test_block_name, + 'attrs' => array( + 'style' => array( + 'elements' => array( + 'link' => array( + 'color' => array( + 'text' => 'var:preset|color|vivid-red', + ), + ), + ), + ), + ), + ); + + $block_markup = '

    Hello WordPress!

    '; + $elements_class_names = array(); + $count = 2; + for ( $i = 0; $i < $count; $i++ ) { + $rendered_block = wp_render_elements_class_name( $block_markup, wp_render_elements_support_styles( $block ) ); + + $processor = new WP_HTML_Tag_Processor( $rendered_block ); + $this->assertTrue( $processor->next_tag( 'P' ), "Expected paragraph in block #$i." ); + $elements_class_name = array_first( + array_filter( + iterator_to_array( $processor->class_list() ), + fn( string $class_name ) => (bool) preg_match( '/^wp-elements-\d+$/', $class_name ) + ) + ); + $this->assertIsString( $elements_class_name, "Expected wp-elements class in block #$i." ); + $elements_class_names[] = $elements_class_name; + } + + $this->assertSame( $count, count( array_unique( $elements_class_names ) ), 'Expected each rendered block to have a unique wp-elements class name.' ); + } + /** * Data provider. * @@ -238,7 +302,7 @@ public function data_elements_block_support_class() { 'button' => array( 'color' => $color_styles ), ), 'block_markup' => '

    Hello WordPress!

    ', - 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', ), 'link element styles apply class to wrapper' => array( 'color_settings' => array( 'link' => true ), @@ -246,7 +310,7 @@ public function data_elements_block_support_class() { 'link' => array( 'color' => $color_styles ), ), 'block_markup' => '

    Hello WordPress!

    ', - 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', ), 'heading element styles apply class to wrapper' => array( 'color_settings' => array( 'heading' => true ), @@ -254,7 +318,7 @@ public function data_elements_block_support_class() { 'heading' => array( 'color' => $color_styles ), ), 'block_markup' => '

    Hello WordPress!

    ', - 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', ), 'element styles apply class to wrapper when it has other classes' => array( 'color_settings' => array( 'link' => true ), @@ -262,7 +326,7 @@ public function data_elements_block_support_class() { 'link' => array( 'color' => $color_styles ), ), 'block_markup' => '

    Hello WordPress!

    ', - 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', ), 'element styles apply class to wrapper when it has other attributes' => array( 'color_settings' => array( 'link' => true ), @@ -270,7 +334,7 @@ public function data_elements_block_support_class() { 'link' => array( 'color' => $color_styles ), ), 'block_markup' => '

    Hello WordPress!

    ', - 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', + 'expected_markup' => '/^

    Hello WordPress<\/a>!<\/p>$/', ), ); } diff --git a/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php b/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php index 16ed26fc9c7bc..5c9fc8af5819d 100644 --- a/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php +++ b/tests/phpunit/tests/block-supports/wpRenderElementsSupportStyles.php @@ -68,6 +68,62 @@ public function test_elements_block_support_styles( $color_settings, $elements_s ); } + /** + * Tests that identical blocks with different elements styles + * generate distinct class names to avoid CSS cascade conflicts. + * + * @ticket 65435 + * + * @covers ::wp_get_elements_class_name + */ + public function test_elements_block_support_styles_with_duplicate_blocks(): void { + $this->test_block_name = 'test/element-block-supports'; + + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( + 'style' => array( + 'type' => 'object', + ), + ), + 'supports' => array( + 'color' => array( + 'link' => true, + ), + ), + ) + ); + + $block = array( + 'blockName' => $this->test_block_name, + 'attrs' => array( + 'style' => array( + 'elements' => array( + 'link' => array( + 'color' => array( + 'text' => 'blue', + ), + ), + ), + ), + ), + ); + + // Process two identical blocks with the same elements styles. + $count = 2; + for ( $i = 0; $i < $count; $i++ ) { + wp_render_elements_support_styles( $block ); + } + $actual_stylesheet = wp_style_engine_get_stylesheet_from_context( 'block-supports', array( 'prettify' => false ) ); + + // Count the number of distinct class names to confirm uniqueness. + $this->assertSame( $count, preg_match_all( '/\.wp-elements-(\d+)/', $actual_stylesheet, $matches ) ); + $unique_classes = array_unique( $matches[1] ); + $this->assertCount( $count, $unique_classes, 'Both blocks should produce distinct class names' ); + } + /** * Data provider. * @@ -127,7 +183,7 @@ public function data_elements_block_support_styles() { 'elements_styles' => array( 'button' => array( 'color' => $color_styles ), ), - 'expected_styles' => '/^.wp-elements-[a-f0-9]{32} .wp-element-button, .wp-elements-[a-f0-9]{32} .wp-block-button__link' . $color_css_rules . '$/', + 'expected_styles' => '/^.wp-elements-\d+ .wp-element-button, .wp-elements-\d+ .wp-block-button__link' . $color_css_rules . '$/', ), 'link element styles are applied' => array( 'color_settings' => array( 'link' => true ), @@ -139,15 +195,15 @@ public function data_elements_block_support_styles() { ), ), ), - 'expected_styles' => '/^.wp-elements-[a-f0-9]{32} a:where\(:not\(.wp-element-button\)\)' . $color_css_rules . - '.wp-elements-[a-f0-9]{32} a:where\(:not\(.wp-element-button\)\):hover' . $color_css_rules . '$/', + 'expected_styles' => '/^.wp-elements-\d+ a:where\(:not\(.wp-element-button\)\)' . $color_css_rules . + '.wp-elements-\d+ a:where\(:not\(.wp-element-button\)\):hover' . $color_css_rules . '$/', ), 'generic heading element styles are applied' => array( 'color_settings' => array( 'heading' => true ), 'elements_styles' => array( 'heading' => array( 'color' => $color_styles ), ), - 'expected_styles' => '/^.wp-elements-[a-f0-9]{32} h1, .wp-elements-[a-f0-9]{32} h2, .wp-elements-[a-f0-9]{32} h3, .wp-elements-[a-f0-9]{32} h4, .wp-elements-[a-f0-9]{32} h5, .wp-elements-[a-f0-9]{32} h6' . $color_css_rules . '$/', + 'expected_styles' => '/^.wp-elements-\d+ h1, .wp-elements-\d+ h2, .wp-elements-\d+ h3, .wp-elements-\d+ h4, .wp-elements-\d+ h5, .wp-elements-\d+ h6' . $color_css_rules . '$/', ), 'individual heading element styles are applied' => array( 'color_settings' => array( 'heading' => true ), @@ -159,12 +215,12 @@ public function data_elements_block_support_styles() { 'h5' => array( 'color' => $color_styles ), 'h6' => array( 'color' => $color_styles ), ), - 'expected_styles' => '/^.wp-elements-[a-f0-9]{32} h1' . $color_css_rules . - '.wp-elements-[a-f0-9]{32} h2' . $color_css_rules . - '.wp-elements-[a-f0-9]{32} h3' . $color_css_rules . - '.wp-elements-[a-f0-9]{32} h4' . $color_css_rules . - '.wp-elements-[a-f0-9]{32} h5' . $color_css_rules . - '.wp-elements-[a-f0-9]{32} h6' . $color_css_rules . '$/', + 'expected_styles' => '/^.wp-elements-\d+ h1' . $color_css_rules . + '.wp-elements-\d+ h2' . $color_css_rules . + '.wp-elements-\d+ h3' . $color_css_rules . + '.wp-elements-\d+ h4' . $color_css_rules . + '.wp-elements-\d+ h5' . $color_css_rules . + '.wp-elements-\d+ h6' . $color_css_rules . '$/', ), ); } diff --git a/tests/phpunit/tests/compat/wpUtf8CodePointSpan.php b/tests/phpunit/tests/compat/wpUtf8CodePointSpan.php new file mode 100644 index 0000000000000..da66095ce79af --- /dev/null +++ b/tests/phpunit/tests/compat/wpUtf8CodePointSpan.php @@ -0,0 +1,100 @@ +assertSame( + $expected_span, + _wp_utf8_codepoint_span( $text, $byte_offset, $max_code_points, $found_code_points ), + 'Should have found the expected byte span.' + ); + + $this->assertSame( + $expected_found, + $found_code_points, + 'Should have reported the expected number of code points.' + ); + } + + /** + * Data provider. + * + * @return array + */ + public static function data_codepoint_spans() { + $long_ascii_run = str_repeat( 'a', 1024 ); + + return array( + 'zero code point budget' => array( + 'abcdef', + 0, + 0, + 0, + 0, + ), + 'long ASCII run at start' => array( + $long_ascii_run, + 0, + 5, + 5, + 5, + ), + 'long ASCII run from non-zero offset' => array( + "zz{$long_ascii_run}", + 2, + 5, + 5, + 5, + ), + 'multibyte character before the boundary' => array( + "ab\u{1F170}cd", + 0, + 2, + 2, + 2, + ), + 'multibyte character at the boundary' => array( + "ab\u{1F170}cd", + 0, + 3, + strlen( "ab\u{1F170}" ), + 3, + ), + 'invalid span after the boundary' => array( + "ab\xF0\x9Fzz", + 0, + 2, + 2, + 2, + ), + 'invalid span at the boundary' => array( + "ab\xF0\x9Fzz", + 0, + 3, + 4, + 3, + ), + ); + } +} diff --git a/tests/phpunit/tests/general/template.php b/tests/phpunit/tests/general/template.php index 20f6d0012b3a7..00b683971a6a0 100644 --- a/tests/phpunit/tests/general/template.php +++ b/tests/phpunit/tests/general/template.php @@ -860,7 +860,7 @@ public function test_the_embed_site_title_uses_fallback_when_attachment_url_fail add_filter( 'wp_get_attachment_image_src', '__return_false' ); $output = get_echo( 'the_embed_site_title' ); - $fallback = includes_url( 'images/w-logo-blue.png' ); + $fallback = includes_url( 'images/w-logo-gray-white-bg.svg' ); $processor = new WP_HTML_Tag_Processor( $output ); $this->assertTrue( $processor->next_tag( 'IMG' ), 'Expected IMG tag with fallback.' ); @@ -914,7 +914,7 @@ public function test_the_embed_site_title_omits_srcset_when_1x_and_2x_urls_are_i */ public function test_the_embed_site_title_uses_fallback_without_srcset_when_no_site_icon_set(): void { $output = get_echo( 'the_embed_site_title' ); - $fallback = includes_url( 'images/w-logo-blue.png' ); + $fallback = includes_url( 'images/w-logo-gray-white-bg.svg' ); $processor = new WP_HTML_Tag_Processor( $output ); diff --git a/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php b/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php index fd73ddc43a4ba..185d93b7a652c 100644 --- a/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php +++ b/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php @@ -890,7 +890,8 @@ public function test_attribute_ops_on_tag_closer_do_not_change_the_markup() { * //

    * ``` * - * To prevent it, `set_attribute` escapes dangerous characters (`"`, `'`, `<`, `>`, `&`) using HTML character references. + * To prevent it, `set_attribute` escapes HTML syntax characters like `"` using + * HTML character references. * * ```php *
    diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php new file mode 100644 index 0000000000000..fba2eacde43f5 --- /dev/null +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -0,0 +1,110 @@ +registry = WP_Icons_Registry::get_instance(); + } + + public function tear_down() { + $instance_property = new ReflectionProperty( WP_Icons_Registry::class, 'instance' ); + + if ( PHP_VERSION_ID < 80100 ) { + $instance_property->setAccessible( true ); + } + + $instance_property->setValue( null, null ); + + $this->registry = null; + parent::tear_down(); + } + + /** + * Invokes WP_Icons_Registry::register despite it being private + * + * @param string $icon_name Icon name including namespace. + * @param array $icon_properties Icon properties (label, content, filePath). + * @return bool True if the icon was registered successfully. + */ + private function register( $icon_name, $icon_properties ) { + $method = new ReflectionMethod( $this->registry, 'register' ); + + if ( PHP_VERSION_ID < 80100 ) { + $method->setAccessible( true ); + } + + return $method->invoke( $this->registry, $icon_name, $icon_properties ); + } + + /** + * Provides invalid icon names. + * + * @return array[] + */ + public function data_invalid_icon_names() { + return array( + 'non-string name' => array( 1 ), + 'no namespace' => array( 'plus' ), + 'uppercase characters' => array( 'Test/Plus' ), + 'invalid characters' => array( 'test/_doing_it_wrong' ), + ); + } + + /** + * Should fail to re-register the same icon. + * + * @ticket 64847 + * + * @expectedIncorrectUsage WP_Icons_Registry::register + */ + public function test_register_icon_twice() { + $name = 'test-plugin/duplicate'; + $settings = array( + 'label' => 'Icon', + 'content' => '', + ); + + $result = $this->register( $name, $settings ); + $this->assertTrue( $result ); + $result2 = $this->register( $name, $settings ); + $this->assertFalse( $result2 ); + } + + + /** + * Should fail to register icon with invalid names. + * + * @ticket 64847 + * + * @dataProvider data_invalid_icon_names + * @expectedIncorrectUsage WP_Icons_Registry::register + * + * @param mixed $icon_name Icon name to register. + */ + public function test_register_invalid_name( $icon_name ) { + $settings = array( + 'label' => 'Icon', + 'content' => '', + ); + + $result = $this->register( $icon_name, $settings ); + $this->assertFalse( $result ); + } +}