Would like posts to automatically expire based on a custom field created with the Magic Fields plugin.
Code needs to be added to the Theme Functions. The field is name "expiration". Basically the code needs to compare the date in the field "expiration" with the system date and if the chosen date is older the post needs to be set as Draft.
Date is in the following format "2011-04-15". I do not have controll over how the date is recorded. Information is kept in the postmeta area of the database.
Please document what the segments of code do.
John Cotton answers:
Something like this will do it:
function expire_posts() {
global $wpdb;
$sql = "UPDATE $wpdb->posts SET post_status = 'draft' WHERE ID IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'expiration' AND DATEDIFF(meta_value, NOW()) < 0);"
$wpdb->query($sql);
}
add_action('init, 'expire_posts');
However, that will run on every request which might not be desirable for a variety of reasons.
So perhaps this is better:
function do_expire_posts() {
global $wpdb;
$sql = "UPDATE $wpdb->posts SET post_status = 'draft' WHERE ID IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'expiration' AND DATEDIFF(meta_value, NOW()) < 0);"
$wpdb->query($sql);
}
add_action('expire_posts', 'do_expire_posts');
function set_expiration() {
wp_schedule_event(time(), 'hourly', 'expire_posts');
}
register_activation_hook(__FILE__, 'set_expiration');
function unset_expiration() {
wp_clear_scheduled_hook('expire_posts');
}
register_deactivation_hook(__FILE__, 'unset_expiration');
although that clearly has the drawn back that some posts could be on the site up to an hour longer than planned.
One other thing - the expiration field needs to be in a proper date format eg
2016-04-24 13:07:36
Regards
John
Phillip Cox comments:
I like where this is going. Two issues. Magic Fields keeps custom field data in the following table: "mf_post_meta". The second thing is the format of the date. I am not sure what format the date is in, but can the date be extracted and compared with "strtotime" to ensure that the date formats match.
John Cotton comments:
Hi Philip
I don't know the Magic Fields plug-in, but if it puts info in a different table then adjusting the should deal with that.
On the date format, the problem with extracting and comparing in PHP is that you end up with more complicated code that can't be executed in SQL.
Are you using the Magic Fields plug-in just for expiration or for something else as well? If the former, then you don't need it with the above, just have a custom field called expiration and the above will work.
JC
Phillip Cox comments:
John,
Magic Fields is used for more than expiration and the plugin integrates well with the needs of the client. In addition Magic Fields provides a simple date picker that makes inputting a date quick and uniform.
Phillip Cox comments:
John,
I attempted to try that code and I got a syntax error. Can you please check the format of the code?
John Cotton comments:
Sorry, there's a semi-colon missing off the end of the '$sql =' line in both.
Also, that line would benefit from being modified to read
$sql = "UPDATE $wpdb->posts SET post_status = 'draft' WHERE post_status = 'publish' AND ID IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'expiration' AND DATEDIFF(meta_value, NOW()) < 0);";
If you're sticking with the plugin, then it's the sub-query that needs to be changed to take account of that. Does that table have the same structure as the standard post meta one?
If so, then it would be something like this:
$sql = "UPDATE $wpdb->posts SET post_status = 'draft' WHERE post_status = 'publish' AND ID IN (SELECT post_id FROM {$wpdb->prefix}mf_post_meta WHERE meta_key = 'expiration' AND DATEDIFF(meta_value, NOW()) < 0);";
JC
Phillip Cox comments:
John,
That code still produces an error when implemented.
John Cotton comments:
Are you sure you've copied the code fully? I don't get any errors from this:
function do_expire_posts() {
global $wpdb;
$sql = "UPDATE $wpdb->posts SET post_status = 'draft' WHERE post_status = 'publish' AND ID IN (SELECT post_id FROM {$wpdb->prefix}mf_post_meta WHERE meta_key = 'expiration' AND DATEDIFF(meta_value, NOW()) < 0);";
$wpdb->query($sql);
}
add_action('expire_posts', 'do_expire_posts');
function set_expiration() {
wp_schedule_event(time(), 'hourly', 'expire_posts');
}
register_activation_hook(__FILE__, 'set_expiration');
function unset_expiration() {
wp_clear_scheduled_hook('expire_posts');
}
register_deactivation_hook(__FILE__, 'unset_expiration');
What's the error you're seeing?
JC
Phillip Cox comments:
I was using the first function that you gave that runs everytime the page loads. The error was in the last line add_action('init, 'expire_posts');
The end tag of 'init was missing.
John Cotton comments:
Try:
add_action('init', 'expire_posts');
Phillip Cox comments:
John,
I implemented the code but there were no changes to the database. Right now the solution proposed does not work. It is up to you if you want to continue your support to this project. I would appreciate if you would continue.
John Cotton comments:
It's a bit tricky debugging blind!
But some answers to these questions might help:
1/ What is the full name of the table with the data in?
2/ Does that table have post_id, meta_value and meta_key fields?
3/ Are the entries in there with 'expiration' in the meta_key field?
4/ If yes to 3, what is the precise content of the meta_value field(s)?
Also, have you retro fitted the later SQL into the version of the code you are using - probably best to post all the code you currently have.
Finally, it would be worth sticking this code in after $wpdb->query($sql);
echo "<!-- MAGIC FIELDS EXPIRATION \n\n";
echo $wpdb->last_error;
echo "\n\n -->";
Once you've executed a page, have a look in the source an let me know what appears (if anything) in the HTML comment above.
JC
Phillip Cox comments:
1/ The name of the table is postmeta. Previously I thought it was a different table.
2/ The table has meta_id, post_id, meta_key, and meta_value
3/ meta_key = expiration meta_value = 2011-04-15
4/ Answered above
John Cotton comments:
Can you post the code your using and the output of the last_error echo?
Phillip Cox comments:
Putting the last_error code in the functions file does not do anything.
John Cotton comments:
Can you send me a link?
Phillip Cox comments:
The last_error code was blank between the comments.
John Cotton comments:
OK - so no error. That means the code is working...
So either there are no posts that have expired or no posts that have expired with post_status set to publish.
Do you have cpanel access? If so try running the SQL in phpMyAdmin and see what happens...
Phillip Cox comments:
The expiration field stores the date as 2011-04-15 is that a problem?
John Cotton comments:
Well it's only a problem if your MySQL is set to create dates in another format. But 2011-04-15 is the standard.
Phillip Cox comments:
Changed post_meta to postmeta to match the database which removed the underscore. How do I make the code run every 60 seconds or immediately?
John Cotton comments:
Getting it to run every 60 seconds is a whole other question since - whilst it can be done - WordPress doesn't support it directly.
The init makes it run on every page view (option 1) or the scheduled version would allow hourly, twice daily or daily.
Phillip Cox comments:
Using the first option gives this error Warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'expire_posts' was given in D:\Hosting\4928375\html\wp-includes\plugin.php on line 395
John Cotton comments:
Phillip
I need to see the code you've edited - I think you've misunderstood what I said but I'm not sure how.
Phillip Cox comments:
function do_expire_posts() {
global $wpdb;
$sql = "UPDATE $wpdb->posts SET post_status = 'draft' WHERE post_status = 'publish' AND ID IN (SELECT post_id FROM postmeta WHERE meta_key = 'expiration' AND DATEDIFF(meta_value, NOW()) < 0);";
$wpdb->query($sql);
}
add_action('expire_posts', 'do_expire_posts');
function set_expiration() {
wp_schedule_event(time(), 'hourly', 'expire_posts');
}
register_activation_hook(__FILE__, 'set_expiration');
function unset_expiration() {
wp_clear_scheduled_hook('expire_posts');
}
register_deactivation_hook(__FILE__, 'unset_expiration');
John Cotton comments:
Please can you email me on [email protected]
Denzel Chia answers:
Hi,
I think I got a working solution for you.
The custom field key or post meta key is 'expiration' and value is something like "2011-04-15"
which is set by user using your magic fields plugin.
The code below is self explanatory.
Put them within your functions.php
<?php
//function that will be run by cron to auto expire posts.
function auto_expire_posts(){
global $wpdb;
//get all post ids of published posts.
$post_ids = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_status ='publish' " );
foreach($post_ids as $id){
$postid = $id->ID;
//get post meta value from post meta key expiration
$expiration_value = get_post_meta($postid,'expiration',true);
if($expiration_value){
$todays_date = date("Y-m-d");
$today = strtotime($todays_date);
$expiration_date = strtotime($expiration_value);
if ($expiration_date > $today) {
//do not do anything
} else {
// it is expired, we set post status to draft, without changing anything
$my_post = array();
$my_post['ID'] = $postid;
$my_post['post_status'] = 'draft';
// Update the post into the database
wp_update_post( $my_post );
}
}//end if(expiration_value);
}
}
//verify event has not been scheduled
if ( !wp_next_scheduled( 'auto_expire_posts_cron_hook' ) ) {
//schedule the event to run daily
wp_schedule_event( time(), 'daily', 'auto_expire_posts_cron_hook' );
}
add_action('auto_expire_posts_cron_hook','auto_expire_posts');
//create menu to see cron hook schedules.
add_action('admin_menu','auto_exp_view_cron_menu');
function auto_exp_view_cron_menu() {
//create view cron jobs settings page
add_options_page( 'View Cron Jobs', 'View Cron Jobs', 'manage_options', 'boj-view-cron', 'exp_view_cron_settings' );
}
function exp_view_cron_settings() {
$cron = _get_cron_array();
$schedules = wp_get_schedules();
$date_format = 'M j, Y @ G:i';
?>
<div class="wrap" id="cron-gui">
<h2>Cron Events Scheduled</h2>
<table class="widefat fixed">
<thead>
<tr>
<th scope="col">Next Run (GMT/UTC)</th>
<th scope="col">Schedule</th>
<th scope="col">Hook Name</th>
</tr>
</thead>
<tbody>
<?php foreach ( $cron as $timestamp => $cronhooks ) { ?>
<?php foreach ( (array) $cronhooks as $hook => $events ) { ?>
<?php foreach ( (array) $events as $event ) { ?>
<tr>
<td>
<?php echo date_i18n( $date_format, wp_next_scheduled( $hook ) ); ?>
</td>
<td>
<?php
if ( $event[ 'schedule' ] ) {
echo $schedules[ $event[ 'schedule' ] ][ 'display' ];
} else {
?>One-time<?php
}
?>
</td>
<td><?php echo $hook; ?></td>
</tr>
<?php } ?>
<?php } ?>
<?php } ?>
</tbody>
</table>
</div>
<?
}
?>
The above code includes a menu called "view cron jobs" under settings,
click on it and you can find the scheduled hook name "auto_expire_posts_cron_hook"
which is set to run daily to execute auto_expire_posts function.
if you need any more information, or a copy of the above code, please email me at [email protected]
<strong>The above codes are tested using Twenty Ten Theme in my localhost, with custom field named expiration</strong>
Thanks.
Denzel
Denzel Chia comments:
Hi,
Please find attached screenshot of my custom field created for testing.
I am sure your magic fields plugin will create a "nice" post meta box with name expiration and text input for value for your user.
But I am using normal custom field for testing purpose. You should be able to change the custom field name (key) within the above codes to something else if it is not called "expiration".
Thanks.
Denzel