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.
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.
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 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)
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.
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:
I’ve put a couple of bits of functionality in here,
{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);
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.
{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');
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.
/plugins
folder, then activate via the WordPress admin Plugins page./templates
directory and place them within a folder named above-below-admin-tables
in your theme directory{$post_name}-above-all.php
and {$post_name}-below-all.php
Robin is the dedicated developer behind Solarise.dev. With years of experience in web development, he's committed to delivering high-quality solutions and ensuring websites run smoothly. Always eager to learn and adapt to the ever-changing digital landscape, Robin believes in the power of collaboration and sharing knowledge. Outside of coding, he enjoys diving into tech news and exploring new tools in the industry.
If you'd like to get in touch to discuss a project, or if you'd just like to ask a question, fill in the form below.
Send me a message and I'll get back to you as soon as possible. Ask me about anything - web development, project proposals or just say hi!