Adding content above and below the Wordpress wp-admin `All Posts` table

A recent project I worked on required an extension to the Wordpress ‘All Posts’ index listings page to display small amounts of content above and below the default table view.

New administration headings within the dashboard for Wordpress
Here’s what we’re trying to achieve. Notice the new notification boxes above and below the standard ‘All Posts’ index listing.

But, as I outlined in an earlier post, Wordpress doesn’t offer much in the way of customisation hooks when it comes to the layout and appearance of the admin index pages (as opposed to the post edit pages, which offer a lot of customisation potential)

To get around this, I had to get a little more technical and make use of a Javascript-based solution. Definitely not ideal, but since we cannot make any adjustments to the core administration files, that’s pretty much all we can do.

If you want to skip to the chase, you can grab the plugin files by clicking on the button below. There’s some additional configuration required, which is outlined at the end of this article.

Download Plugin .zip

Note: This will allow for content panels to appear for users with javascript enabled. Non-javascript users will not see this content, however, so don’t rely on it for any mission critical dialogs. Instead, it may be utilised to present content which complements the standard admin view.

The Javascript solution

The javascript for this is relatively straightforward. I decided to hook onto two seperate elements, the “status” bar at the top (which reads something like All (2) | Published (2) which I’ll place content before and the .tablenav.bottom element, which is a decent hook to place some content after.

The code is inserted directly into the <head> element of the admin pages, typically the simplest way of adding extra scripting/styling to the administration area.

This ends up reading a little something like this:

var aboveContainer = document.createElement("div");
var belowContainer = document.createElement("div");

aboveContainer.classList.add('custom-admin-table-content', 'above');
belowContainer.classList.add('custom-admin-table-content', 'below');

// These templates are generated from the output of the PHP template files, placed earlier in the <head> output
var aboveTemplate = document.querySelector('template[name=above]');
var belowTemplate = document.querySelector('template[name=below]');

// If the templates exist, apply their contents to the new
// container elements
if(aboveTemplate) {
    aboveContainer.innerHTML = aboveTemplate.innerHTML;
}

if(belowTemplate) {
    belowContainer.innerHTML = belowTemplate.innerHTML;
}

// Insert a 0 timeout to cater for initial JS initialization
setTimeout(function(){

    if(aboveTemplate) {
        // .subsubsub is the class of the container holding the All(123) | Published(2) | Trash(5) status
        var targetAbove = document.querySelector(".subsubsub");                     
        if(targetAbove) {
            // Insert the "above" container just before .subsubsub
            targetAbove.parentNode.insertBefore(aboveContainer, targetAbove.previousSibling);
        }
    }

    if(belowTemplate) {
        // We'll plop the belowContainer right after the bottom navigation element
        // of the table
        var targetBelow = document.querySelector('.tablenav.bottom');
        if(targetBelow) {
            targetBelow.parentNode.appendChild(belowContainer);
        }
    }

}, 0)

Freestylin’

Because we’re adding some custom containers, I applied some additional styling rules. Again, these have been dropped straight into the admin <head> via the admin_head hook.

<style>
    .custom-admin-table-content {
        border: 1px solid #e1e1e1;
        color: #32373c;
        background: #FFF;
        border-left: 4px solid #888;
        padding: 0 12px;
    }
    /* for the situation where there are no additional
       notification boxes above */
    .wp-header-end + .custom-admin-table-content {
        margin-top: 1em;
    }
    .custom-admin-table-content p {
        margin: 0.5em 0;
        padding: 2px;
    }
    .custom-admin-table-content.below {
        margin-top: 1em;
    }
</style>

I’ve added a below and above class to each respective container, so you can adjust the styles as you see fit.

Defining the content

I wanted to create a robust solution, so I allowed for the content to be defined within PHP files located inside the currently active theme directory. That way, you can apply any content changes directly to your template files, and keep everything safely tucked away in version control.

To define the content, I’ve set up this code so that you can create files inside a /admin-above-below directory within your theme. Inside this folder, place the files post-admin-above.php and post-admin-below.php

Here’s a couple of sample files you can make use of initially:

Above the table

I’ve put a couple of bits of functionality in here,

  1. Grab the current user’s name, and give them a nice little welcome.
  2. Locate all posts by the current user. If they’ve already made some posts, let them know when their last post was added, and prompt them to add another. If not, just ask them to make a new one.

Extra content above the table

{your_theme_folder}/above-below-admin-tables/post-above-all.php

// Get the logged in user's user name
$user = wp_get_current_user();
$name = $user->data->display_name;

$post_type = isset($_GET['post_type']) ? $_GET['post_type'] : 'post';

// Check to see what posts this user has written
$args = array(
    'author' => $user->ID, // I could also use $user_ID, right?
    'orderby' => 'post_date',
    'post_type' => $post_type,
    'order' => 'DESC',
    'numberposts' => 1
);

$posts = get_posts($args);

// Show a different message depending on whether they've already contributed or not
if($posts):
    $editMessage = sprintf("Welcome back! Your last post was <a href='%s'>%s</a>, created %s ago",
                                get_edit_post_link($posts[0]->ID),
                                $posts[0]->post_title,
                                human_time_diff(time(), strtotime($posts[0]->post_date)));
else:
    $editMessage = "Get started by creating a <a href=''>new post</a>";
endif;

printf("<p>Hi, %s. %s</p>", $name, $editMessage);

Below the table

Underneath the posts table, a snippet of code that lets the user know how many revisions have been made in the last 7 days. Useful? Not useful? Who can say. But there we go.

Extra content below the table

{your_theme_folder}/above-below-admin-tables/post-below-all.php

$post_type = isset($_GET['post_type']) ? $_GET['post_type'] : 'post';

global $wpdb;

// Returns all 'revision' posts within the last 7 days of a given post type
$sql = <<<SQL
    SELECT DISTINCT P0.ID, P1.post_type FROM
    {$wpdb->prefix}posts as P0
    LEFT JOIN {$wpdb->prefix}posts as P1 ON (P0.post_parent = P1.ID)
    WHERE P0.post_type = 'revision'
    AND P1.post_type = '%s'
    AND P1.post_name NOT LIKE '%-autosave-%'
    AND P1.post_status != 'trash'
    AND P0.post_modified >= DATE(NOW()) - INTERVAL 7 DAY
SQL;

$results = $wpdb->get_results($wpdb->prepare($sql, $post_type));

$count = sizeof($results);

printf("<p>There have been <b>%d %s</b> in the past 7 days</p>", $count, $count == 1 ? 'revision' : 'revisions');

Download the plugin

Here’s the code in all its glory. I’ve encased it within a plugin, so you can install it directly via the Wordpress plugins panel, or grab the code straight from the files and add to your functions.php

There’s some additional code to check what the current screen is, display the containers only for the specific post types as defined by the filenames of your content files within the \above-and-below-admin-tables folder and some other minor tweaks.

There are two content files already defined within the plugin folder. You can use these by copying them into the {your_theme_folder}/above-below-admin-tables/ folder.

Download Plugin .zip

Installation

  1. Copy the folder within the .zip archive into your /plugins folder, then activate via the Wordpress admin Plugins page.
  2. To set up the initial themes, copy the template files from within the plugin’s /templates directory and place them within a folder named above-below-admin-tables in your theme directory
  3. To create additional content boxes for other custom post types, use the filname format {$post_name}-above-all.php and {$post_name}-below-all.php