diff --git a/src/wp-admin/includes/misc.php b/src/wp-admin/includes/misc.php index 3c55f3f59a..8c4caa468d 100644 --- a/src/wp-admin/includes/misc.php +++ b/src/wp-admin/includes/misc.php @@ -1658,6 +1658,7 @@ function wp_check_php_version() { * @type bool $dismissible Optional. Whether the admin notice is dismissible. Default false. * @type string $id Optional. The value of the admin notice's ID attribute. Default empty string. * @type string[] $additional_classes Optional. A string array of class names. Default empty array. + * @type string[] $attributes Optional. Additional attributes for the notice div. Default empty array. * @type bool $paragraph_wrap Optional. Whether to wrap the message in paragraph tags. Default true. * } * @return string The markup for an admin notice. @@ -1668,6 +1669,7 @@ function wp_get_admin_notice( $message, $args = array() ) { 'dismissible' => false, 'id' => '', 'additional_classes' => array(), + 'attributes' => array(), 'paragraph_wrap' => true, ); @@ -1681,9 +1683,10 @@ function wp_get_admin_notice( $message, $args = array() ) { * @param array $args The arguments for the admin notice. * @param string $message The message for the admin notice. */ - $args = apply_filters( 'wp_admin_notice_args', $args, $message ); - $id = ''; - $classes = 'notice'; + $args = apply_filters( 'wp_admin_notice_args', $args, $message ); + $id = ''; + $classes = 'notice'; + $attributes = ''; if ( is_string( $args['id'] ) ) { $trimmed_id = trim( $args['id'] ); @@ -1721,11 +1724,24 @@ function wp_get_admin_notice( $message, $args = array() ) { $classes .= ' ' . implode( ' ', $args['additional_classes'] ); } + if ( is_array( $args['attributes'] ) && ! empty( $args['attributes'] ) ) { + $attributes = ''; + foreach ( $args['attributes'] as $attr => $val ) { + if ( is_bool( $val ) ) { + $attributes .= $val ? ' ' . $attr : ''; + } elseif ( is_int( $attr ) ) { + $attributes .= ' ' . esc_attr( trim( $val ) ); + } elseif ( $val ) { + $attributes .= ' ' . $attr . '="' . esc_attr( trim( $val ) ) . '"'; + } + } + } + if ( false !== $args['paragraph_wrap'] ) { $message = "

$message

"; } - $markup = sprintf( '
%3$s
', $id, $classes, $message ); + $markup = sprintf( '
%4$s
', $id, $classes, $attributes, $message ); /** * Filters the markup for an admin notice. diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index f6bd75741d..a707c62de2 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -2645,12 +2645,14 @@ function _wp_add_global_attributes( $value ) { 'aria-describedby' => true, 'aria-details' => true, 'aria-expanded' => true, + 'aria-hidden' => true, 'aria-label' => true, 'aria-labelledby' => true, - 'aria-hidden' => true, + 'aria-live' => true, 'class' => true, 'data-*' => true, 'dir' => true, + 'hidden' => true, 'id' => true, 'lang' => true, 'style' => true, diff --git a/tests/phpunit/tests/admin/wpAdminNotice.php b/tests/phpunit/tests/admin/wpAdminNotice.php index 2feca7de7c..d882c93457 100644 --- a/tests/phpunit/tests/admin/wpAdminNotice.php +++ b/tests/phpunit/tests/admin/wpAdminNotice.php @@ -212,6 +212,71 @@ class Tests_Admin_WpAdminNotice extends WP_UnitTestCase { ), 'expected' => '

A notice with additional classes that are not an array.

', ), + 'additional attribute with a value' => array( + 'message' => 'A notice with an additional attribute with a value.', + 'args' => array( + 'attributes' => array( 'aria-live' => 'assertive' ), + ), + 'expected' => '

A notice with an additional attribute with a value.

', + ), + 'additional hidden attribute' => array( + 'message' => 'A notice with the hidden attribute.', + 'args' => array( + 'attributes' => array( 'hidden' => true ), + ), + 'expected' => '', + ), + 'additional attribute no associative keys' => array( + 'message' => 'A notice with a boolean attribute without an associative key.', + 'args' => array( + 'attributes' => array( 'hidden' ), + ), + 'expected' => '', + ), + 'additional attribute with role' => array( + 'message' => 'A notice with an additional attribute role.', + 'args' => array( + 'attributes' => array( 'role' => 'alert' ), + ), + 'expected' => '', + ), + 'multiple additional attributes' => array( + 'message' => 'A notice with multiple additional attributes.', + 'args' => array( + 'attributes' => array( + 'role' => 'alert', + 'data-test' => -1, + ), + ), + 'expected' => '', + ), + 'data attribute with unsafe value' => array( + 'message' => 'A notice with an additional attribute with an unsafe value.', + 'args' => array( + 'attributes' => array( 'data-unsafe' => '' ), + ), + 'expected' => '

A notice with an additional attribute with an unsafe value.

', + ), + 'additional invalid attribute' => array( + 'message' => 'A notice with an additional attribute that is invalid.', + 'args' => array( + 'attributes' => array( 'not-valid' => 'not-valid' ), + ), + 'expected' => '

A notice with an additional attribute that is invalid.

', + ), + 'multiple attributes with "role", invalid, data-*, numeric, and boolean' => array( + 'message' => 'A notice with multiple attributes with "role", invalid, "data-*", numeric, and boolean.', + 'args' => array( + 'attributes' => array( + 'role' => 'alert', + 'disabled' => 'disabled', + 'data-name' => 'my-name', + 'data-id' => 1, + 'hidden', + ), + ), + 'expected' => '', + ), 'paragraph wrapping as a falsy value rather than (bool) false' => array( 'message' => 'A notice with paragraph wrapping as a falsy value rather than (bool) false.', 'args' => array( diff --git a/tests/phpunit/tests/admin/wpGetAdminNotice.php b/tests/phpunit/tests/admin/wpGetAdminNotice.php index b5ba311338..718fdf3d94 100644 --- a/tests/phpunit/tests/admin/wpGetAdminNotice.php +++ b/tests/phpunit/tests/admin/wpGetAdminNotice.php @@ -208,6 +208,64 @@ class Tests_Admin_WpGetAdminNotice extends WP_UnitTestCase { ), 'expected' => '

A notice with additional classes that are not an array.

', ), + 'additional attribute with a value' => array( + 'message' => 'A notice with an additional attribute with a value.', + 'args' => array( + 'attributes' => array( 'aria-live' => 'assertive' ), + ), + 'expected' => '

A notice with an additional attribute with a value.

', + ), + 'additional hidden attribute' => array( + 'message' => 'A notice with the hidden attribute.', + 'args' => array( + 'attributes' => array( 'hidden' => true ), + ), + 'expected' => '', + ), + 'additional attribute no associative keys' => array( + 'message' => 'A notice with a boolean attribute without an associative key.', + 'args' => array( + 'attributes' => array( 'hidden' ), + ), + 'expected' => '', + ), + 'additional attribute with role' => array( + 'message' => 'A notice with an additional attribute role.', + 'args' => array( + 'attributes' => array( 'role' => 'alert' ), + ), + 'expected' => '', + ), + 'multiple additional attributes' => array( + 'message' => 'A notice with multiple additional attributes.', + 'args' => array( + 'attributes' => array( + 'role' => 'alert', + 'data-test' => -1, + ), + ), + 'expected' => '', + ), + 'data attribute with unsafe value' => array( + 'message' => 'A notice with an additional attribute with an unsafe value.', + 'args' => array( + 'attributes' => array( 'data-unsafe' => '' ), + ), + 'expected' => '

A notice with an additional attribute with an unsafe value.

', + ), + 'multiple attributes with "role", invalid, data-*, numeric, and boolean' => array( + 'message' => 'A notice with multiple attributes with "role", invalid, "data-*", numeric, and boolean.', + 'args' => array( + 'attributes' => array( + 'role' => 'alert', + 'disabled' => 'disabled', + 'data-name' => 'my-name', + 'data-id' => 1, + 'hidden', + ), + ), + 'expected' => '', + ), 'paragraph wrapping as a falsy value rather than (bool) false' => array( 'message' => 'A notice with paragraph wrapping as a falsy value rather than (bool) false.', 'args' => array(