Creating custom data columns for the (custom) Posts table in Wordpress admin

I often find myself needing to present additional data alongside the default Post listings. Wordpress doesn’t offer as many hooks for the admin area as it does for the frontend to mess around with layout, but there are more than enough to get the job done.

Wordpress' default Post index columns
Wordpress’ default Post index columns, showing headings and column content.

The goal here will be to insert a couple of new columns into Wordpress’ standard “Posts” index page.

There are two hooks we’ll be looking at here, the first is manage_posts_columns, and the other manage_posts_custom_column. These hooks will let you modify both the table headings and content on the Post index page.

We’ll use this neat functionality to add a couple of additional columns to Wordpress’ standard Posts table.

There are plugins available that offer similar types of functionality, but they are limited in scope and I usually prefer to avoid plugins where functionality can be implemented using small code snippets instead. Keeps things tidy.

Adding a ‘word count’ column

Depending on the website requirements, this may or may not be a useful feature. We’ll add a count of the total number of words within an entry, counting only text within the main content WYSIWYG. If you want to include words from other content boxes, you could do so, but you’ll need to get a little more creative.

First, we insert a hook and function to add a new column header to the Posts table. We’ll call it ‘Word Count’. Let’s also insert it somewhere in the middle of the table, rather than placing it at the end. Just before Wordpress’ default ‘Tags’ column will do.

add_filter('manage_post_posts_columns', 'add_word_count_column');

/**
 * Add a new column header, 'Word Count'
 */
function add_word_count_column($columns) {

    // Add this before the 'Tags' column
    // First, let's find the index of the 'tags' key
    // from the $columns array

    $insertBeforeIndex = array_search("tags", array_keys($columns));

    $newColumns = array_slice($columns, 0, $insertBeforeIndex, true) +
    array("word_count" => "Word Count") + // insert the new column header
    array_slice($columns, $insertBeforeIndex, count($columns) - $insertBeforeIndex, true);

    return $newColumns;

}

Note: This is assuming that the $columns array is Wordpress’ default order. Other plugins or theme code may have changed the ordering of this or may even have removed the ‘Tags’ column entirely, so you may need to adjust this.

Next, another hook and another function which will perform the task of inserting the relevant data into the appropriate fields.

add_action('manage_post_posts_custom_column', 'word_count_column_content', 10, 2);

/**
 * Populate the content of our new word count column
 */
function word_count_column_content($column, $post_id) {
    $post = get_post($post_id);
    switch ( $column ) {
        case 'word_count':
            $content = $post->post_content;
            // This is a very basic "Word count" function. You might want
            // to implement something more robust
            $words = explode(" ", $content);
            echo sizeof($words);
            break;
    }
}

Listing all contributors to an article

Wordpress’ revisions functionality is a useful core feature allowing multiple users to maintain a history of changes within an article. We can use Wordpress’ internal revisions data (accessible via the wp_get_post_revisions($post_id) function) to list out all the individual contributors to an article.

Lets add a new column to our table to start us off.

First, we’ll add some code to insert a new column header, let’s call it ‘Contributors’. Let’s place that before the ‘Categories’ column. Also, let’s remove the default ‘Author’ column, as this makes it redundant.

add_filter('manage_post_posts_columns', 'add_contributors_column');

/**
 * Add a new column header, 'Word Count'
 */
function add_contributors_column($columns) {

    // Add this before the 'Tags' column
    // First, let's find the index of the 'tags' key
    // from the $columns array

    unset($columns['author']);

    $insertBeforeIndex = array_search("categories", array_keys($columns));

    $newColumns = array_slice($columns, 0, $insertBeforeIndex, true) +
    array("contributors" => "Contributors") + // insert the new column header
    array_slice($columns, $insertBeforeIndex, count($columns) - $insertBeforeIndex, true);

    return $newColumns;

}

Next, as before, the hook and function which will be responsible for populating this column’s entries. Here, we’ll grab the revision data, loop through it to extract all the individual author IDs and then finally create a comma seperate list of author profile links to display.

add_action('manage_post_posts_custom_column' ,'contributors_column_content', 10, 2 );

function contributors_column_content($column, $post_id) {

    switch ( $column ) {
        case 'contributors':
            $revisions = wp_get_post_revisions($post_id);
            $authors = array();
            if($revisions) {
                foreach($revisions as $revision):
                    $authors[] = $revision->post_author;
                endforeach;
            } else {
                // Show the main post author
                $post = get_post($post_id);
                $authors[] = $post->post_author;
            }

            // Remove duplicates
            $authors = array_unique($authors);
            $authorLinks = array();

            // Loop through again to fetch the author links
            foreach($authors as $author):
                $name = get_the_author_meta('display_name', $author);
                $authorLinks[] = sprintf("<a href='%s'>%s</a>", get_edit_user_link($author), $name);
            endforeach;

            echo implode(', ', $authorLinks);

            break;
    }
}

Here’s the end result:

Showing the additional columns within the Wordpress Post index page
The new modified Post index layout, showing the ‘Word Count’ and ‘Contributors’ columns.

The code in full (with some optimisation)

Rather than have these two columns controlled by seperate functions, it’s easier to combine the functionality and keep everything contained within two seperate hook/function combos. Here you go, the code in full:

// Add all the columns
add_filter('manage_post_posts_columns', 'modify_column_headers');

/**
 * Modify the column headers
 */
function modify_column_headers($columns) {

    // Insert the Word Count column before "Tags"
    $insertBeforeIndex = array_search("tags", array_keys($columns));

    $newColumns = array_slice($columns, 0, $insertBeforeIndex, true) +
    array("word_count" => "Word Count") + // insert the new column header
    array_slice($columns, $insertBeforeIndex, count($columns) - $insertBeforeIndex, true);

    $columns = $newColumns;

    // Remove the author column and add Contributors before Categories
    unset($columns['author']);

    $insertBeforeIndex = array_search("categories", array_keys($columns));

    $newColumns = array_slice($columns, 0, $insertBeforeIndex, true) +
    array("contributors" => "Contributors") + // insert the new column header
    array_slice($columns, $insertBeforeIndex, count($columns) - $insertBeforeIndex, true);

    return $newColumns;

}


// Process all the column data
add_action('manage_post_posts_custom_column', 'extra_column_content', 10, 2);

/**
 * Populate the content of our new columns
 */
function extra_column_content($column, $post_id) {
    $post = get_post($post_id);
    switch ( $column ) {

        case 'word_count':
            $content = $post->post_content;
            // This is a very basic "Word count" function. You might want
            // to implement something more robust
            $words = explode(" ", $content);
            echo sizeof($words);
            break;

        case 'contributors':
            $revisions = wp_get_post_revisions($post_id);
            $authors = array();
            if($revisions) {
                foreach($revisions as $revision):
                    $authors[] = $revision->post_author;
                endforeach;
            } else {
                // Show the main post author
                $post = get_post($post_id);
                $authors[] = $post->post_author;
            }

            // Remove duplicates
            $authors = array_unique($authors);
            $authorLinks = array();

            // Loop through again to fetch the author links
            foreach($authors as $author):
                $name = get_the_author_meta('display_name', $author);
                $authorLinks[] = sprintf("<a href='%s'>%s</a>", get_edit_user_link($author), $name);
            endforeach;

            echo implode(', ', $authorLinks);

            break;
    }
}

Custom columns for custom post types

You can implement this functionality for any post type, you’re not limited to the defaults. There are variations on the default hooks which can be used to get exactly the same result, but on custom post index pages.

// Here we just swap out 'post' for whatever your custom post type
// name is
$post_type = "your_custom_post_type";

add_action("manage_{$post_type}_posts_columns", "column_headings_callback");
add_action("manage_{$post_type}_posts_custom_column", "column_content_callback", 10, 2);

Follow the same approach as above, adjust for your requirements and Voila! Custom column content fun for all the family.