Miscellaneous Developer Focused Changes in WordPress 5.4

WordPress 5.4 adds a handful of small developer-focused changes. Let’s take a look!

Better information about errors in wp_login_failed

A new parameter, $error, gets passed in the wp_login_failed action, to get you more information about the error that caused login failure.

It’s the second argument of the action and holds a WP_Error object with the authentication failure details.

Find it in wp-includes/pluggable.php:

/**
 * Fires after a user login has failed.
 *
 * @since 2.5.0
 * @since 4.5.0 The value of `$username` can now be an email address.
 * @since 5.4.0 The `$error` parameter was added.
 *
 * @param string   $username Username or email address.
 * @param WP_Error $error    A WP_Error object with the authentication failure details.
 */
do_action( 'wp_login_failed', $username, $error );

See this related ticket on Trac: #49007

Multisite: add site ID to newblog_notify_siteadmin filter

A new parameter, website ID, gets passed in the newblog_notify_siteadmin filter, to help you to customize site admins notifications depending on the website ID.

Find it in wp-includes/ms-functions.php:

/**
 * Filters the message body of the new site activation email sent
 * to the network administrator.
 *
 * @since MU (3.0.0)
 * @since 5.4.0 The `$blog_id` parameter was added.
 *
 * @param string $msg Email body.
 * @param int$blog_id The new site's ID.
 */
$msg = apply_filters( 'newblog_notify_siteadmin', $msg, $blog_id );

For more, see the relevant ticket: #48554

Introducing TikTok videos embed

WordPress 5.4 introduces TikTok as a new oEmbed provider.

As of version 5.4, WordPress will recognize TikTok video URLs that follow this pattern:

'#https?://(www\.)?tiktok\.com/.*/video/.*#i'

TikTok support also comes with its own block for the editor.

Plus, TikTok embeds work in the Block and Classic Editors, in the Text Widget — and anywhere else you can use Embeds now.

For more, see:

  • Related ticket on Trac: #49083
  • Related pull request on Gutenberg GitHub repository: 19345

Removal of CollegeHumor video embed

Since the CollegeHumor service no longer exists, its oEmbed provider was removed from Core in WordPress 5.3.1.

With version 5.4, the service is now completely gone from WordPress and the corresponding Gutenberg Embed block is deprecated. For backward compatibility, existing CollegeHumor blocks will automatically be converted into a generic embed block.

For reference, see:

  • Related ticket on Trac: #48696
  • Related pull request on Gutenberg GitHub repository: 18591

Media: store the original URL of the attachment in the _source_url post meta value

When you use media_sideload_image to sideload a file from a URL, the original URL is now automatically stored as metadata.

You can also use media_sideload_image to store a local copy of a file.

This is great for two reasons. First, for copyright and fairness, storing the source URL gives you an easy way to find out where that file was originally hosted. Plus, you can query existing attachments and not sideload the same file twice.

The original URL of the attachment is stored in the _source_url post meta:

add_post_meta( $id, '_source_url', $file );

For reference, see the related Trac ticket: #48164

Accessibility: the Admin Bar is now loaded with wp_body_open when available

It’s a first principle of accessibility that the look of a page and its source order should match. And for far too long, it’s been a principle WordPress has observed inconsistently.

Consider the Admin Bar. Up to now, it’s been loaded with the wp_footer hook:

add_action( 'wp_footer', 'wp_admin_bar_render', 1000 );

In WordPress 5.4, the Admin Bar loads in the hook wp_body_open(new since WP 5.2) and fixes that source-order problem:

add_action( 'wp_body_open', 'wp_admin_bar_render', 0 );

Of course, some older themes don’t yet support the wp_body_open hook. For them, there’s a fallback in the wp_footer function:

function wp_footer() {
    /*
     * Sets up the Admin Bar if the current theme does not use `wp_body_open`.
     * @since 5.4.0
     */
    if ( ! did_action( 'wp_body_open' ) ) {
        add_action( 'wp_footer', 'wp_admin_bar_render', 1000 );
    }
}

See the relevant Trac ticket: #47053

Widgets: avoid duplicate IDs in Recent Comments

In WordPress 5.4, the Recent Comments widget no longer generates widgets with the same HTML ID.

Even in the rare case where you might need duplicate instances of the Recent Comments widgets on the same page, don’t target the ID on the <ul> HTML element for styling— it will be different for every instance of the widget.

See the relevant Trac ticket: #46747

Login and Registration: new parameter passed into the lostpassword_post action in retrieve_password()

In WordPress 5.4, the $user_data parameter gets passed into the lostpassword_post action in retrieve_password().

If the user exists, $user_data will return the corresponding WP_User object. Otherwise, the parameter will return false.

Either way, developers have one more piece of information to act on.

See the related Trac ticket: #38334

Theme headers support “Requires at least” and “Requires PHP” declarations

Have you ever wanted to make sure your theme users were running modern versions of WordPress and PHP? (Maybe so you could implement a particular feature?)

In WordPress 5.4 you can. It’s fairly easy, too: declare these header entries in your themes’ main stylesheets:

Requires PHP: declare the minimal required PHP version.

Requires at least: declare the minimal WordPress version.

delete_posts without triggering PHP notices—in every post type

What’s worse than an ugly orange PHP notice on a post—when you know your code is solid?

Have you ever triggered one by using 'map_meta_cap' => false in the declaration of a custom post type? Up to now, that could happen if you checked the delete_posts capability in several places in Core.

In WordPress 5.4, the delete_posts capability is now part of the get_post_type_capabilities() function by default—and without regard to the map_meta_cap value.

So say goodbye to those PHP notices — at least from delete_posts.

For more, see the relevant Trac ticket: #30991

#5-4, #dev-notes