Set post to draft after set period based on post_modified date

I have a custom post type for the user profile page. My client wants all users to update their profile at least once a month, so we want their profile (custom post) to be automatically set to draft after 30 days. They will then receive an email asking them to update the post. The post needs to be set to draft again 30 days after the last modified date and another reminder sent out, etc.

So far I have this code to expire the posts after 30 days of the published date, which doesn’t seem to work. I assume I am missing something regarding the cron scheduling:

Read More
if ( ! wp_next_scheduled( 'run_post_expiration' ) ) {
    wp_schedule_event( time(), 'hourly', 'run_post_expiration' );
}    
add_action( 'run_post_expiration', 'expire_posts' );

function expire_posts() {
    global $wpdb;
    $daystogo = "30";
    $sql = "UPDATE wp_posts 
        SET `post_status` = 'draft' 
        WHERE `post_type` = 'profile' 
        AND DATEDIFF(NOW(), `the_date`) > '$daystogo')";
    $wpdb->query($sql);
}
add_action('init', 'expire_posts');

I have this code to set the post_date to the post_modified date, therefore enabling the code above to still work every 30 days:

function reset_post_date_wpse_121565($data,$postarr) {
    $data['post_date'] = $data['post_modified'];
    $data['post_date_gmt'] = $data['post_modified_gmt'];      
    global $post;
    if ($post->post_type == 'profile'){
        return $data;
    } 
}
add_filter('wp_insert_post_data','reset_post_date_wpse_121565',99,2);

The above code to modify the post_date does actually work, but it is also causing some problems with other post types which I wasn’t expecting, so I think there may be an error somewhere.

Can anybody help me to get this to work? Or am I even going about this in the right way? Perhaps there is a better solution. I have tried the “Post Expirator” plugin, but this only works with the post_date; once someone has updated their profile once, it no longer works.

EDIT:

I edited the original code to set the posts to draft to this (based on some code I found elsewhere):

add_action( 'my_trash_hook', 'my_trash_hook_function' );
if (!wp_next_scheduled('my_trash_hook')) {
    wp_schedule_event( time(), 'hourly', 'my_trash_hook' );
}

function my_trash_hook_function() {
    global $wpdb;
    $daystogo = "1";
    $sql = "UPDATE {$wpdb->posts}
        SET 'post_status' = 'draft'
        WHERE 'post_type' = 'tests'
        AND DATEDIFF(NOW(), 'the_date') > '$daystogo')";
    $wpdb->query($sql);
}

I have another function set up to send me an email when the post is set to draft and another to send me an email when the post is published again.

What is happening is that when I log in to the site I am sent an email saying the post has been published, but not one saying the post was set to draft. Not sure if the two pieces of code are conflicting with each other or just plain wrong.

Related posts

2 comments

  1. Don’t use global $post. Everything you need should be passed into the function in the $data and $postarr parameters.

    Secondly, you have to return the $data in all cases. What you are doing now is effectively erasing data for everything but your profile post type.

    You need to reorganize a bit:

    function reset_post_date_wpse_121565($data,$postarr) {
      if ($data['post_type'] == 'profile'){
        $data['post_date'] = $data['post_modified'];
        $data['post_date_gmt'] = $data['post_modified_gmt'];
      } 
      return $data;
    }
    add_filter('wp_insert_post_data','reset_post_date_wpse_121565',99,2);
    

    I have not tested that thoroughly but that is the right idea.

    As for a better way…

    Your expire_posts function is not going to be reliably portable as it uses a hard-coded database prefix. At the very least, rewrite that SQL so that the prefix is not hard-coded:

    function expire_posts() {
      global $wpdb;
      $daystogo = "30";
      $sql = "UPDATE {$wpdb->posts} SET `post_status` = 'draft' WHERE `post_type` = 'profile' AND DATEDIFF(NOW(), `post_date`) > '$daystogo')";
      $wpdb->query($sql);
    }
    add_action('init', 'expire_posts');
    

    I also just notices that your WHERE clause has a column that doesn’t exist. There is no the_date column in the $wpdb->posts table. It should be post_date.

    Honestly, if you were to deal with the modified date only, you should be able to get this to work without ever altering the post date at all. I don’t think you need that second block of code at all.

  2. Quick correction on s_ha_dum’s excellent post: There was a closing bracket at the end of the $sql statement that shouldn’t be there. This worked for me:

    function expire_posts() {
        global $wpdb;
        $daystogo = "12";
        $sql = "UPDATE {$wpdb->posts} SET `post_status` = 'draft' WHERE `post_type` = 'post' AND `post_status` = 'publish' AND DATEDIFF(NOW(), `post_date`) > $daystogo";
        $wpdb->query($sql);
    }
    add_action('init', 'expire_posts');
    

Comments are closed.