WordPress 4.7: Custom bulk actions, Theme inheritance and more

WordPress 4.7 brought a lot of new features in September and there are more to come. Again there are great things like custom bulk actions, better theme inheritance and many more. But there are also things that might break your customers site which I want to show you. Read the more up to date and famous one afterwards!
Recent changes with WordPress 4.7
Open in new tab for later

New goodies in the WordPress Core

This blog is all about the WordPress Core – no plugins, no themes. So I am always happy when the core has some new features I can play with. Below you’ll find the latest changes and some nerdy examples.

Theme inheritance made easier

Those who read this blog know that theme inheritance is possible for ages using the locate_template function. With WordPress 4.7 there will be additional functions which do the same:

  • get_theme_file_uri
  • get_parent_theme_file_uri
  • get_theme_file_path (almost like locate_template)
  • get_parent_theme_file_path

The names speak for themselves. A call like get_theme_file_uri( 'inc/foo.php' ) will look up the file “inc/foo.php” in the child-theme and afterwards in the parent theme. Once found it will return the URL to this file or the path in case of the get_theme_file_path function.

If this is not enough then filters like theme_file_path can extend the possibilities. Imagine any plugin that comes with its own template files. It might like to add its own template files like that:

add_filter(
  'theme_file_path',
  function ( $path, $file ) {
    if ( $path ) {
      // already found in child- or parent-theme => keep it like that
      return $path;
    }

    // look up in my plugin
    if ( file_exists ( WP_PLUGIN_DIR . '/' . $file ) ) {
      return WP_PLUGIN_DIR . '/' . $file;
    }
  }
);

// within the plugin do something like this:
if ( $path = get_theme_file_path( 'my-plugin-slug/a-template.php' ) ) {
  require_once $path;
}

The advantage is that WordPress searches the file “my-plugin-slug/a-template.php” like this:

  • wp-content/themes/child-theme/my-plugin-slug/a-template.php
  • wp-content/themes/parent-theme/my-plugin-slug/a-template.php
  • wp-content/plugins/my-plugin-slug/a-template.php

Very great that the theme got the ability to override how a shortcode or widget will look by this simple hook. I would love to see this everywhere.

Override any shortcode you like

WordPress 4.7 brings a very nice filter for changing shortcodes on-the-fly. Some themes or plugins bring immutable shortcodes and your customer wants it slightly different. The filter “pre_do_shortcode_tag” allows overriding a shortcode by your own function:

// lets change the fictional [marquee] shortcode
add_filter(
  'pre_do_shortcode_tag',
  function ( $short_circuit, $tag, $attr ) {
    if ( 'marquee' != $tag ) {
      // not the marquee shortcode => ignore
      return $short_circuit;
    }

    // seems to be marquee => show something else
    return 'rly? marquee? is it 1990 again?';
  },
  10,
  3
);

Now the shortcode is overwritten by our own function as the documentation says:

Passing a truthy value to the filter will effectively short-circuit the shortcode generation process, returning that value instead.

Very helpful when you buy a theme and want to change the behavior of a shortcode for some pages or disable shortcodes at some points. Remember that even a string with whitespaces is a truthy value.

I hope the “pre_do_shortcode_tag” filter will become more dynamic by using a “pre_do_shortcode_{$shortcode}” filter. It costs a little less runtime and every hooked in function won’t have to have an early exit. This would be a very simple and clean enhancement.

Custom bulk actions

Custom Bulk actions in WordPress 4.7
Custom Bulk actions in WordPress 4.7

WordPress finally supports adding custom bulk actions (see “Using Custom Bulk Actions”).  It is very easy to register and can be done for any post type:

add_filter( 'bulk_actions-edit-post', 'register_my_bulk_actions' );
function register_my_bulk_actions( $bulk_actions ) {
  $bulk_actions['export_multiple'] = __( 'Export', 'acme');
  return $bulk_actions;
}

Once multiple rows are selected and our custom bulk action is used we need to fetch the action. As usual we hook into the process:

add_filter(
  'handle_bulk_actions-edit-post',
  'my_bulk_action_handler',
  10,
  3
);

function my_bulk_action_handler( $redirect_to, $action, $post_ids ) {
  if ( $action !== 'export_multiple' ) {
    return $redirect_to;
  }

  foreach ( $post_ids as $post_id ) {
    // Perform action for each post.
  }
 
  // after doing so allow a notice via URL-Query
  $redirect_to = add_query_arg(
    'bulk_export_multiple',
    count( $post_ids ),
    $redirect_to
  );

  return $redirect_to;
}

Later we can react to the “bulk_export_multiple” parameter and show a notice to the user:

add_action( 'admin_notices', 'my_bulk_export_multiple_notice' );
 
function my_bulk_export_multiple_notice() {
  if ( empty( $_REQUEST['bulk_export_multiple'] ) ) {
    // none of our business => early exit
    return;
  }

  // inform the user how many have been exported
  $count = intval( $_REQUEST['bulk_export_multiple'] );
  printf(
    '<div id="message" class="updated fade">'
    . __( '%s exported.', 'acme' )
    . '</div>',
    $emailed_count
  );
}

This is a full round-trip how custom bulk actions can be added. I wonder how many new plugins will be uploaded to the WordPress Plugin Repository that enhance bulk actions further.

Smilies, logos and other abbreviations

Keep this in mind for a situation where your customer likes to write “LMFAO” or “:LOGO:” in his text, which shall be replaced by an smiley or the company logo. Or at some point you decide to replace some abbreviations with the full text. Possible in WordPress 4.7 which solves the “Filter to filter default smilies” issue. Extend the set of smilies with a simple filter:

add_filter(
  'smilies',
  function ( $wpsmiliestrans ) {
    $wpsmiliestrans[':logo:'] = 'logo.png'
  }
);

This allows you to add or remove emojis per context. From now on the function translate_smiley() should return a path like “wp-includes/images/smilies/logo.png”. This can be corrected using the “smilies_src”-Filter:

add_filter(
  'smilies_src',
  function ( $smiley_url, $img ) {
    if ( file_exists( get_stylesheet_directory() . '/img/' . $img ) ) {
      // found image in theme => use it
      return get_stylesheet_uri() . '/img/logo.png';
    }

    // otherwise do nothing
    return $smiley_url
  }
);

Create your own event system with WP_Hook

A new class called WP_Hook has been introduced to cover the filter and action system of WordPress itself. The great thing about it is, that you can create your own event driven system with it very easy:

$_my_own_events = array();

// add callbacks to a task
add_task( 'cat', 'purr_function' );
add_task( 'cat', 'eat_function' );

// execute a task
do_task( 'cat' );

// and here are the according functions (summarized)
function add_task( $tag, $callable ) {
  global $_my_own_events;

  if ( ! isset( $_my_own_events[ $tag ] ) ) {
    // not yet registered => create new hook
    $_my_own_events[ $tag ] = new WP_Hook();
  }

  $_my_own_events[ $tag ]->add_filter( $tag, $callable, 10, 0 );
}

function do_task( $tag, $args ) {
  global $_my_own_events;

  if ( ! isset( $_my_own_events[ $tag ] ) ) {
    // task does not exist => do nothing
    return false;
  }

  $_my_own_events[ $tag ]->do_action( $args );
}

This is good when you want to manage a message broker separated from WordPress actions or filters. In addition the priority could be any other number or a context, when and where to run a filter. Multidimensional observer are possible now. I just made this term up but it really is as cool as it sounds and works with the WP_Hook class.

Edit: If Gary “Pento” Pendergast reads this, then please leave a comment why this class is “final”. Be prepared that I don’t like it.

Noteworthy nerdy changes

This would be great! After installing WordPress you are directly logged in. Instead of taking the user to the login screen everyone will get logged in directly after the installation and can continue to work (see “Automatically log user in after installation”). The code says “Yes!” but the trial says “No!”. After installing the latest version I am still stuck in the login screen.

Another thing is that when you have drop-downs that show all users, then the function wp_dropdown_users now allows to filter them by role. You can set a ‘role__in’ option to get only some users (like “subscriber”, “administrator” etc) or ‘role__not_in’ to have it the other way around (see “Support ‘role__in’ and ‘role__not_in’ arguments in wp_dropdown_users”).

A thing that might be overseen is the “View posts” link. While looking at the list of posts in the backend there will be a “View posts” link in the admin bar which leads to the archive page (see “Add a ‘View Archive’ link to admin bar for post types”). If your are afraid that customers yell at you, because the archive page is not well designed, then remove the node with the ID “archive” from the admin bar.

Passwords for protected posts can now be 255 chars long – finally! This ticket is 12 years old and solved in WordPress 4.7 (see “Lengthen password field for protected posts”).

And even more like …

  • The function get_the_archive_description will now show the author description on the archive page of an author.
  • A WP_Term_Query allows to order by a comma separated list of term_ids now (get_terms(['includes'=>'2,3', 'orderby'=>'includes']).
  • The title of uploaded files will now be sanitized. With this comes the opportunity to change the title via the “sanitize_title” filter.
  • Non-English category templates can be loaded now. Something like “category-машина.php” was not possible (see “category-slug.php cannot be properly loaded when slug is not English”).

Changes you should be aware of

While gathering information about the new features of WordPress 4.7 I also found a lot of stuff that might fail. Either I lost sight of it or things just broke and are not repaired yet. WordPress likes to have a great backward compatibility but the following ones seem like the opposite. Be prepared when updating your customers sites or while maintaining your plugin / theme.

Customizer: Support for IE8 dropped

As of Issue #38021 the support for Internet Explorer 8 has been dropped. This piece of software is about 7 years old and PHP 5.2 is 10 years old … Why did I say that? Nevermind.

This change affects only the customizer in the upcoming WordPress release. So if you have customers working with IE8 for some reason, then is this the interesting part of it:

<!--[if lte IE 8]>
<script type="text/javascript">
  document.body.className = document.body.className.replace( /(^|\s) (no-)?customize-support(?=\s|$)/, '' ) + ' no-customize-support';
</script>
<![endif]-->

The CSS-Class “no-customize-support” indicates that the current browser won’t be able to handle the customizer right. This is a point where you can hook in and show some update or “please call me” notice.

Another deprecated function in WordPress 4.7

As you see in the screenshot: Something just got deleted and at a later point it comes back as a deprecated function.

bildschirmfoto-2016-10-05-um-23-14-46

The function wp_get_network has been introduced in WordPress 3.9 and in Version 4.6 it has been marked as an internal function. Now it is just gone. So not backward compatible and about to break things.

Edit: Thank you Michael Beckwith for finding it. Not a breaking change but another deprecated function among plenty others. Especially when we speak of multisite functions. Exchange this with the get_network function.

Small but mighty ones

For building menus there is a simple search that helps us finding posts, pages and more. It showed unpublished posts and pages which will no longer appear. According to the commits on “Menu Customizer: Adding draft items?” the query has changed. So if you rely on something like scheduled menu entries then please take a look if something broke.

Both functions wp_enqueue_style and wp_enqueue_script have a changed signature. The “src” arguments (2nd one) default value is now an empty string instead of a boolean false. So whenever you use it or strict check for boolean false, test your plugin / theme against the current WordPress 4.7 alpha.

Some plugins or themes bring in their own tables and use dbDelta for that. A few are that cool to define an index on their table like “IDX_NAMES”, which will WordPress force down to a lowercase “idx_names”. Be aware that the things you want will be sanitized within dbDelta and indexes are no longer how your named them.

The customizer always had links without underlining them but now almost every link got underlines. So check if the customizer of your theme is still consistent (see “Customizer: Links within text should be underlined”).

Within WP_Rest_Request is the public method ::sanitize_params() which cleans up all the params you throw in the request. Before WordPress 4.7 it returned “null” when everything worked fine. Now it returns “true”, which is a completely different thing even in PHP. So if you checked for “null” on that, then change it to “true” for WordPress 4.7 and above.

Comment

While reading the commits I often have to think about some statements from core developers. “Final classes won’t go away!”, while there is a issue that promise the removal of the final statement. “Deprecated functions stay, we want BC!”, is another quote but signatures and interfaces change in almost every tenth commit. I have lost track what is wrong and right, so I just read the changes, inform you about it and try to remember those issues when a customers website breaks.

Anyway, I might open some issues or inform a core developer about that pain, so that they can think about it and decide if it is legit. Some changes are hard, some won’t harm anyone. I keep on track of all things that change with WordPress 4.7, maybe they correct things in a while. So stay tuned, subscribe or come back next week for new goodies found in WordPress.

12 thoughts on “WordPress 4.7: Custom bulk actions, Theme inheritance and more”

  1. Whoa! Thanks for the roundup! I would have missed theme inheritance and WP_Hook features in 4.7. I’m actually creating a new plugin right now that will really, really benefit from `theme_file_path` filter example! I was going to do it the WooCommerce way, which seems a bit awkward, so these are really great news!

    1. Yeah, I am happy to help and wonder what plugin that will be 🙂
      I also liked how WordPress 4.7 will solve this.

      1. It’s going to be a portfolio plugin for Photographers. If a theme doesn’t define it’s own layouts, the plugin will fall back to the default views, that kind of thing 🙂

    1. Yeah! Thank you very much. I really missed that one.
      So no breaking change there and I corrected the article.

      I am a bit ashamed that I wasn’t able to find it with my IDE 😀

  2. Pingback: WordPress 4.7 - Todas las novedades
  3. ” This piece of software is about 7 years and PHP 5.2 is 10 years old … Why did I say that? Nevermind.”

    That made me laugh 🙂 Great article though, definitely interested in seeing more posts like this- bookmarking.

  4. Thank you for sharing. It’s great to have an article resuming all changes and potential issues! I updated 24 websites last night and everything looks good.

    1. So how about now? WordPress 4.8 got those new Widgets and some core changes that break things too. Be aware and good luck! 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *