Make WordPress Core

Changeset 56654


Ignore:
Timestamp:
09/21/2023 06:22:10 PM (10 months ago)
Author:
joedolson
Message:

Login and Registration: Improve HTML for errors and notices.

Improve markup on Login and Registration errors. Use list markup for multiple issues, paragraph when only one to reduce semantic burden in the most common case. Normalize classes and markup for wrapper using wp_admin_notice() and wp_get_admin_notice() functions. Move definition of those functions from wp-admin\includes\misc.php to wp-includes\functions.php. Move tests to functions group.

Props extendwings, sabernhardt, afercia, lukecavanagh, rianrietveld, oglekler, sergeybiryukov, costdev, joedolson.
Fixes #30685.

Location:
trunk
Files:
4 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/css/login.css

    r55582 r56654  
    4343
    4444.login .message,
    45 .login .success,
    46 .login #login_error {
     45.login .notice,
     46.login .success {
    4747    border-left: 4px solid #72aee6;
    4848    padding: 12px;
     
    5858}
    5959
    60 .login #login_error {
     60/* Match border color from common.css */
     61.login .notice-error {
    6162    border-left-color: #d63638;
     63}
     64
     65.login .login-error-list {
     66    list-style: none;
     67}
     68
     69.login .login-error-list li + li {
     70    margin-top: 4px;
    6271}
    6372
     
    238247}
    239248
     249#login form .indicator-hint,
     250#login #reg_passmail {
     251    margin-bottom: 16px;
     252}
     253
    240254#login form p.submit {
    241255    margin: 0;
     
    343357}
    344358
    345 .js.login input.password-input,
    346 .js.login-action-rp form .input,
    347 .js.login-action-rp input[type="text"] {
     359.js.login input.password-input {
    348360    padding-right: 2.5rem;
    349361}
     
    355367}
    356368
     369.js.login-action-resetpass input[type="text"],
     370.js.login-action-resetpass input[type="password"],
    357371.js.login-action-rp input[type="text"],
    358372.js.login-action-rp input[type="password"] {
  • trunk/src/wp-admin/includes/misc.php

    r56603 r56654  
    16431643    return $response;
    16441644}
    1645 
    1646 /**
    1647  * Creates and returns the markup for an admin notice.
    1648  *
    1649  * @since 6.4.0
    1650  *
    1651  * @param string $message The message.
    1652  * @param array  $args {
    1653  *     Optional. An array of arguments for the admin notice. Default empty array.
    1654  *
    1655  *     @type string   $type               Optional. The type of admin notice.
    1656  *                                        For example, 'error', 'success', 'warning', 'info'.
    1657  *                                        Default empty string.
    1658  *     @type bool     $dismissible        Optional. Whether the admin notice is dismissible. Default false.
    1659  *     @type string   $id                 Optional. The value of the admin notice's ID attribute. Default empty string.
    1660  *     @type string[] $additional_classes Optional. A string array of class names. Default empty array.
    1661  *     @type string[] $attributes         Optional. Additional attributes for the notice div. Default empty array.
    1662  *     @type bool     $paragraph_wrap     Optional. Whether to wrap the message in paragraph tags. Default true.
    1663  * }
    1664  * @return string The markup for an admin notice.
    1665  */
    1666 function wp_get_admin_notice( $message, $args = array() ) {
    1667     $defaults = array(
    1668         'type'               => '',
    1669         'dismissible'        => false,
    1670         'id'                 => '',
    1671         'additional_classes' => array(),
    1672         'attributes'         => array(),
    1673         'paragraph_wrap'     => true,
    1674     );
    1675 
    1676     $args = wp_parse_args( $args, $defaults );
    1677 
    1678     /**
    1679      * Filters the arguments for an admin notice.
    1680      *
    1681      * @since 6.4.0
    1682      *
    1683      * @param array  $args    The arguments for the admin notice.
    1684      * @param string $message The message for the admin notice.
    1685      */
    1686     $args       = apply_filters( 'wp_admin_notice_args', $args, $message );
    1687     $id         = '';
    1688     $classes    = 'notice';
    1689     $attributes = '';
    1690 
    1691     if ( is_string( $args['id'] ) ) {
    1692         $trimmed_id = trim( $args['id'] );
    1693 
    1694         if ( '' !== $trimmed_id ) {
    1695             $id = 'id="' . $trimmed_id . '" ';
    1696         }
    1697     }
    1698 
    1699     if ( is_string( $args['type'] ) ) {
    1700         $type = trim( $args['type'] );
    1701 
    1702         if ( str_contains( $type, ' ' ) ) {
    1703             _doing_it_wrong(
    1704                 __FUNCTION__,
    1705                 sprintf(
    1706                     /* translators: %s: The "type" key. */
    1707                     __( 'The %s key must be a string without spaces.' ),
    1708                     '<code>type</code>'
    1709                 ),
    1710                 '6.4.0'
    1711             );
    1712         }
    1713 
    1714         if ( '' !== $type ) {
    1715             $classes .= ' notice-' . $type;
    1716         }
    1717     }
    1718 
    1719     if ( true === $args['dismissible'] ) {
    1720         $classes .= ' is-dismissible';
    1721     }
    1722 
    1723     if ( is_array( $args['additional_classes'] ) && ! empty( $args['additional_classes'] ) ) {
    1724         $classes .= ' ' . implode( ' ', $args['additional_classes'] );
    1725     }
    1726 
    1727     if ( is_array( $args['attributes'] ) && ! empty( $args['attributes'] ) ) {
    1728         $attributes = '';
    1729         foreach ( $args['attributes'] as $attr => $val ) {
    1730             if ( is_bool( $val ) ) {
    1731                 $attributes .= $val ? ' ' . $attr : '';
    1732             } elseif ( is_int( $attr ) ) {
    1733                 $attributes .= ' ' . esc_attr( trim( $val ) );
    1734             } elseif ( $val ) {
    1735                 $attributes .= ' ' . $attr . '="' . esc_attr( trim( $val ) ) . '"';
    1736             }
    1737         }
    1738     }
    1739 
    1740     if ( false !== $args['paragraph_wrap'] ) {
    1741         $message = "<p>$message</p>";
    1742     }
    1743 
    1744     $markup = sprintf( '<div %1$sclass="%2$s"%3$s>%4$s</div>', $id, $classes, $attributes, $message );
    1745 
    1746     /**
    1747      * Filters the markup for an admin notice.
    1748      *
    1749      * @since 6.4.0
    1750      *
    1751      * @param string $markup  The HTML markup for the admin notice.
    1752      * @param string $message The message for the admin notice.
    1753      * @param array  $args    The arguments for the admin notice.
    1754      */
    1755     return apply_filters( 'wp_admin_notice_markup', $markup, $message, $args );
    1756 }
    1757 
    1758 /**
    1759  * Outputs an admin notice.
    1760  *
    1761  * @since 6.4.0
    1762  *
    1763  * @param string $message The message to output.
    1764  * @param array  $args {
    1765  *     Optional. An array of arguments for the admin notice. Default empty array.
    1766  *
    1767  *     @type string   $type               Optional. The type of admin notice.
    1768  *                                        For example, 'error', 'success', 'warning', 'info'.
    1769  *                                        Default empty string.
    1770  *     @type bool     $dismissible        Optional. Whether the admin notice is dismissible. Default false.
    1771  *     @type string   $id                 Optional. The value of the admin notice's ID attribute. Default empty string.
    1772  *     @type string[] $additional_classes Optional. A string array of class names. Default empty array.
    1773  *     @type bool     $paragraph_wrap     Optional. Whether to wrap the message in paragraph tags. Default true.
    1774  * }
    1775  */
    1776 function wp_admin_notice( $message, $args = array() ) {
    1777     /**
    1778      * Fires before an admin notice is output.
    1779      *
    1780      * @since 6.4.0
    1781      *
    1782      * @param string $message The message for the admin notice.
    1783      * @param array  $args    The arguments for the admin notice.
    1784      */
    1785     do_action( 'wp_admin_notice', $message, $args );
    1786 
    1787     echo wp_kses_post( wp_get_admin_notice( $message, $args ) );
    1788 }
  • trunk/src/wp-includes/functions.php

    r56570 r56654  
    87638763    return abs( (float) $expected - (float) $actual ) <= $precision;
    87648764}
     8765
     8766/**
     8767 * Creates and returns the markup for an admin notice.
     8768 *
     8769 * @since 6.4.0
     8770 *
     8771 * @param string $message The message.
     8772 * @param array  $args {
     8773 *     Optional. An array of arguments for the admin notice. Default empty array.
     8774 *
     8775 *     @type string   $type               Optional. The type of admin notice.
     8776 *                                        For example, 'error', 'success', 'warning', 'info'.
     8777 *                                        Default empty string.
     8778 *     @type bool     $dismissible        Optional. Whether the admin notice is dismissible. Default false.
     8779 *     @type string   $id                 Optional. The value of the admin notice's ID attribute. Default empty string.
     8780 *     @type string[] $additional_classes Optional. A string array of class names. Default empty array.
     8781 *     @type string[] $attributes         Optional. Additional attributes for the notice div. Default empty array.
     8782 *     @type bool     $paragraph_wrap     Optional. Whether to wrap the message in paragraph tags. Default true.
     8783 * }
     8784 * @return string The markup for an admin notice.
     8785 */
     8786function wp_get_admin_notice( $message, $args = array() ) {
     8787    $defaults = array(
     8788        'type'               => '',
     8789        'dismissible'        => false,
     8790        'id'                 => '',
     8791        'additional_classes' => array(),
     8792        'attributes'         => array(),
     8793        'paragraph_wrap'     => true,
     8794    );
     8795
     8796    $args = wp_parse_args( $args, $defaults );
     8797
     8798    /**
     8799     * Filters the arguments for an admin notice.
     8800     *
     8801     * @since 6.4.0
     8802     *
     8803     * @param array  $args    The arguments for the admin notice.
     8804     * @param string $message The message for the admin notice.
     8805     */
     8806    $args       = apply_filters( 'wp_admin_notice_args', $args, $message );
     8807    $id         = '';
     8808    $classes    = 'notice';
     8809    $attributes = '';
     8810
     8811    if ( is_string( $args['id'] ) ) {
     8812        $trimmed_id = trim( $args['id'] );
     8813
     8814        if ( '' !== $trimmed_id ) {
     8815            $id = 'id="' . $trimmed_id . '" ';
     8816        }
     8817    }
     8818
     8819    if ( is_string( $args['type'] ) ) {
     8820        $type = trim( $args['type'] );
     8821
     8822        if ( str_contains( $type, ' ' ) ) {
     8823            _doing_it_wrong(
     8824                __FUNCTION__,
     8825                sprintf(
     8826                    /* translators: %s: The "type" key. */
     8827                    __( 'The %s key must be a string without spaces.' ),
     8828                    '<code>type</code>'
     8829                ),
     8830                '6.4.0'
     8831            );
     8832        }
     8833
     8834        if ( '' !== $type ) {
     8835            $classes .= ' notice-' . $type;
     8836        }
     8837    }
     8838
     8839    if ( true === $args['dismissible'] ) {
     8840        $classes .= ' is-dismissible';
     8841    }
     8842
     8843    if ( is_array( $args['additional_classes'] ) && ! empty( $args['additional_classes'] ) ) {
     8844        $classes .= ' ' . implode( ' ', $args['additional_classes'] );
     8845    }
     8846
     8847    if ( is_array( $args['attributes'] ) && ! empty( $args['attributes'] ) ) {
     8848        $attributes = '';
     8849        foreach ( $args['attributes'] as $attr => $val ) {
     8850            if ( is_bool( $val ) ) {
     8851                $attributes .= $val ? ' ' . $attr : '';
     8852            } elseif ( is_int( $attr ) ) {
     8853                $attributes .= ' ' . esc_attr( trim( $val ) );
     8854            } elseif ( $val ) {
     8855                $attributes .= ' ' . $attr . '="' . esc_attr( trim( $val ) ) . '"';
     8856            }
     8857        }
     8858    }
     8859
     8860    if ( false !== $args['paragraph_wrap'] ) {
     8861        $message = "<p>$message</p>";
     8862    }
     8863
     8864    $markup = sprintf( '<div %1$sclass="%2$s"%3$s>%4$s</div>', $id, $classes, $attributes, $message );
     8865
     8866    /**
     8867     * Filters the markup for an admin notice.
     8868     *
     8869     * @since 6.4.0
     8870     *
     8871     * @param string $markup  The HTML markup for the admin notice.
     8872     * @param string $message The message for the admin notice.
     8873     * @param array  $args    The arguments for the admin notice.
     8874     */
     8875    return apply_filters( 'wp_admin_notice_markup', $markup, $message, $args );
     8876}
     8877
     8878/**
     8879 * Outputs an admin notice.
     8880 *
     8881 * @since 6.4.0
     8882 *
     8883 * @param string $message The message to output.
     8884 * @param array  $args {
     8885 *     Optional. An array of arguments for the admin notice. Default empty array.
     8886 *
     8887 *     @type string   $type               Optional. The type of admin notice.
     8888 *                                        For example, 'error', 'success', 'warning', 'info'.
     8889 *                                        Default empty string.
     8890 *     @type bool     $dismissible        Optional. Whether the admin notice is dismissible. Default false.
     8891 *     @type string   $id                 Optional. The value of the admin notice's ID attribute. Default empty string.
     8892 *     @type string[] $additional_classes Optional. A string array of class names. Default empty array.
     8893 *     @type bool     $paragraph_wrap     Optional. Whether to wrap the message in paragraph tags. Default true.
     8894 * }
     8895 */
     8896function wp_admin_notice( $message, $args = array() ) {
     8897    /**
     8898     * Fires before an admin notice is output.
     8899     *
     8900     * @since 6.4.0
     8901     *
     8902     * @param string $message The message for the admin notice.
     8903     * @param array  $args    The arguments for the admin notice.
     8904     */
     8905    do_action( 'wp_admin_notice', $message, $args );
     8906
     8907    echo wp_kses_post( wp_get_admin_notice( $message, $args ) );
     8908}
  • trunk/src/wp-login.php

    r56628 r56654  
    229229
    230230    if ( $wp_error->has_errors() ) {
    231         $errors   = '';
    232         $messages = '';
     231        $error_list = array();
     232        $messages   = '';
    233233
    234234        foreach ( $wp_error->get_error_codes() as $code ) {
     
    236236            foreach ( $wp_error->get_error_messages( $code ) as $error_message ) {
    237237                if ( 'message' === $severity ) {
    238                     $messages .= '  ' . $error_message . "<br />\n";
     238                    $messages .= '<p>' . $error_message . '</p>';
    239239                } else {
    240                     $errors .= '    ' . $error_message . "<br />\n";
     240                    $error_list[] = $error_message;
    241241                }
    242242            }
    243243        }
    244244
    245         if ( ! empty( $errors ) ) {
     245        if ( ! empty( $error_list ) ) {
     246            $errors = '';
     247
     248            if ( count( $error_list ) > 1 ) {
     249                $errors .= '<ul class="login-error-list">';
     250
     251                foreach ( $error_list as $item ) {
     252                    $errors .= '<li>' . $item . '</li>';
     253                }
     254
     255                $errors .= '</ul>';
     256            } else {
     257                $errors .= '<p>' . $error_message . '</p>';
     258            }
     259
    246260            /**
    247261             * Filters the error messages displayed above the login form.
     
    251265             * @param string $errors Login error message.
    252266             */
    253             echo '<div id="login_error">' . apply_filters( 'login_errors', $errors ) . "</div>\n";
     267            $errors = apply_filters( 'login_errors', $errors );
     268            wp_admin_notice(
     269                $errors,
     270                array(
     271                    'type'           => 'error',
     272                    'id'             => 'login_error',
     273                    'paragraph_wrap' => false,
     274                )
     275            );
    254276        }
    255277
     
    262284             * @param string $messages Login messages.
    263285             */
    264             echo '<p class="message" id="login-message">' . apply_filters( 'login_messages', $messages ) . "</p>\n";
     286            $messages = apply_filters( 'login_messages', $messages );
     287            wp_admin_notice(
     288                $messages,
     289                array(
     290                    'type'               => 'info',
     291                    'id'                 => 'login-message',
     292                    'additional_classes' => array( 'message' ),
     293                    'paragraph_wrap'     => false,
     294                )
     295            );
    265296        }
    266297    }
     
    400431
    401432    ?>
    402     <div class="clear"></div>
    403433    </body>
    404434    </html>
     
    830860        do_action( 'lost_password', $errors );
    831861
    832         login_header( __( 'Lost Password' ), '<p class="message">' . __( 'Please enter your username or email address. You will receive an email message with instructions on how to reset your password.' ) . '</p>', $errors );
     862        login_header(
     863            __( 'Lost Password' ),
     864            wp_get_admin_notice(
     865                __( 'Please enter your username or email address. You will receive an email message with instructions on how to reset your password.' ),
     866                array(
     867                    'type'               => 'info',
     868                    'additional_classes' => array( 'message' ),
     869                )
     870            ),
     871            $errors
     872        );
    833873
    834874        $user_login = '';
     
    947987            reset_password( $user, $_POST['pass1'] );
    948988            setcookie( $rp_cookie, ' ', time() - YEAR_IN_SECONDS, $rp_path, COOKIE_DOMAIN, is_ssl(), true );
    949             login_header( __( 'Password Reset' ), '<p class="message reset-pass">' . __( 'Your password has been reset.' ) . ' <a href="' . esc_url( wp_login_url() ) . '">' . __( 'Log in' ) . '</a></p>' );
     989            login_header(
     990                __( 'Password Reset' ),
     991                wp_get_admin_notice(
     992                    __( 'Your password has been reset.' ) . ' <a href="' . esc_url( wp_login_url() ) . '">' . __( 'Log in' ) . '</a>',
     993                    array(
     994                        'type'               => 'info',
     995                        'additional_classes' => array( 'message', 'reset-pass' ),
     996                    )
     997                )
     998            );
    950999            login_footer();
    9511000            exit;
     
    9551004        wp_enqueue_script( 'user-profile' );
    9561005
    957         login_header( __( 'Reset Password' ), '<p class="message reset-pass">' . __( 'Enter your new password below or generate one.' ) . '</p>', $errors );
     1006        login_header(
     1007            __( 'Reset Password' ),
     1008            wp_get_admin_notice(
     1009                __( 'Enter your new password below or generate one.' ),
     1010                array(
     1011                    'type'               => 'info',
     1012                    'additional_classes' => array( 'message', 'reset-pass' ),
     1013                )
     1014            ),
     1015            $errors
     1016        );
    9581017
    9591018        ?>
     
    9861045
    9871046            <p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
    988             <br class="clear" />
    9891047
    9901048            <?php
     
    10801138        $redirect_to = apply_filters( 'registration_redirect', $registration_redirect, $errors );
    10811139
    1082         login_header( __( 'Registration Form' ), '<p class="message register">' . __( 'Register For This Site' ) . '</p>', $errors );
     1140        login_header(
     1141            __( 'Registration Form' ),
     1142            wp_get_admin_notice(
     1143                __( 'Register For This Site' ),
     1144                array(
     1145                    'type'               => 'info',
     1146                    'additional_classes' => array( 'message', 'register' ),
     1147                )
     1148            ),
     1149            $errors
     1150        );
    10831151
    10841152        ?>
     
    11051173                <?php _e( 'Registration confirmation will be emailed to you.' ); ?>
    11061174            </p>
    1107             <br class="clear" />
    11081175            <input type="hidden" name="redirect_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
    11091176            <p class="submit">
  • trunk/tests/phpunit/tests/functions/wpAdminNotice.php

    r56653 r56654  
    44 * Tests for `wp_admin_notice()`.
    55 *
    6  * @group admin
     6 * @group functions.php
    77 *
    88 * @covers ::wp_admin_notice
    99 */
    10 class Tests_Admin_WpAdminNotice extends WP_UnitTestCase {
     10class Tests_Functions_WpAdminNotice extends WP_UnitTestCase {
    1111
    1212    /**
  • trunk/tests/phpunit/tests/functions/wpGetAdminNotice.php

    r56653 r56654  
    44 * Tests for `wp_get_admin_notice()`.
    55 *
    6  * @group admin
     6 * @group functions.php
    77 *
    88 * @covers ::wp_get_admin_notice
    99 */
    10 class Tests_Admin_WpGetAdminNotice extends WP_UnitTestCase {
     10class Tests_Functions_WpGetAdminNotice extends WP_UnitTestCase {
    1111
    1212    /**
Note: See TracChangeset for help on using the changeset viewer.