WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

publish_post Runs when a post is published, or if it is edited and its status is "published". Action function arguments: post ID.

-Plugin API Documentation

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?

 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 
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;
    $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.

share|improve this question
@leeand00 - Can you post your code as an update to your answer so we can be better able to see what needs correcting? – MikeSchinkel Feb 1 '11 at 5:49
@Mike Here's the update! – leeand00 Feb 1 '11 at 6:12
@leeand00 - Hey, I just noticed. You've got a 0% accept rate for answers to the 9 questions you've asked here on the site. I assume you didn't realize that you accepting answers was expected? The people who have answered your questions haven't gotten any reputation points for helping you thus far. You'll find people more likely to answer your questions if you'll go back and click the check mark next to the best answer for each of your prior questions, and that you also make sure to do the same going forward. It takes so little on your part to keep us all motivated to help you here... :) – MikeSchinkel Feb 1 '11 at 6:33
@MikeSchinkel Sometimes that just because I haven't gotten the chance to try the answers out yet, and I didn't want to mark one that's not a good answer....but I'll go back and see what I can do! – leeand00 Feb 1 '11 at 16:28
@MikeSchinkel Okay I cleaned them up and selected the best answers, sorry about that, I didn't realise how I had neglected this particular StackExchange. – leeand00 Feb 1 '11 at 16:34
up vote 14 down vote accepted
add_action('new_to_publish', 'your_function');
add_action('draft_to_publish', 'your_function');
add_action('pending_to_publish', 'your_function');
share|improve this answer
Thanks for a good example! – leeand00 Feb 1 '11 at 15:48
How about the wp_insert_post action? core.trac.wordpress.org/browser/tags/3.3.1/wp-includes/… – soulseekah Mar 23 '12 at 9:56

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.

share|improve this answer
Thanks for the link to the documentation – leeand00 Feb 1 '11 at 15:47

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.

function my_post_new($new_status, $old_status=null, $post=null){
    if ($new_status == "auto-draft"){
        // do stuff here
add_action('transition_post_status', 'my_post_new');
share|improve this answer
This will fire off every time an auto-draft is saved, i.e. every minute or so when editing. – soulseekah Mar 23 '12 at 9:54
Good point. In my case this is not an issue but it could be for others, depending on what you were doing. – PapaFreud Mar 23 '12 at 10:50
i wanted to upvote, but i can't yet. so props go here, @PapaFreud . btw, this is now documented at codex.wordpress.org/Post_Status_Transitions . again, thanks man – Ana Ban May 22 '12 at 12:30
@soulseekah Interesting point you raised, I tested this, and auto-draft is assigned as the $new_status of a post upon its creation (i.e. Add New), once only. As soon as the first AUTOSAVE procedure fires after 1 minute, the $new_status value is updated to draft. The $old_status is still NULL until the post is manually saved or published. So technically this would work and not fire each time an the editor auto-saves your work. As an additional measure you could check to ensure that the $old_status is NULL to verify -> if ($new_status == "auto-draft" && $old_status === NULL) – userabuser May 22 '13 at 13:57

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.