I have an IFTTT recipe that creates posts for me on some occasion, but for some weird reason it creates three, sometimes four posts of the same content.
I would like to add an add_action
hook/callback to validate what will be a new post and, if it already exists, cancel the post, or move it to trash or something like it.
I found the the xmlrpc_prepare_post
but I don't think I can cancel it from there. Unless I can update some attribute and set it to trash?
Update.
I tried the following and it only ever gets into the xmlrpc_call, but never ever inside xmlrpc_wp_insert_post_meta. I even added a hard-coded add_filter call (not just in case of if newPost) and my logs never show such logging message.
Here's the code:
function hueman_xmlrpc_call( $method )
{
error_log("XMLRPC | hueman_xmlrpc_call Method = $method \n" , 3, '/home/.../debug.log');
if( 'wp.newPost' === $method || 'metaWeblog.newPost' === $method )
{
error_log("XMLRPC | hueman_xmlrpc_call method = " . $method . " \n" , 3, '/home/.../debug.log');
add_filter( 'xmlrpc_wp_insert_post_data', 'hueman_xmlrpc_wp_insert_post_data' );
}
}
add_action('xmlrpc_call', 'hueman_xmlrpc_call', 1 );
add_filter( 'xmlrpc_wp_insert_post_data', 'hueman_xmlrpc_wp_insert_post_data' );
function hueman_xmlrpc_wp_insert_post_data( $post_data )
{
error_log("XMLRPC | hueman_xmlrpc_wp_insert_post_data \n" , 3, '/home/.../debug.log');
// Check if the post title exists:
$tmp = get_page_by_title(
$post_data['post_title'],
OBJECT,
$post_data['post_type']
);
if( is_object ( $tmp ) )
{
// Go from 'insert' to 'update' mode within wp_insert_post():
//$post_data['ID'] = $tmp->ID;
$post_data['post_status'] = 'trash';
error_log("XMLRPC | hueman_xmlrpc_wp_insert_post_data I TRASHED IT! \n" , 3, '/home/.../debug.log');
}
return $post_data;
}
In the logs, I have this kind of log statements:
XMLRPC | hueman_xmlrpc_call Method = mt.supportedMethods
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getRecentPosts
XMLRPC | hueman_xmlrpc_call Method = mt.supportedMethods
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getRecentPosts
XMLRPC | hueman_xmlrpc_call Method = mt.supportedMethods
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getRecentPosts
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getCategories
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.newPost
XMLRPC | hueman_xmlrpc_call method = metaWeblog.newPost
Then, I see other filters I added when an article is created. I know I will probably end-up fixing the articles there but I'm sure this XMLRPC filters/actions should work.
From the logs, I see it go inside the if newPost and adding the filter 'xmlrpc_wp_insert_post_data' but it never executes the hueman_xmlrpc_wp_insert_post_data function... :(
I have an IFTTT recipe that creates posts for me on some occasion, but for some weird reason it creates three, sometimes four posts of the same content.
I would like to add an add_action
hook/callback to validate what will be a new post and, if it already exists, cancel the post, or move it to trash or something like it.
I found the the xmlrpc_prepare_post
but I don't think I can cancel it from there. Unless I can update some attribute and set it to trash?
Update.
I tried the following and it only ever gets into the xmlrpc_call, but never ever inside xmlrpc_wp_insert_post_meta. I even added a hard-coded add_filter call (not just in case of if newPost) and my logs never show such logging message.
Here's the code:
function hueman_xmlrpc_call( $method )
{
error_log("XMLRPC | hueman_xmlrpc_call Method = $method \n" , 3, '/home/.../debug.log');
if( 'wp.newPost' === $method || 'metaWeblog.newPost' === $method )
{
error_log("XMLRPC | hueman_xmlrpc_call method = " . $method . " \n" , 3, '/home/.../debug.log');
add_filter( 'xmlrpc_wp_insert_post_data', 'hueman_xmlrpc_wp_insert_post_data' );
}
}
add_action('xmlrpc_call', 'hueman_xmlrpc_call', 1 );
add_filter( 'xmlrpc_wp_insert_post_data', 'hueman_xmlrpc_wp_insert_post_data' );
function hueman_xmlrpc_wp_insert_post_data( $post_data )
{
error_log("XMLRPC | hueman_xmlrpc_wp_insert_post_data \n" , 3, '/home/.../debug.log');
// Check if the post title exists:
$tmp = get_page_by_title(
$post_data['post_title'],
OBJECT,
$post_data['post_type']
);
if( is_object ( $tmp ) )
{
// Go from 'insert' to 'update' mode within wp_insert_post():
//$post_data['ID'] = $tmp->ID;
$post_data['post_status'] = 'trash';
error_log("XMLRPC | hueman_xmlrpc_wp_insert_post_data I TRASHED IT! \n" , 3, '/home/.../debug.log');
}
return $post_data;
}
In the logs, I have this kind of log statements:
XMLRPC | hueman_xmlrpc_call Method = mt.supportedMethods
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getRecentPosts
XMLRPC | hueman_xmlrpc_call Method = mt.supportedMethods
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getRecentPosts
XMLRPC | hueman_xmlrpc_call Method = mt.supportedMethods
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getRecentPosts
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.getCategories
XMLRPC | hueman_xmlrpc_call Method = metaWeblog.newPost
XMLRPC | hueman_xmlrpc_call method = metaWeblog.newPost
Then, I see other filters I added when an article is created. I know I will probably end-up fixing the articles there but I'm sure this XMLRPC filters/actions should work.
From the logs, I see it go inside the if newPost and adding the filter 'xmlrpc_wp_insert_post_data' but it never executes the hueman_xmlrpc_wp_insert_post_data function... :(
Share Improve this question edited Nov 4, 2014 at 20:09 Vallieres asked Aug 9, 2014 at 0:32 VallieresVallieres 1857 bronze badges 2- 4 A very good (and interesting) question. Might you please file an edit and explain how IFTTT works together with WP, how you set it up and how you point to the XML-RPLC interface? Please be as verbose as possible as this has the potential to become a very epic question. Thanks. – kaiser Commented Aug 9, 2014 at 8:04
- It's actually very simple. I used IFTTT with the RSS 2 Wordpress recipe. Plugged in my author RSS feed from the external site. In the Wordpress I logged in user my regular Wordpress user. here's the channel: ifttt/wordpress and the recipe: ifttt/recipes/19382-rss-feed-to-wordpress-blog – Vallieres Commented Nov 4, 2014 at 19:23
3 Answers
Reset to default 7It looks like the xmlrpc_prepare_post
filter is only applied to the output of the wp_getPost
and wp_getRevision
methods of the wp_xmlrpc_server
class.
It would be great if this code line:
do_action( 'xmlrpc_call', 'wp.newPost' );
would be replaced with extra input arguments, for example:
do_action( 'xmlrpc_call', 'wp.newPost', ..., $content_struct );
but that's not going to happen according to this ticket.
So we need to find another way around this.
Possible workarounds:
Here are some untested ideas using the xmlrpc_call
and the xmlrpc_wp_insert_post_data
filters.
Modify input data before it's inserted with wp_insert_posts()
:
/**
* Prevent duplicate posts when doing wp.newPost via XML-RPC
*
* @see http://wordpress.stackexchange/a/157261/26350
*/
add_action( 'xmlrpc_call', 'wpse_xmlrpc_call' );
function wpse_xmlrpc_call( $method )
{
if( 'wp.newPost' === $method )
add_filter( 'xmlrpc_wp_insert_post_data', 'wpse_xmlrpc_wp_insert_post_data' );
}
function wpse_xmlrpc_wp_insert_post_data( $post_data )
{
// Check if the post title exists:
$tmp = get_page_by_title(
$post_data['post_title'],
OBJECT,
$post_data['post_type']
);
// Go from 'insert' to 'update' mode within wp_insert_post():
if( is_object ( $tmp ) )
$post_data['ID'] = $tmp->ID;
return $post_data;
}
Here we try to find an existing post with the same title, during wp.newPost
calls. If we find one, we add it's ID
to the $post_data
array, so it will be updated instead.
Notice that we could also have modified the post_status
instead with:
$post_data['post_status'] = 'trash';
so all extra inserts are directed to the trash.
You could also try to create your own insert method via the xmlrpc_methods
filter.
I hope you can modify this to your needs, assuming this will work ;-)
Update: I've now tested this idea and it works, I can both trash the duplicated posts or update it directly.
Thanks to @DavidPeterson for noticing my silly PHP syntax errors from when I edited the code within the WPSE editor ;-)
In the wp-includes/class-wp-xmlrpc-server.php file
Inside the function mw_newPost() after these lines:
$post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null;
$post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null;
Add:
global $wpdb;
$some_post = $wpdb->get_row("
SELECT ID
FROM {$wpdb->posts}
WHERE post_title = '{$post_title}'
");
And create a statement:
if (!empty($some_post->ID) and $some_post->ID > 0) {
return 0;
} else {
// the rest of the code that already exists in the function
/*
$post_status = $publish ? 'publish' : 'draft';
...
return strval($post_ID);
*/
}
None of the previous solutions were working for me (maybe because I´m posting using metaWeblog.newPost)
So I made my own:
1-Make a copy of xmlrpc.php and rename to xmlrpc2.php to stay safe from WordPress updates.
2-Paste the code below this part:
/** Include the bootstrap for setting up WordPress environment */
require_once __DIR__ . '/wp-load.php';
Paste this code to prevent duplicate titles:
$mixml = file_get_contents('php://input');
$mixml2 = simplexml_load_string($mixml);
$json = json_encode($mixml2);
if ( ! is_admin() ) {
require_once( ABSPATH . 'wp-admin/includes/post.php' );
}
$jsonIterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator(json_decode($json, TRUE)),
RecursiveIteratorIterator::SELF_FIRST);
$nextok=false;
//Sorry for the noob parsing :)
foreach ($jsonIterator as $key => $val) {
if ($val=="title") $nextok=true;
if (($key=="string")&&($nextok)) {
$nextok=false;
$post_id = post_exists($val);
//already exists, so I break the posting with exit
if ($post_id) exit;
}
}
Explanation:
- Read the XMLRPC input.
- Include post.php so we can use post_exists later.
- Get the post title. Sorry for the dirty parsing :) you will probably want to improve it.
- If the title already exists (even in recycle bin) we use exit so nothing is posted.
You can modify the original xmlrpc.php file, but you may lose the changes in future updates.
Renaming xmlrpc.php file is recommended since some servers block that file path and some brute force attacks target it.