WordPress: How to change the Excerpt box label & description

How to change the default WordPress Excerpt label and description

Sometimes you want to change default WordPress labels and descriptions so they’re a bit more tailed towards your site. One of the sites I work on is a news site and writers were getting confused as to exactly what an excerpt is, how long it should be and what it should consist of. To combat we the problem we simply changed the description label on the excerpt box in the WordPress admin. Voilà, problem sorted.

Changing the Excerpt Label and Description

Add the following code to your theme’s functions.php file. Be sure to change the translated text to whatever you wish your excerpt label and description to say.

function wpartisan_excerpt_label( $translation, $original ) {
	if ( 'Excerpt' == $original ) {
		return __( 'My Custom Excerpt Label' );
	} elseif ( false !== strpos( $original, 'Excerpts are optional hand-crafted summaries of your' ) ) {
		return __( 'My Custom Excerpt Description' );
	}
	return $translation;
}
add_filter( 'gettext', 'wpartisan_excerpt_label', 10, 2 );

And this is the result:

Setting target blank to be checked by default in WP

How to set the WordPress link dialog box to have the target _blank checkbox selected by default.

A ticket came up recently requesting that the target _blank checkbox in the WordPress link dialog be checked by default. We link to external sites a lot so it makes a lot of sense. Ok no problem, I thought. WordPress is bound to have an easy hook or two I can use. Well after a lot of Googling I found plenty of methods, none of which appeared to work. So, reluctantly, I went digging.

The Problem

WordPress open link in new window

If you take a look in /wordpress/wp-includes/class-wp-editor.php line 1439 you can see that the input is hardcoded without any options or hooks. Humm annoying.

Digging into TinyMCE we see that in version 4.4.3 there’s a default_link_target attribute that can be passed into the config to achieve exactly what we what. Unfortunately that doesn’t work for one of two reasons, either it’s because WordPress currently uses TinyMCE 4.4.1 or it’s because WordPress uses a custom Link dialog popup which doesn’t account for all of TinyMCE’s options. I didn’t bother to investigate which it was.

Ok so there’s clearly no ‘right’ way to do, looks like it’ll be a custom javascript solution then.

The code


	/**
	 * Select target _blank by default.
	 *
	 * Outputs javascript that hooks into the WordPress link dialog
	 * and sets the target _blank checkbox to be selected by default.
	 *
	 * @return null
	 */
	function default_target_blank() {

		?>
		<script>
			jQuery(document).on( 'wplink-open', function( wrap ) {
				if ( jQuery( 'input#wp-link-url' ).val() <= 0 )
					jQuery( 'input#wp-link-target' ).prop('checked', true );
			});
		</script>
		<?php
	}
	add_action( 'admin_footer-post-new.php', 'default_target_blank', 10, 0 );
	add_action( 'admin_footer-post.php', 'default_target_blank', 10, 0 );

The code checks to see if a URL has been entered for the link and if not auto checks the target _blank checkbox. If a URL has been entered it does nothing. Add this code to your functions.php file then on the Post New and Post Edit screens the target blank checkbox will be checked by default for all new links.

Hopefully you’ll find this helpful, any problems let me know in the comments below.

WordPress Multisite Speed Improvements – Admin Bar

The WP admin bar is certainly handy and the dropdown menu listing the sites in the current network is great for quickly switching between them. Unfortunately this same dropdown menu can also cause slow site speeds, particularly in a large multisite network. To understand why a dropdown menu can be slow and how it can be improved let’s take a look at the code.

The problem with the Admin Bar

If we dig deep into the wp-includes/class-wp-admin-bar.php file we find a method called add_menus which (unsurprisingly!) is where all the menu nodes are registered for the admin bar. Using an action called ‘admin_bar_menu’ we can see that each node in the menu corresponds to a registered function. The function that deals with the sites drop down is called wp_admin_bar_my_sites_menu and is located in the wp-includes/admin-bar.php file.

If we take a look at this function we see it does a few basic permission checks, sets up the network menu if the user is a super admin and finally gets all the blogs the user is a member of ($wp_admin_bar->user->blogs is assigned in the WP_Admin_Bar class) cycles through them and creates the appropriate menu links.

In order to create these links for each of the sites in the dropdown it calls a function called switch_to_blog(), a handy multisite function that temporarily replaces all global blog variables with new ones for another blog on your network. The problem is it’s slow. Really, really slow. If you have anything like a large multisite install then the fact it’s being called on every page load, for every logged in user, to switch to every site in the network is going to have a significant affect on your site’s performance.

The Solution

The obvious answer is to not use it and create the links some other way. So that’s exactly what we’re going to do. Here’s how.

Firstly, de-register the old function:

add_action( 'add_admin_bar_menus', 'large_network_remove_wp_admin_bar_my_sites_menu', 10, 0 );

/**
 * De-register the native WP Admin Bar My Sites function.
 *
 * Loading all sites menu for large multisite is inefficient and bad news. This de-registers the native WP function so it can be replaced with a more efficient one.
 *
 * @return null
 */
public function large_network_remove_wp_admin_bar_my_sites_menu() {
	remove_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 );
}

Now add in this new function. It’s mostly the same as the original:

add_action( 'admin_bar_menu', 'large_network_replacement_my_sites_menu', 20, 1 );


/**
 * Add the "My Sites/[Site Name]" menu and all submenus.
 *
 * Essentially the same as the WP native one but doesn't use switch_to_blog();
 *
 *
 * @param WP_Admin_Bar $wp_admin_bar
 * @return null
 */
public function large_network_replacement_my_sites_menu( $wp_admin_bar ) {
	// Don't show for logged out users or single site mode.
	if ( ! is_user_logged_in() || ! is_multisite() )
		return;

	// Show only when the user has at least one site, or they're a super admin.
	if ( count( $wp_admin_bar->user->blogs ) < 1 && ! is_super_admin() ) return; if ( $wp_admin_bar->user->active_blog ) {
		$my_sites_url = get_admin_url( $wp_admin_bar->user->active_blog->blog_id, 'my-sites.php' );
	} else {
		$my_sites_url = admin_url( 'my-sites.php' );
	}

	$wp_admin_bar->add_menu( array(
		'id'    => 'my-sites',
		'title' => __( 'My Sites' ),
		'href'  => $my_sites_url,
	) );

	if ( is_super_admin() ) {
		$wp_admin_bar->add_group( array(
			'parent' => 'my-sites',
			'id'     => 'my-sites-super-admin',
		) );

		$wp_admin_bar->add_menu( array(
			'parent' => 'my-sites-super-admin',
			'id'     => 'network-admin',
			'title'  => __('Network Admin'),
			'href'   => network_admin_url(),
		) );

		$wp_admin_bar->add_menu( array(
			'parent' => 'network-admin',
			'id'     => 'network-admin-d',
			'title'  => __( 'Dashboard' ),
			'href'   => network_admin_url(),
		) );
		$wp_admin_bar->add_menu( array(
			'parent' => 'network-admin',
			'id'     => 'network-admin-s',
			'title'  => __( 'Sites' ),
			'href'   => network_admin_url( 'sites.php' ),
		) );
		$wp_admin_bar->add_menu( array(
			'parent' => 'network-admin',
			'id'     => 'network-admin-u',
			'title'  => __( 'Users' ),
			'href'   => network_admin_url( 'users.php' ),
		) );
		$wp_admin_bar->add_menu( array(
			'parent' => 'network-admin',
			'id'     => 'network-admin-t',
			'title'  => __( 'Themes' ),
			'href'   => network_admin_url( 'themes.php' ),
		) );
		$wp_admin_bar->add_menu( array(
			'parent' => 'network-admin',
			'id'     => 'network-admin-p',
			'title'  => __( 'Plugins' ),
			'href'   => network_admin_url( 'plugins.php' ),
		) );
		$wp_admin_bar->add_menu( array(
			'parent' => 'network-admin',
			'id'     => 'network-admin-o',
			'title'  => __( 'Settings' ),
			'href'   => network_admin_url( 'settings.php' ),
		) );
	}

	// Add site links
	$wp_admin_bar->add_group( array(
		'parent' => 'my-sites',
		'id'     => 'my-sites-list',
		'meta'   => array(
			'class' => is_super_admin() ? 'ab-sub-secondary' : '',
		),
	) );

	foreach ( (array) $wp_admin_bar->user->blogs as $blog ) {

		$blavatar = '
<div class="blavatar"></div>

';

		$blogname = $blog->blogname;

		if ( ! $blogname ) {
			$blogname = preg_replace( '#^(https?://)?(www.)?#', '', $blog->siteurl );
		}

		$menu_id  = 'blog-' . $blog->userblog_id;

		$admin_url = $blog->siteurl . '/wp-admin';

		$wp_admin_bar->add_menu( array(
			'parent'    => 'my-sites-list',
			'id'        => $menu_id,
			'title'     => $blavatar . $blogname,
			'href'      => $admin_url,
		) );

		$wp_admin_bar->add_menu( array(
			'parent' => $menu_id,
			'id'     => $menu_id . '-d',
			'title'  => __( 'Dashboard' ),
			'href'   => $admin_url,
		) );

		$wp_admin_bar->add_menu( array(
			'parent' => $menu_id,
			'id'     => $menu_id . '-v',
			'title'  => __( 'Visit Site' ),
			'href'   => $blog->siteurl,
		) );

	}
}

Unfortunately we’ve had to remove the “New Post” & “Manage Comments” links. To add them in we’d have to check the user’s permissions on that blog and one way or another that would involve using switch_to_blog(). I don’t see it as much of a loss however.

The site I added this to had noticeable speed improvements after implementing so if you do have a large multisite environment or a lot of users then it’s definitely worth doing.

Comments or questions, post below!

WordPress failing to redirect with protected Posts

I recently had a fairly obscure problem on a WordPress site and I wanted to write about it on the off chance it saves somebody else a few frustrating hours.

The problem

Whenever a password was entered into a protected posts password box the form would submit successfully, start to redirect, then die on a blank screen. If the webpage was refreshed the password protected post was unlocked (indicating it was likely a redirect problem not an auth problem).

The Cause

It turns out that WordPress ends up using a helper function called wp_get_raw_referer() to retrieve the address of the previous page. If we take a look at it we see it first checks for a WordPress custom variable called ‘_wp_http_referer’ then the default $_SERVER variable ‘HTTP_REFERER’ before retiring false if neither are set. All perfectly reasonable.

How this affects password protected posts?

The default WordPress password form on posts doesn’t contain the custom ‘_wp_http_referer’ field meaning WordPress is always relying on ‘HTTP_REFERER’ for redirecting. The problem with this is that ‘HTTP_REFERER’ is famously unreliable as it’s set by the user agent, meaning it can be wrong or even missing altogether, as in my case. When navigating from an HTTPS to HTTP page web browsers don’t set the ‘HTTP_REFERER’ field for ancient security reasons (there is a work around in modern browsers involving meta tags). So, as the password protected page redirects you through the WordPress login page to handle the form, if you’re WordPress admin is behind SSL (‘FORCE_ADMIN_SSL includes the login page) but your front-end isn’t (because you’re serving ads or whatnot) the redirect on the password protect page dies on a blank screen as the ‘HTTP_REFERER’ field is stripped out after the wp-login page.

The Solution

Luckily, the fix is easy. We just need to add the ‘_wp_http_referer’ field to the password protect form then we don’t rely on ‘HTTP_REFERER’ at all. Simply add this to your functions.php file and it will do just that.

/**
 * Add a redirect field for password protect forms when
 * HTTP_REFERER isn't set
 *
 * @access public
 * @param string $form_html
 * @return string
 */
public function wpa_add__wp_http_referer( $form_html ) {
 if ( empty( $_SERVER['HTTP_REFERER'] ) ) {
	 $form_html = str_ireplace( '</form>', '<input type="hidden" name="_wp_http_referer" value="' . esc_url( get_the_permalink() ) . '"></form>', $form_html );
 }

 return $form_html;
}
add_filter( 'the_password_form', array( $this, 'wpa_add__wp_http_referer' ), 10, 1 );

It’s fairly obscure I admit but hopefully it will help someone and save them a few hours debugging!

Rename and clean WordPress media filenames

For the most part WordPress does a decent job of renaming and cleaning up attachment media filenames, but sometimes it’s just not quite good enough. When it comes to UTF8 characters or accents it tends to just ignore them as they’re (mostly) considered web safe.

This can become an issue when third party services crawl your site and struggle with the encoding e.g. Facebook’s Sharing Debugger which is where I had trouble. Of course it’s also not great for SEO. In an Ideal world you want the featured image filename to closely mirror the post title, not be “Screenshot 2016-08-05T22:03:22.jpg”.

Cleaner Filenames

If all you want to do is clean up your filenames and remove UTF8 characters and accents then just place this code in your functions.php (will only clean up all new filenames). This code was originally posted by Vaughn Royko.

Update: I’ve also released it as a super simple plugin for those that prefer it. Check it out on WordPress.org here.


/**
 * Produces cleaner filenames for uploads
 *
 * @param  string $filename
 * @return string
 */
function wpartisan_sanitize_file_name( $filename ) {

	$sanitized_filename = remove_accents( $filename ); // Convert to ASCII

	// Standard replacements
	$invalid = array(
		' '   => '-',
		'%20' => '-',
		'_'   => '-',
	);
	$sanitized_filename = str_replace( array_keys( $invalid ), array_values( $invalid ), $sanitized_filename );

	$sanitized_filename = preg_replace('/[^A-Za-z0-9-\. ]/', '', $sanitized_filename); // Remove all non-alphanumeric except .
	$sanitized_filename = preg_replace('/\.(?=.*\.)/', '', $sanitized_filename); // Remove all but last .
	$sanitized_filename = preg_replace('/-+/', '-', $sanitized_filename); // Replace any more than one - in a row
	$sanitized_filename = str_replace('-.', '.', $sanitized_filename); // Remove last - if at the end
	$sanitized_filename = strtolower( $sanitized_filename ); // Lowercase

	return $sanitized_filename;
}

add_filter( 'sanitize_file_name', 'wpartisan_sanitize_file_name', 10, 1 );

So what does it do:

File: ~My WordPress Upload~.jpg
Default WordPress: My-WordPress-Upload.jpg
Custom Solution: my-wordpress-upload.jpg

File: ÐÕçument full of $$$.pdf
Default WordPress: ÐÕçument-full-of-.pdf
Custom Solution: document-full-of.pdf

File: Really%20Ugly%20Filename-_-That_-_Is_Too Common…..png
Default WordPress: Really-Ugly-Filename-_-That_-_Is_Too-Common…..png
Custom Solution: really-ugly-filename-that-is-too-common.png

Complete SEO Solution

If you want a more complete solution we’re currently develop a plugin to do just that. It’ll be able to:

– Sanitize and clean all new media upload filenames
– Optionally sanitize and clean all existing media filenames
– Allow manual renaming of all existing media
– Auto renaming of filenames based on post title, even when the post is updated

How to check PHP version before switching theme

We recently started created a new theme for our company website. Obviously we were excited about using all the swish (relatively) new PHP features, closures, short array syntax etc etc. As you may or may not know WordPress’ minimum PHP version is PHP 5.2.4 which has none of those features and will error if it finds them. This isn’t really a problem for us as it’s a private theme and we have full control of the server, however I have seen these PHP features used in themes before and obviously it causes some confusion. So here is how to check the PHP version when switching theme.

 

// Minimum required version
define( 'THEME_REQUIRED_PHP_VERSION', '5.3.0' );

add_action( 'after_switch_theme', 'oz_check_theme_setup' );
function oz_check_theme_setup(){

	// Compare versions
	if ( version_compare( PHP_VERSION, THEME_REQUIRED_PHP_VERSION, '<' ) ) :

		// Theme not activated info message
		add_action( 'admin_notices', 'oz_php_version_admin_notice' );
		function oz_php_version_admin_notice() {
			?>
			<div class="update-nag">
				<?php printf( __( 'This theme requires a minimum PHP version of %s. Your version is s%. Your previous theme has been restored.', 'text-domain' ), THEME_REQUIRED_PHP_VERSION, PHP_VERSION ); ?>
			</div>
			<?php
		}

		// Switch back to previous theme
		switch_theme( get_option( 'theme_switched' ) );
			return false;

	endif;
}

How to change the default WordPress user admin color theme

As part of an effort to clean up the WordPress admin area for our authors at work we started removing elements from the dashboard they don’t need or want in an effort to make it less distracting for them. As well as removing text and unnecessary menus we also decided to change from the default dark grey theme to a light grey which we thought would fit better with our branding.

Force users to use a certain admin color theme

Add this to your theme’s functions.php. For every user it will force them to use the light theme.

/**
 * Force contributors to use the light colour scheme
 *
 * @param  string $result
 * @return string
 */
function oz_change_admin_color( $result ) {
	return 'light';
}
add_filter( 'get_user_option_admin_color', 'oz_change_admin_color' );

The complete list of default WordPress admin color themes is: default, light, blue, coffee, ectoplasm, midnight, ocean, sunrise.

How to the remove “Screen Options” and “Help” tabs from the wp-admin

Ok so part 2 in my loosely tied together series on “Cleaning up the WordPress admin” is about how to remove the “Screen Options” and “Help” tab from the top right of the screen. In case you missed part 1 – cleaning up the admin footer in my mini series, the reason I’m doing this is to help clean up the admin to give the writers on our site a much cleaner / easier / less distracting writing experience. As usual, add the following to your theme’s functions.php file.

 

Removing the Help Tab

Unsurprisingly it’s pretty easy to do with a filter. You can of course wrap it in a if statement if you want to conditionally remove it.


/**
* Removes the Help tab in the WP Admin
*
* @param array $old_help
* @param int $screen_id
* @param obj $screen
* @return array
*/
function oz_remove_help_tabs( $old_help, $screen_id, $screen ){
	$screen->remove_help_tabs();
	return $old_help;
}
add_filter( 'contextual_help', 'oz_remove_help_tabs', 999, 3 );

Removing the Screen Options Tab

This is actually even easier:

add_filter( 'screen_options_show_screen', '__return_false' );

Of course if you you want to conditionally show it you’ll need to point it to a custom function rather than the WordPress __return_false one. Although simple the only slight snag with this method is that you’ll lose any previously customised screen options, (columns on the admin homepage for example). However an anonymous user in this excellent stackoverflow question provides a solution.

function oz_remove_screen_options( $display_boolean, $wp_screen_object ){
	$blacklist = array('post.php', 'post-new.php', 'index.php', 'edit.php');
	if ( in_array( $GLOBALS['pagenow'], $blacklist ) ) {
		$wp_screen_object->render_screen_layout();
		$wp_screen_object->render_per_page_options();
		return false;
    }
	return true;
}
add_filter( 'screen_options_show_screen', 'oz_remove_screen_options', 10, 2 );

Whilst very similar this will not lose any saved screen options and will only remove the tab for the pages listed in the $blacklist array.

Part 3 is cleaning up the WordPress Dashboard

How to remove the text in the wp-admin footer

After a brief chat with our UI/UX chap we decided it would make sense to remove the text at the bottom of the WordPress admin. “Thank you for creating with WordPress” on the bottom left, and the WordPress version on the bottom right. I agree it’s hardly offensive but we decided it was a bit of a distraction for our writers. We have a lot of them and they all use the wp-admin for writing articles so we’re trying hard to make the process easier and the dashboard cleaner. Whilst the text in the footer isn’t that promient we felt it’d help with the “clean look” we’re tying for. So I set to work.

Bottom left text

The bottom left text it really easy, add the following to your themes functions.php:

add_filter( 'admin_footer_text', '__return_false' );

This will completely remove it, if you wanted to keep it but alter the text:

/**
 * Alters the bottom left admin text
 *
 * @return string
 */
function oz_alter_wp_admin_bottom_left_text( $text ) {
	return sprintf( __( 'Ear more <a href="%s" title="toast" target="_blank">toast</a>' ), 'https://www.google.com/search?q=toast' );
}
add_filter( 'admin_footer_text', 'oz_alter_wp_admin_bottom_left_text' );

Bottom right text

Ok so what about the bottom right. Slightly trickier as we need to remove the filter:

/**
 * Hide the WP version in the footer
 *
 * @return null
 */
function oz_admin_dashboard_footer_right() {
	remove_filter( 'update_footer', 'core_update_footer' );
}
add_action( 'admin_menu', 'oz_admin_dashboard_footer_right' );

Of course, if you just want to show the version for the administrator, (as we’re doing) you can wrap the remove_filter in an if statement:

/**
 * Hide the WP version in the footer for everyone but administrators
 *
 * @return null
 */
function oz_admin_dashboard_footer_right() {
	if ( ! current_user_can( 'update_core' ) ) {
		remove_filter( 'update_footer', 'core_update_footer' );
	}
}
add_action( 'admin_menu', 'oz_admin_dashboard_footer_right' );

So there you go. Much cleaner! In the same vein, how to remove the “Help” and “Screen Options” tabs is up next.