I have a WordPress site with 1000s of posts. I'd like to set all posts older than 30 days to draft automatically when it reaches the specified date.
Been staring myself blind at the following code - triggering the CRON manually has now effect:
<?php
/**
* Function that will draft specific posts on specific conditions
*
* #param \WP_Post $_post
*/
function tgs_maybe_draft_the_post( $_post ) {
$publish_date = get_the_date( 'd M Y', $_post->ID);
// Bail if no publish date set for some reason.
if ( ! $publish_date ) {
return;
}
// Set status to draft if older than 30 days.
if (strtotime($publish_date) < strtotime('-30 days')) {
wp_update_post( array(
'ID' => $_post->ID,
'post_status' => 'draft'
) );
}
}
/**
* Register cron event on init action
*/
function tgs_cron_schedule_draft_posts() {
$timestamp = wp_next_scheduled( 'tgs_draft_posts' );
if ( $timestamp == false ) {
wp_schedule_event( time(), 'hourly', 'tgs_draft_posts' );
}
}
add_action( 'init', 'tgs_cron_schedule_draft_posts' );
/**
* Handle deletion of posts periodically.
* - Loop through the posts and call the tgs_maybe_draft_the_post function.
*/
function tgs_draft_posts_handler() {
$posts = get_posts( array(
'posts_per_page' => - 1,
'post_type' => 'post',
'post_status' => 'publish',
'suppress_filters' => true,
) );
foreach ( $posts as $_post ) {
tgs_maybe_draft_the_post( $_post );
}
}
add_action( 'tgs_draft_posts', 'tgs_draft_posts_handler' );
What am I doing wrong?
You can troubleshoot your logic (change status to draft) by running that logic directly during a page view rather than from cron. Try using the wp_footer action, something like this.
add_action( 'wp_footer', 'tgs_draft_posts_handler')
You can then include print_r(); debugging code and it will show up on your rendered page: ugly but useful. You could do print_r('about to do get_posts'); for example.
You can search for posts with date criteria. That way you don't need a separate check for the posts' age. With thousands of posts this is a significant time saver.
$posts = get_posts( array(
'posts_per_page' => - 1,
'post_type' => 'post',
'post_status' => 'publish',
'suppress_filters' => true,
'date_query' => array(
array(
'column' => 'post_date_gmt',
'before' => '30 days ago'
)
),
) );
Once your basic logic works, you can get it working under cron.
And, turn on the WordPress debugging stuff. It helps a lot.
When it all works, don't forget to remove the print_r() statements (duh, obviously).
Related
I'm stuck on something that I'm sure must be pretty simple but is getting me nuts. I'm forced at work to use WordPress, which I have zero experience with, and I'm struggling so far to understand how it operates regarding hooks and filters.
What I want is pretty simple:I'm using the latest posts block to display posts written by users. Except that the page I'm working on would be the front end for the website's moderator who would have to see the posts with the 'pending' status, not the 'publish' one. I couldn't find any option to change that in the editor, so I'm trying to set a hook to change the query from 'post_status' => 'publish' to 'post_status' => 'pending', but it's not working, I get a 'Oops! That page can’t be found.'
Here's what I wrote in my functions.php:
function name_of_the_function( $query ) {
if( get_query_var('pagename') == 'name_of_the_page' && current_user_can('publish_posts') && $query->is_main_query() ) {
$query->set( 'post_status', 'pending' );
return $query;
}
}
add_filter( 'pre_get_posts', 'name_of_the_function' );
If I leave this function exactly like that but write 'publish' instead of 'pending' the page displays correctly the last published posts, but with 'pending' I get the message I mentioned before. And I tried with add_action instead of add_filter and got hte same results.
I'd like to add that I do have pending posts awaiting, and if I write the following in my page template, they are found:
$args = array (
'cat' => 5,
'post_status' => 'pending'
);
$query = new WP_Query( $args );
while ( $query->have_posts() ) {
$query->the_post();
echo get_the_title();
}
Just to check, directly in the wp-includes/latest-posts.php file, I changed :
$args = array(
'posts_per_page' => $attributes['postsToShow'],
'post_status' => 'publish',
'order' => $attributes['order'],
'orderby' => $attributes['orderBy'],
'suppress_filters' => false,
);
to :
$args = array(
'posts_per_page' => $attributes['postsToShow'],
'post_status' => 'pending',
'order' => $attributes['order'],
'orderby' => $attributes['orderBy'],
'suppress_filters' => false,
);
It works and displays the pending posts but of course I can't use that as the file would be erased at every WordPress update.
Sorry for the long post but I'm lost now and don't know what else to do, I've looked all other the intrnet but can't find an answer to this, I would really appreciate any help regarding that matter, thanks in advance.
By using the pre_get_posts with $query->is_main_query(), you apply this to the query that WordPress uses to find the page (the main query). Your code needs to be changed to:
function name_of_the_function( $query ) {
if ( is_admin() || $query->is_main_query() ) {
return $query;
}
if( get_query_var('pagename') == 'name_of_the_page' && current_user_can('publish_posts') ) {
$query->set( 'post_status', 'pending' );
}
return $query;
}
add_filter( 'pre_get_posts', 'name_of_the_function' );
So basically, don't run this on any query in the admin or any query that is the main query, but only run it on a specific page for people with specific capabilities.
I'm trying to apply a php code snippet to my wordpress page to enable Jetpack to limit its related posts to no later than 2017-01-01. However, when I add this code via Snippets - with Jetpack enabled to show thumbnails - Jetpack doesn't work. Is my code incorrect or is there some magic trick for this?
// related posts - don't show posts earlier than January 1 2017
function jetpackme_exclude_related_post($exclude_post_ids, $post_id ) {
$args = array(
'date_query' => array(
'before' => 2017-01-01,
'inclusive' => true,
),
'posts_per_page' => '99999',
'fields' => 'ids'
);
$query = new WP_Query($args);
if ($query->have_posts()):
foreach($query->posts as $id ):
$exclude_post_ids[] = $id;
endforeach;
endif;
return $exclude_post_ids;
}
add_filter( 'jetpack_relatedposts_filter_exclude_post_ids',
'jetpackme_exclude_related_post', 20, 2 );
I want to trash (not force delete) a Custom Post Type after 30 days.
To do this, I've found a nice solution from #pieter-goosen to delete posts after a number of days: https://wordpress.stackexchange.com/questions/209046/delete-expired-posts-after-a-number-of-days-after-they-expired
My problem is, that the function deletes all the posts of this Custom Post Type and doesn't use the trash.
My code looks like this:
function get_exired_posts_to_delete()
{
/**
* If you need posts that expired more than a week ago, we would need to
* get the unix time stamp of the day a week ago. You can adjust the relative
* date and time formats as needed.
* #see http://php.net/manual/en/function.strtotime.php
* #see http://php.net/manual/en/datetime.formats.php
*/
// As example, we need to get posts that has expired more than 7days ago
$past = strtotime( "- 1 week" );
// Set our query arguments
$args = [
'fields' => 'ids', // Only get post ID's to improve performance
'post_type' => 'job',
'post_status' => 'publish',
'posts_per_page' => -1,
'date_query' => array(
'column' => 'post_date_gmt',
'before' => '30 days'
)
];
$q = get_posts( $args );
// Check if we have posts to delete, if not, return false
if ( !$q )
return false;
// OK, we have posts to delete, lets delete them
foreach ( $q as $id )
wp_delete_post( $id );
}
// expired_post_delete hook fires when the Cron is executed
add_action( 'expired_post_delete', 'get_exired_posts_to_delete' );
// Add function to register event to wp
add_action( 'wp', 'register_daily_post_delete_event');
function register_daily_post_delete_event() {
// Make sure this event hasn't been scheduled
if( !wp_next_scheduled( 'expired_post_delete' ) ) {
// Schedule the event
wp_schedule_event( time(), 'daily', 'expired_post_delete' );
}
}
Is there anythin wrong with the date query?
And is there a better solution to use the server cron instead the WP cron?
I found a solution for my question.
For the problem with the trash, I've changed the argument wp_delete_post() to wp_trash_post() because wp_delete_post() only applies to native posts, pages, and attachments. Great answer from #rarst here: https://wordpress.stackexchange.com/questions/281877/error-after-deleting-custom-post-type-with-a-function-no-trash-used/281888#281888
Here is my code:
function get_delete_old_jobs() {
// Set our query arguments
$args = [
'fields' => 'ids', // Only get post ID's to improve performance
'post_type' => 'job',
'post_status' => 'publish',
'posts_per_page' => -1,
'date_query' => array(
'before' => date('Y-m-d', strtotime('-30 days'))
)
];
$q = get_posts( $args );
// Check if we have posts to delete, if not, return false
if ( !$q )
return false;
// OK, we have posts to delete, lets delete them
foreach ( $q as $id )
wp_trash_post( $id );
}
// expired_post_delete hook fires when the Cron is executed
add_action( 'old_job_delete', 'get_delete_old_jobs' );
// Add function to register event to wp
add_action( 'wp', 'register_daily_jobs_delete_event');
function register_daily_jobs_delete_event() {
// Make sure this event hasn't been scheduled
if( !wp_next_scheduled( 'old_job_delete' ) ) {
// Schedule the event
wp_schedule_event( time(), 'hourly', 'old_job_delete' );
}
}
The following code runs inside of the Facebook Instant Articles plugin by Automattic. It is the query used to generate the RSS feed that facebook pulls for instant articles.
function instant_articles_query( $query ) {
if ( $query->is_main_query() && $query->is_feed( INSTANT_ARTICLES_SLUG ) ) {
$query->set( 'orderby', 'modified' );
$query->set( 'posts_per_page', 100 );
$query->set( 'posts_per_rss', 100 );
$settings_categories = Instant_Articles_Option_Categories::get_option_decoded();
if($settings_categories['categories'] !== '') {
$query->set( 'cat', $settings_categories['categories'] );
}
/**
* If the constant INSTANT_ARTICLES_LIMIT_POSTS is set to true, we will limit the feed
* to only include posts which are modified within the last 24 hours.
* Facebook will initially need 100 posts to pass the review, but will only update
* already imported articles if they are modified within the last 24 hours.
*/
if ( defined( 'INSTANT_ARTICLES_LIMIT_POSTS' ) && INSTANT_ARTICLES_LIMIT_POSTS ) {
$query->set( 'date_query', array(
array(
'column' => 'post_modified',
'after' => '1 day ago',
),
) );
}
}
}
add_action( 'pre_get_posts', 'instant_articles_query', 10, 1 );
I need to modify the query that this runs to take a meta query but I need to do so outside of the plugin file. I know that hooks are the way to go but is it possible or correct to do the following?
function mod_ia_query( $query ) {
$query->set( 'meta_query', array(
array(
'key' => 'is_instant_article',
'value' => true
),
);
}
add_action('instant_articles_query','mod_ia_query', 10, 1);
I'm coming back to update this but essentially what I did was create a subdirectory in my theme with the name of the facebook instant articles plugin and create a class that extends the Instant_Articles Class.
I unhooked the add_action( 'pre_get_posts', 'instant_articles_query', 10, 1 );
And added my own method to the hook, this performs my functionality within the context of the plugin but without directly editing core plugin files.
I have some trouble with the wp_schedule_event function and I was not able to find any solution here so far.
The event is triggert, atleast I see the next planned when calling wp_next_scheduled( 'update_expired_events_hourly' ), but when the time is reached, nothing is happening.
Also the function is working fine when I call it manually. I tried to fire the event by calling /wp-cron.php?doing_wp_cron, but that had no effect either.
The following code is in my themes functions.php
Any suggestions how to fix that?
Thank you in advance!
add_action( 'wp', 'expired_activation' );
add_action( 'update_expired_events_hourly', 'update_expired_events' );
function expired_activation(){
if ( !wp_next_scheduled( 'update_expired_events_hourly' ) ) {
wp_schedule_event(time(), 'hourly', 'update_expired_events_hourly');
}
}
function update_expired_events(){
// events query
$args = array(
'post_type' => 'espresso_events',
'post_status' => array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash'),
'post_per_page' => -1,
'tax_query' => array(
array(
'taxonomy' => 'espresso_event_categories',
'field' => 'slug',
'terms' => 'ee_exp', // the expired category slug
'operator' => 'NOT IN',
),
)
);
$events = new WP_QUERY($args);
$today = strtotime(current_time( "Y-m-d H:i:s", $gmt =0 )); // get current date
if ( $events->have_posts() ){
while ( $events->have_posts() ){
$events->the_post();
//get event end date
$event_end = strtotime( espresso_event_end_date( 'Y-m-d', 'H:i:s', false, false ) ); // get event end date
if( $event_end - (60*60*24) < $today ){
wp_set_object_terms( get_the_ID(), 'ee_exp', 'espresso_event_categories', true); // Add post to expired category
}
}
}
}
I forgot to mention, that define('DISABLE_WP_CRON'); is set to false in my config.php
---------------------- EDIT ----------------------
The code above is working. Apparently the only thing not working was firing the event via URL (/wp-cron.php?doing_wp_cron). Sorry for that and thx for your help!
I think you shouldn't disable wp_cron I suggest you use wp_schedule_single_event it worked well for me
wp_schedule_single_event( time() + 3600, 'update_expired_events_hourly' );