Since we started the Code4Lib Journal, we’ve had a persistent problem of editors accidentally publishing articles early (i.e., before the issue was officially published). Our first workaround for that was to remove the ‘publish_posts’ capability from the editor role. With that capability removed, only the administrator (i.e., me) could publish articles.
That worked, but it was a hassle, as it required me to do what should have been the coordinating editor’s job of publishing each article when it came time to publish a new issue. For that matter, even if permissions weren’t an issue, the publication process would still be cumbersome: open every article in the issue, change its status to “Published”, and save it, doing them all as fast as you can so people didn’t find a half-published issue when they came to the website.
It was to fix these and other problems that I created the Issue Manager WordPress plugin. It connects the publication status of an article to the category it’s in. You can designate a category as “Unpublished”, so that no articles in that category can be published. If you try to publish an article, it’s status changes to “Pending Review”. When it’s time to publish an issue, you change its category’s status to “Published”, which publishes all the “Pending Review” articles in that category.
How it works
Step 1, Store information
This plugin is going to keep track of which categories are published and which are unpublished. To do this, it needs a couple of records in the options
table. We tell WordPress to create these with the activation hook:
register_activation_hook( __FILE__, 'issue_manager_activation' ); function issue_manager_activation( ) { // if option records don't already exist, create them if ( !get_option( 'im_published_categories' ) ) { add_option( 'im_published_categories', array() ); } if ( !get_option( 'im_unpublished_categories' ) ) { add_option( 'im_unpublished_categories', array() ); } }
Step 2, Prevent early publication
Now that we have a list of published and unpublished categories, we can keep editors from publishing articles they should not. We add an action to run whenever a post is published:
add_action('publish_post', 'issue_manager_publish_intercept');
If the post is in one of the unpublished categories, change its status to ‘pending’ rather than ‘publish’:
function issue_manager_publish_intercept( $post_ID ) { $unpublished = get_option( 'im_unpublished_categories' ); $publishable = TRUE; // check if post is in an unpublished category foreach ( get_the_category($post_ID) as $cat ) { if ( in_array( $cat->cat_ID, $unpublished ) ) { $publishable = FALSE; break; } } // if post is in an unpublished category, change its status to 'pending' instead of 'publish' if ( !$publishable ) { wp_update_post( array( 'ID' => $post_ID, 'post_status' => 'pending' ) ); } }
Step 3, Add an admin page
That’s all well and good, but if its to be useful, an editor needs to be able to add categories to the list of published or unpublished categories. To do this, we need an administration page.
Adding pages to the WordPress administration sections is fairly easy, following the instructions in the Codex. The relevant code:
add_action('admin_menu', 'issue_manager_manage_page');
This tells WP to use the issue_manager_manage_page
function to add a page to the admin menu. And the function itself:
function issue_manager_manage_page( ) { if ( function_exists('add_management_page') ) { add_management_page( 'Manage Issues', 'Issues', 'publish_posts', 'manage-issues', 'issue_manager_admin' ); } }
add_management_page()
adds a sub-menu item to the “Manage” menu. Users have to have the “publish_posts” capability to access it, and when you visit the page, the function issue_manager_admin()
is called to build the contents of the page and perform other actions.
issue_manager_admin()
includes a separate file (im_admin_main.php
) where the HTML of the management page lives. That page needs variables containing the list of all categories and the lists of published and unpublished categories. The former can be retrieved using get_categories()
, making sure to pass it the parameter hide_empty=0
so you get all the categories, not just those with published posts.
This page contains a pretty simple table, containing a row for each category, with a links to publish that category, to unpublish it, or for the plugin to ignore that category entirely. Each link contains three parameters:
page
- Matches the 4th parameter to
add_management_page()
, telling WordPress to send the request back to this same page cat_ID
- The ID of the category in question
action
- One of three actions (‘publish’, ‘unpublish’, or ‘ignore’) to tell
issue_manager_admin()
what to do with that category
A brief note about the HTML for the admin page: to take advantage of the WordPress stylesheets already in place, make sure to wrap your HTML in <div class="wrap"></div>
and give your table the class “widefat”.
Step 4, Make the admin page do stuff
So, what happens when you click on one of the links?
If you click “Ignore”, the plugin should just stop worrying about that category. It takes the category ID out of the im_published_categories
or im_unpublished_categories
options, and then leaves well enough alone.
“Unpublish” adds the category ID to the im_unpublished_categories
option, of course, and then sets out to unpublish any published post in that category. get_posts( "numberposts=-1&post_status=publish&category=$cat_ID" )
will give us an array containing all the published posts in the given category. We simply change the status on those posts to ‘pending’:
wp_update_post( array( 'ID' => $post->ID, 'post_status' => 'pending' ) );
“Publish” goes the opposite direction. The category ID moves to the im_published_categories
option, and we publish all the pending posts in that category. get_posts( "numberposts=-1&post_status=pending&category=$cat_ID" );
gives us that list of posts. We want to do things a bit differently, when updating the posts, so that all the posts will be published now, rather than whenever the timestamp was last set:
$counter = 0; foreach ( $posts as $post ) { wp_update_post( array( 'ID' => $post->ID, 'post_date' => date( 'Y-m-d H:i:s', strtotime(current_time('mysql'))-($counter*60) ) ) ); wp_publish_post($post->ID); $counter++; }
We update the post_date to the current time, but make sure to put a minute gap between each post so that the order is controllable. Whatever order the pending posts were in, they’ll keep that order when they’re published; their timestamps will just be updated to about now. We use wp_publish_post
rather than updating the post_status to ‘publish’, so that WordPress can do everything else it needs to do when publishing a post (including the check we instituted in Step 2, in case a post is in multiple categories, some published, some unpublished).
Another brief note: depending on how PHP is configured on your system, time()
may or may not return GMT. This affects the value returned by WordPress’s current_time()
, which should give the current time, taking into account the offset you set on the general settings page. If you call current_time('mysql')
, that works. If you use current_time('timestamp')
, though, the function expects time()
to give it GMT, which may not work. Which is why current_time('mysql')
is converted to a timestamp, rather than just calling current_time('timestamp')
to get a timestamp in the first place.
Future development
The next major feature this needs would be an easier way to adjust the order of published articles. Right now, you have to set the timestamp on them in advance, which still requires opening up each post before you can publish the issue. Ideally, one would click “Publish” and be presented with a list of posts about to be published, with the ability to re-order those posts appropriately.
Download the plugin
As soon as I get this into WordPress SVN, I’ll provide a link to the plugin page. In the meantime, the code is available at the Code4Lib Journal’s Google Code page.
UPDATE (2008-09-30): Now available from the WordPress Plugin Directory.
UPDATE (2008-10-13): Version 1.2 now available. You can now sort the posts as you publish them. Next version will allow you to set a different timestamp, to enable post- or pre-dating entire issues.
UPDATE (2008-10-28): Version 1.3 now available, with timestamp editing capabilities. I can’t think of any more features to add/modify; user feedback is welcome.
UPDATE (2008-12-18): Version 1.4 now available. Version 1.4 should work with WordPress 2.7.x. If you’re still using WP 2.6.x, don’t upgrade; continue using version 1.3.
Our site runs on WP as well, so will check it out, thanks
This is awesome. We should make sure to plug it/thank you in the intro to the next c4l journal issue.
I don’t know if you’ve thought of this, but I wonder if there is anyway for the category to be removed from posts when they are published through Issue Manager. For instance, I am using a “private” category just to organize the administration of publishing issues of a newsletter once a week. I don’t want to create a new category every week but rather want the category to function like a status (draft, pending review, scheduled etc.). So, I recycle the same category week to week. This requires that I go in and remove the category I use to publish the issue from each post after it’s published. It would be nice if I didn’t have to do this and if the status of that category would toggle back to “ignored” in issue manager once it publishes.
Kevin, that’s probably a bit beyond the scope of the Issue Manager plugin, but I can envision a plugin that piggy-backs on it to do what you want to do. You would create a function that ties into the
publish_post
action hook. When a post is published, the function would just remove the category you want removed. After you’ve published your issue, it would then just be one more click to ignore that category again.It has long been looking for this information, Thank you for your work.
Greetings,
I just downloaded this plugin and tried to use it with WordPress 2.7. It appears that I cannot actually publish an issue. Every time I click Publish and Publish Issue, nothing happens other than the page reloading to the Issues page. The issue never gets published and the status remains at either Unpublished or Ignore depending on what the status was prior.
This plugin is exactly what I need for the new site I am building. Any help would be greatly appreciated.
Best,
Joe Dakroub
hi. Thanks for plugin
perfect.
Regards
i tried the plugin, but it not only didnt work, but some of the files on my desktop got corrupted, has anyone else had this, my norton anti-virus has expred though, i am going to re-install it, and try again.
I have found a small bug in Issue Manager with WordPress 2.8.3+.
In im_article_list.php on line 23:
needs to be changed to:
This will allow the publish box to display again.
Thanks,
Bill
Lets try that again:
Add:
class=”metabox-holder has-right-sidebar”
into the div tag on line 23.
Thanks, Bill. I’ve added the class in version 1.4.3.