Skip to content

Commit e55d365

Browse files
authored
Merge pull request #184 from Automattic/fix/127-syndication-loop-prevention
2 parents 323294e + 1f71f63 commit e55d365

File tree

5 files changed

+1397
-1
lines changed

5 files changed

+1397
-1
lines changed

includes/class-syndication-wp-rest-client.php

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,58 @@ function __construct( $site_ID, $port = 80, $timeout = 45 ) {
2525
public static function get_client_data() {
2626
return array( 'id' => 'WP_REST', 'modes' => array( 'push' ), 'name' => 'WordPress.com REST' );
2727
}
28-
28+
29+
/**
30+
* Check if a post with the given meta key/value exists on the target site.
31+
*
32+
* Used to prevent syndication loops when syndicating back to source.
33+
*
34+
* @since 2.2.0
35+
*
36+
* @param string $meta_key The meta key to search for.
37+
* @param string $meta_value The meta value to match.
38+
* @return bool True if post exists on target site, false otherwise.
39+
*/
40+
public function is_source_site_post( $meta_key = '', $meta_value = '' ) {
41+
42+
// If meta key or value are empty.
43+
if ( empty( $meta_key ) || empty( $meta_value ) ) {
44+
return false;
45+
}
46+
47+
// Get posts from the target website matching the meta key and value.
48+
$url = sprintf(
49+
'https://public-api.wordpress.com/rest/v1/sites/%s/posts/?meta_key=%s&meta_value=%s',
50+
$this->blog_ID,
51+
rawurlencode( $meta_key ),
52+
rawurlencode( $meta_value )
53+
);
54+
55+
$response = wp_remote_get(
56+
$url,
57+
array(
58+
'timeout' => $this->timeout,
59+
'user-agent' => $this->useragent,
60+
'sslverify' => false,
61+
'headers' => array(
62+
'authorization' => 'Bearer ' . $this->access_token,
63+
),
64+
)
65+
);
66+
67+
if ( is_wp_error( $response ) ) {
68+
return false;
69+
}
70+
71+
$response = json_decode( wp_remote_retrieve_body( $response ) );
72+
73+
if ( empty( $response->error ) && ! empty( $response->found ) && $response->found > 0 ) {
74+
return true;
75+
}
76+
77+
return false;
78+
}
79+
2980
public function new_post( $post_ID ) {
3081

3182
$post = (array)get_post( $post_ID );

includes/class-syndication-wp-xmlrpc-client.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,66 @@ function get_remote_post( $remote_post_id ) {
448448
return $this->getResponse();
449449
}
450450

451+
/**
452+
* Check if a post with the given meta key/value exists on the target site.
453+
*
454+
* Used to prevent syndication loops when syndicating back to source.
455+
*
456+
* @since 2.2.0
457+
*
458+
* @param string $meta_key The meta key to search for.
459+
* @param string $meta_value The meta value to match.
460+
* @return bool True if post exists on target site, false otherwise.
461+
*/
462+
public function is_source_site_post( $meta_key = '', $meta_value = '' ) {
463+
464+
// If meta key or value are empty.
465+
if ( empty( $meta_key ) || empty( $meta_value ) ) {
466+
return false;
467+
}
468+
469+
// Use filter to limit posts returned and request custom_fields.
470+
$filter = array(
471+
'number' => 100,
472+
);
473+
474+
$result = $this->query(
475+
'wp.getPosts',
476+
'1',
477+
$this->username,
478+
$this->password,
479+
$filter,
480+
array( 'post_id', 'custom_fields' )
481+
);
482+
483+
if ( ! $result ) {
484+
return false;
485+
}
486+
487+
$posts_list = $this->getResponse();
488+
489+
if ( empty( $posts_list ) ) {
490+
return false;
491+
}
492+
493+
foreach ( $posts_list as $post ) {
494+
if ( empty( $post['custom_fields'] ) ) {
495+
continue;
496+
}
497+
498+
foreach ( $post['custom_fields'] as $field ) {
499+
if ( isset( $field['key'], $field['value'] ) &&
500+
$meta_key === $field['key'] &&
501+
$meta_value === $field['value']
502+
) {
503+
return true;
504+
}
505+
}
506+
}
507+
508+
return false;
509+
}
510+
451511
public function is_post_exists( $remote_post_id ) {
452512
$remote_post = $this->get_remote_post( $remote_post_id );
453513

includes/class-wp-push-syndication-server.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,10 @@ public function save_syndicate_settings() {
864864
$selected_sitegroups = !empty( $_POST['selected_sitegroups'] ) ? array_map( 'sanitize_key', $_POST['selected_sitegroups'] ) : '' ;
865865
update_post_meta( $post->ID, '_syn_selected_sitegroups', $selected_sitegroups );
866866

867+
if ( '' === get_post_meta( $post->ID, 'post_uniqueid', true ) ) {
868+
update_post_meta( $post->ID, 'post_uniqueid', uniqid() );
869+
}
870+
867871
}
868872

869873
public function add_syndication_status_metabox() {
@@ -928,6 +932,15 @@ public function push_content( $sites ) {
928932
$client = Syndication_Client_Factory::get_client( $transport_type ,$site->ID );
929933
$info = $this->get_site_info( $site->ID, $slave_post_states, $client );
930934

935+
// Check if post already exists on target to prevent syndication loops.
936+
if ( in_array( $transport_type, array( 'WP_REST', 'WP_XMLRPC' ), true ) ) {
937+
$unique_id = get_post_meta( $post_ID, 'post_uniqueid', true );
938+
939+
if ( ! empty( $unique_id ) && $client->is_source_site_post( 'post_uniqueid', $unique_id ) ) {
940+
continue;
941+
}
942+
}
943+
931944
if( $info['state'] == 'new' || $info['state'] == 'new-error' ) { // states 'new' and 'new-error'
932945

933946
$push_new_shortcircuit = apply_filters( 'syn_pre_push_new_post_shortcircuit', false, $post_ID, $site, $transport_type, $client, $info );

0 commit comments

Comments
 (0)