publish_post Runs when a post is
published, or if it is edited and its
status is “published”. Action function
arguments: post ID.
I’ve added the publish_post hook to a WordPress plugin that I’m writing. The function called by the hook itself, is meant to change the categories of several posts using the wp_update_post function.
This hook does not work however as the result returned from running wp_update_post is always 0. My best guess is that running wp_update_post causes another instance of my hook to run because it re-publishes the post…which I believe brings about the “…or if it is edited and its status is “published”” of the statement above.
Is there any other action-hook that I can use that will only be called when the post added is completely new and not edited?
<?php
/*
Plugin Name: Category Switcher Plugin
Plugin URI: http://www.example.com
Description: When a new post is created this plugin will cause the
Version: 0.1
Author: Me
License: GPL2
?>
<?php
class categoryShifter {
function shiftCategories($post_ID) {
$maxNumPostsFirstTeir = 4;
$first_teir_cat = "Fresh News Stories 1";
$second_teir_cat = "Slightly Dated Stories 2";
$firephp = FirePHP::getInstance(true);
$firephp->info('BEGIN: categoryShifter.shiftCategories()');
$firephp->log($post_ID, 'post_ID: ');
$firephp->trace('trace to here');
$first_teir_id = categoryShifter::getIDForCategory($first_teir_cat, $firephp);
$second_teir_id = categoryShifter::getIDForCategory($second_teir_cat, $firephp);
$firephp->log($first_teir_id, '$first_teir_id');
$firephp->log($second_teir_id, '$second_teir_id');
$qPostArgs = array(
'numberposts' => 100,
'order' => 'DESC',
'orderby' => 'post_date',
'post_type' => 'post',
'post_status' => 'published',
'category_name' => $first_teir_cat
);
$firstTeirPosts = get_posts($qPostArgs);
$firephp->log($firstTeirPosts, 'got posts:');
$firephp->log(sizeof($firstTeirPosts), 'sizeof');
// NOTE: This appears to work.
for($i = sizeof($firstTeirPosts)-1; $i > $maxNumPostsFirstTeir-4; $i--)
{
$newCats = array($second_teir_id);
$editingId = $firstTeirPosts->ID;
$result = wp_set_post_categories($editingId, $newCats); /* NOTE: Doesn't work presently... returns an array with the $second_teir_id in it. */
$firephp->log($result, 'Result');
}
/*
$my_post = array();
$my_post['ID'] = 132;
$my_post['post_category'] = array($second_teir_id);
$firephp->log('Before', 'Before');
if(wp_update_post( $my_post ) == 0) {
$firephp->Error('Fatal Error, Post not updated', 'error');
}
$firephp->log('After', 'After');
*/
return $post_ID;
}
function getIDForCategory($cat_name, $logger) {
$logger->Info("Begin: getIDForCategory()");
$cats = get_categories();
$whichCatId = "";
foreach($cats as $single_cat) {
if($single_cat->name == $cat_name) {
$whichCatId = $single_cat->term_id;
break;
}
}
$logger->Info("End: getIDForCategory()");
return (int)$whichCatId;
}
}
/* Hook Post Creation */
/* add_action('publish_post', array('categoryShifter','shiftCategories')); */
add_action('wp_insert_post', array('categoryShifter', 'shiftCategories'));
?>
I’ve switched to using the wp_insert_post hook for the time being…but I still can’t get the wp_set_post_categories function to change the categories of the posts.
I understand that I will probably need to update this code so that it takes into account the existing categories of the post and only modifies the ones specified by the plugin, but for now it’s really just an alpha.
I have extensively read the WordPresss core and I have tried it all.
wp_transition_post_status()
,new_to_publish()
,new_{post_type}()
,wp_insert_post()
.All of these are, after all, unreliable.
wp_transition_post_status()
isn’t reliable because the new status “publish” is the default status both for creating new posts and updating existing ones. Old status isn’t reliable to define what’s a new post and what’s not because it can be draft, auto-draft, publish, etc.new_to_publish()
doesn’t work for custom post types.new_{post_type}
only passes $post as parameter, and you can’t know if it’s new or updating existing onewp_insert_post()
have a $update parameter that should be TRUE if updating existing posts and FALSE if creating new posts, but it’s unreliable because it returns TRUE for new posts because of auto-draft.Solution: Using a post meta
I ended up using a custom post meta that I called
check_if_run_once
that will execute some logic only one time:Optionally, if you need to update your existing posts with the “check_if_run_once” meta, so it don’t run the code above for existing posts created before you added that function, you could do:
Precisely target creation of new post is actually more tricky than it seems. Technically there are multiple ways post can get created or updated and there are plenty not so obvious things that are technically posts as well (revisions for example).
WordPress provides dynamic hooks that track not only post creation, but what it was and what it became. See Post Status Transitions in Codex.
More using experimenting than following the docs, this works for me (WP 3.3). I get a transition_post_status hook call with $new_status set to “auto-draft” when you create a new post.
I found the best option was to check the post status when
wp_insert_post
is called.When a new post is created, the initial status is
auto-draft
.This error_log will only ever fire once.
The most clean and reliable way to handle this for a known post type is to use the
{$new_status}_{$post->post_type}
hook which provides the old status and compare it to “publish”.For example, if you wanted to fire when a new “page” is created you would use: