Working with excerpts in WordPress, or why isn’t this filter working?

When is an excerpt not an excerpt?

If you call get_the_excerpt() WordPress returns a string that looks like an excerpt – but it might not be. If your post doesn’t have a handcrafted excerpt, WordPress returns an automatically generated word-counted trimmed-down version of the full post content (see codex).

TL/DR: WordPress returns a string for get_the_excerpt() even when has_excerpt() is FALSE.

I’d used this bit of code to automatically add a ‘read more’ link at the end of every excerpt on a search page.

function ds_excerpt_read_more( $output ) {
	$read_more_link = ' (<a class="more-link" href="' . get_permalink() . '">Read more</a>)';
	if ( has_excerpt() && ! is_attachment() & is_search() ) {
		$output .= " " . $read_more_link;
	return $output;
add_filter( 'get_the_excerpt', 'ds_excerpt_read_more'  );

Problem was, it wasn’t working everywhere – some posts didn’t have the modified excerpt.

has_excerpt() was the problem – it only returns TRUE if there is a manual excerpt.

So this works better:

function ds_excerpt_read_more( $output ) {
	$read_more_link = ' (<a class="more-link" href="' . get_permalink() . '">Read more</a>)';
	if ( has_excerpt() && ! is_attachment() & is_search() ) {
		$output .= " " . $read_more_link;
	} elseif (is_search() &&  ! has_excerpt() )  {
		$excerpt_length = apply_filters('excerpt_length', 55);
		$output = wp_trim_words( $output, $excerpt_length, '' );
		$output .= " " . $read_more_link;
	return $output;
add_filter( 'get_the_excerpt', 'ds_excerpt_read_more'  );

but this is best:

function ds_excerpt_read_more( $output ) {
	if (  is_search() && ! is_attachment() ) {
		$read_more_link = ' (<a class="more-link" href="' . get_permalink() . '">Read more</a>)';
		if ( ! has_excerpt() ) {
			$excerpt_length = apply_filters('excerpt_length', 55);
			$output = wp_trim_words( $output, $excerpt_length, '' );
			/* Finds the last full stop in the excerpt, removes everything after it. */
			if ( ( $pos = mb_strrpos( $output, '.' ) ) !== false ) {
				$output = substr( $output, 0, $pos + 2 );
		/* Appends a 'read more' link at the end of the excerpt - if it's a search */
		$output .= " " . $read_more_link;
	return $output;
add_filter( 'get_the_excerpt', 'ds_excerpt_read_more'  );

How to add a new Page Template to WordPress

You have to do two things to add a new page template to WordPress.

1. Add a file with the correct name

WordPress uses a template hierarchy – you need to give your template the right name. Either page-ID.php – where ID is the post ID of the page you are targeting, or page-SLUG.php – where SLUG is either the slug of the page you are targeting or an understandable name you can assign through the page interface to pages.

2. Add a ‘Template Name’ declaration at the start of your template file

For WordPress to pick your template up, you need to start each page template file with a Template Name string followed by a unique name for the template:

Template Name: Offer page template

How to remove your front page from WordPress search results

If you use a static front page for your WordPress site, depending on its contents and structure it may show up in your search results – you may (or may not) want this to happen.

If you don’t want to see your front page in your search results, add this code to your functions.php file:

add_action( 'pre_get_posts', 'edinburgh_wp_exclude_front_page_from_search' );
function edinburgh_wp_exclude_front_page_from_search( $query ) {
    if ( $query->is_main_query() && $query->is_search() ) {
        // Get an array with page ID of front page 
	$pageID = array(get_option('page_on_front'));
 	$query->set('post__not_in', $pageID);

If you use a ‘regular’ setup and have a home page and not a static front page, use this slightly different function to exclude the home page from search results.

add_action( 'pre_get_posts', 'edinburgh_wp_exclude_home_page_from_search' );
function edinburgh_wp_exclude_home_page_from_search( $query ) {
    if ( $query->is_main_query() && $query->is_search() ) {
        // Get an array with page ID of home page
	$pageID = array(get_option('page_for_posts'));
 	$query->set('post__not_in', $pageID);

The best way to customise the categories widget in WordPress

You may not like how WordPress’ Categories widget lists categories – particularly if you want to modify the list that’s displayed and hide some categories or have a custom order of categories (rather than alphabetical or by number of posts).

So what’s the easiest way to customise the display of the Categories widget??

You don’t.

Create a new *menu*, add the categories that you want in the order you want them, and add your new menu to the widget area.

So you can go from this:

to this:

You’ve created a custom post type, but WordPress says your posts can’t be found…

This strange thing happened to me earlier today – added a custom post type earlier this week, published an article this afternoon – but then I couldn’t view the published version – WordPress said it couldn’t be found (404 error).


Go to your dashboard, and then to settings/permalinks.

Scroll to the bottom and click “Save Changes”. This will force WordPress to update permalink settings and your posts in your custom post type should now work.

Removing page, post, tag, category and taxonomy IDs from the body_class in WordPress

WordPress loves to add a wide range of CSS classes to the body of a page through, depending on what type of page it is, what resources it uses, and so on. Some of this is helpful (like ‘archive’, ‘post’ and the page format classes – ‘single-format-gallery’, ‘single-format-image’ etc.), but some of the tags are just noise – like the tags that have the post/page/category/tag/term IDs. There are a couple of ways to remove tags that you don’t want by filtering the body_class function. You can remove all generated classes, or add new ones, but selectively removing classes is a bit more tricky, depending on what you want to remove.

This function will strip out the IDs WordPress adds to tags, categories, pages, posts and taxonomy pages.

add_filter('body_class','remove_ID_classes_from_body', 50 );
function remove_ID_classes_from_body( $classes ) {
	global $wp_query;
	$page_query = $wp_query->get_queried_object();
	if(is_tag() || is_tax() || is_category()) {	
		$classes = array_diff( $classes, [( is_tag() ? 'tag-' : ( is_tax() ? 'term-':'category-') ) . $page_query->term_id] );
	} elseif (is_single() || is_page() ) {
		$classes = array_diff( $classes, [( is_page() ? 'page-id-' :'postid-').$page_query->ID] );
	return array_unique( $classes );

How to stop WordPress adding responsive images to your RSS feed

I have spent a lot of time hacking/munging how WordPress handles RSS output, as I often want different titles/image sizes etc. for RSS feeds than I do for the original web posts. I was just a bit unhappy to find out that WordPress’ automatic generation of responsive images (via the ‘srcset’ attributes) now adds this code to images in RSS files as well.

This is simply fixed by turning the srcset generation code off for RSS files:

if (is_feed()) {add_filter( 'wp_calculate_image_srcset_meta', '__return_null' );}

Variable names in WordPress (or why you should never use $category as a variable name)

The great thing about WordPress and PHP is that you can call variables anything you like. The bad thing about WordPress and PHP is that you can call variables anything you like…

Think about these variables:

$post, $category, $template

What do they have in common? Well, they all look like good, descriptive variable names don’t they? What if you changed this to

global $post, $category, $template;

and then put this in a template for a post…?

echo $post; echo $category; echo $template;

Well, you’d get all sorts of interesting things, as $post, $category and $template are all global variables in WordPress.

FYI: there is a (partial) list of WordPress global variables in the Codex.

So if you use these variable names, and you also access global variables with those names… sometimes you’ll be thinking: why doesn’t my code work? And you’ll find out that when you thought you were changing a local variable you were actually changing a global variable, and that affected something else…

See also: Global variables and understanding scope in WordPress.

Working with taxonomies in WordPress

I’ve really started to get into using taxonomies in WordPress. One of the early problems I had though was trying to get them to work – so here’s a few things I’ve learned about WordPress taxonomies.

Don’t define taxonomies in functions.php

This is the classic mistake users (and some developers) make. They register (i.e. define) taxonomies in a theme file, change the theme… and wonder where their taxonomies went! (Hint: they’re still there in your database – but if they’re not registered they can’t be accessed.) The solution to this is simple: use a functionality plugin. Then it doesn’t matter which theme you use: as long as the plugin is active, any theme can access your taxonomy data.

Managing taxonomies

WordPress doesn’t have the most robust set of tools for managing taxonomies – you can’t add them via the interface for example, and there are no built in tools for moving things between tags/categories/taxonomies. There are a couple of plugins that I’ve found very helpful:

Term Management Tools

Want to move terms between tags or categories and taxonomies? Want to rename/combine tags? Term Management Tools does that.

TC Custom Taxonomy Filter

You know those helpful dropdown menus you can use to filter posts by category etc.? TC Custom Taxonomy Filter lets you do that for every taxonomy you define. Very useful.

Making taxonomies appear – aka ‘why doesn’t it work’?

So you’ve defined your taxonomy, you’ve added new items/tags, you click on ‘View’ and you get… a 404 page. Go to Settings: Permalinks and select ‘Save Changes’. (NB: you don’t have to make any changes…!) That will update the rewrite rules and your taxonomy should work… assuming of course that you’ve got posts in that taxonomy.

A taxonomy is just a… category

Don’t believe me? Go to a taxonomy template. Add this:


Categories, tags, taxonomies… all the same thing.

Learning from themes

A big part of learning how to use WordPress is learning how to do things: one of the best ways to do that is to pull themes and plugins apart and see how they work. There are some great themes and there are some rubbish themes: the more you look at, the more elegant solutions you find. I’ll admit I didn’t really get the big deal behind the twentytwelve theme, and I was distinctly underwhelmed by the underscore theme – though I learned a lot from pulling it to bits.

The upcoming twentythirteen theme is different though – I really like the way it uses post formats and can see how you can abstract a lot of formatting decisions that I used to make on a category basis.

add_node vs add_menu in WordPress

If you were modifying the admin bar in WordPress 3.x you used to call this:

// Add a link to the dashboard in the [pre-existing] site-name node
$wp_admin_bar->add_menu( array(
	'parent' => 'site-name',
	'id'     => 'dashboard',
	'title'  => 'Dashboard',
	'href'   => admin_url(),
) );

to add a new item to your admin toolbar. Now you just replace add_menu with add_node – they have exactly the same effect – and exactly the same syntax – just a slightly different semantic effect on the reader.

Changing the heading levels for WordPress widgets

If you want to change the heading levels for widgets in a sidebar, you shouldn’t use actions or filters or css to override WordPress: what you do is change how the sidebar is *defined* when it is first initialised.

If your theme uses a sidebar, it has to register it first. Somewhere in your theme (usually in your functions.php file) you will find a call to the register_sidebar( $args ); function, where $args is either a string or an array that defines how the sidebar is structured.

If there are no arguments for the register_sidebar() call then the defaults are used – this is to use h2 for the widget titles. If you want to use a different heading (or none at all) then you need to set the values for 'before_title' and 'after_title' when you call register_sidebar() like this:

<?php register_sidebar(array(
  'name' => __( 'Right Hand Sidebar' ),
  'id' => 'right-sidebar',
  'description' => __( 'Widgets in this area will be shown on the right-hand side.' ),
  'before_title' => '<h1>',
  'after_title' => '</h1>'

What template am I using? Understanding the WordPress template hierarchy.

Sometimes I find that when I’m developing a theme, changes I make to .php and .css files sometimes don’t seem to have any effect on my theme, no matter how many times I reload the page in a browser or empty the browser’s cache. This is often because WordPress isn’t loading the template file I think it is. This is very true when you’re first developing a brand new site and may not have may posts/tags etc..

The standard advice is to familiarise yourself with WordPress’ template hierarchy – not only does the Codex explain when a template is being used, there is also a helpful flow chart provided by Chip Bennett. The problem is that the documentation often isn’t 100% accurate – for example WordPress won’t use an archive.php template if you’ve only got one post – it will use index.php instead.

A solution to this is to add some code that lets you check what template file WordPress is using: fortunately that value is stored in a global variable called $template. The code below adds a menu item to the WordPress Admin Bar that tells you what template is being used. The function checks if you are on an Admin screen or on your site, and adds an appropriate menu item.

// removes and adds links/menus from the admin bar
function edwp_admin_bar_render() {
	global $wp_admin_bar;
	$wp_admin_bar->add_group( array( 
		'parent' => 'site-name', 
		'id' => 'theme' ) 
	if ( !is_admin() ) {
		global $template;
		$short_path = explode(get_stylesheet_directory(), $template,2); 
		$wp_admin_bar->add_menu( array(
		'id' => 'template',
		'title' => 'Template: '.$short_path[1],
		'href' => ''
	if ( is_admin() ) {
		global $current_screen;
		$wp_admin_bar->add_menu( array(
		'id' => 'cur_screen',
		'title' => 'Admin screen: '.$current_screen->base,
		'href' => ''
add_action( 'wp_before_admin_bar_render', 'edwp_admin_bar_render',10);