From 84c3f03657de0e4b288c4f7e4a78be89a276117d Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:12:17 +1100 Subject: [PATCH 01/57] Use post abstraction for pulling network posts. --- .../NetworkSiteConnection.php | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index abebb768e..47ac1935f 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -256,14 +256,24 @@ public function pull( $items ) { $created_posts = array(); foreach ( $items as $item_array ) { - $post = $this->remote_get( [ 'id' => $item_array['remote_post_id'] ] ); + $update = false; + $insert_args = array(); + if ( ! empty( $item_array['post_status'] ) ) { + $insert_args['post_status'] = $item_array['post_status']; + } + if ( ! empty( $item_array['post_id'] ) ) { + $insert_args['remote_post_id'] = $item_array['post_id']; + $update = true; + } + + $post = $this->remote_get( [ 'id' => $item_array['remote_post_id'] ], $insert_args ); if ( is_wp_error( $post ) ) { $created_posts[] = $post; continue; } - $post_props = get_object_vars( $post ); + $post_props = $post; $post_array = array(); $current_blog_id = get_current_blog_id(); @@ -278,41 +288,20 @@ public function pull( $items ) { } } - foreach ( $post_props as $key => $value ) { - $post_array[ $key ] = $value; - } - - if ( ! empty( $item_array['post_id'] ) ) { - $post_array['ID'] = $item_array['post_id']; - } else { - unset( $post_array['ID'] ); - } - - if ( isset( $post_array['post_parent'] ) ) { - unset( $post_array['post_parent'] ); - } - - if ( ! empty( $item_array['post_status'] ) ) { - $post_array['post_status'] = $item_array['post_status']; - } - add_filter( 'wp_insert_post_data', array( '\Distributor\InternalConnections\NetworkSiteConnection', 'maybe_set_modified_date' ), 10, 2 ); // Filter documented in includes/classes/ExternalConnections/WordPressExternalConnection.php $new_post_args = Utils\post_args_allow_list( apply_filters( 'dt_pull_post_args', $post_array, $item_array['remote_post_id'], $post, $this ) ); - $new_post_id = wp_insert_post( wp_slash( $new_post_args ) ); - + if ( $update ) { + $new_post_id = wp_update_post( wp_slash( $new_post_args ) ); + } else { + $new_post_id = wp_insert_post( wp_slash( $new_post_args ) ); + } remove_filter( 'wp_insert_post_data', array( '\Distributor\InternalConnections\NetworkSiteConnection', 'maybe_set_modified_date' ), 10, 2 ); if ( ! is_wp_error( $new_post_id ) ) { - update_post_meta( $new_post_id, 'dt_original_post_id', (int) $item_array['remote_post_id'] ); update_post_meta( $new_post_id, 'dt_original_blog_id', (int) $this->site->blog_id ); update_post_meta( $new_post_id, 'dt_syndicate_time', time() ); - update_post_meta( $new_post_id, 'dt_original_post_url', esc_url_raw( $post->link ) ); - - if ( ! empty( $post->post_parent ) ) { - update_post_meta( $new_post_id, 'dt_original_post_parent', (int) $post->post_parent ); - } /** * Allow bypassing of all meta processing. @@ -321,15 +310,15 @@ public function pull( $items ) { * * @param {bool} true If Distributor should set the post meta. * @param {int} $new_post_id The newly created post ID. - * @param {array} $post->meta List of meta items attached to the post, formatted by {@link \Distributor\Utils\prepare_meta()}. + * @param {array} $post_meta List of meta items attached to the post, formatted by {@link \Distributor\Utils\prepare_meta()}. * @param {int} $remote_post_id The original post ID. * @param {array} $post_array The arguments passed into wp_insert_post. * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. * * @return {bool} If Distributor should set the post meta. */ - if ( apply_filters( 'dt_pull_post_meta', true, $new_post_id, $post->meta, $item_array['remote_post_id'], $post_array, $this ) ) { - \Distributor\Utils\set_meta( $new_post_id, $post->meta ); + if ( apply_filters( 'dt_pull_post_meta', true, $new_post_id, $post['meta'], $item_array['remote_post_id'], $post_array, $this ) ) { + \Distributor\Utils\set_meta( $new_post_id, $post['meta'] ); } /** @@ -339,15 +328,15 @@ public function pull( $items ) { * * @param {bool} true If Distributor should set the post terms. * @param {int} $new_post_id The newly created post ID. - * @param {array} $post->terms List of terms items attached to the post, formatted by {@link \Distributor\Utils\prepare_taxonomy_terms()}. + * @param {array} $post_terms List of terms items attached to the post, formatted by {@link \Distributor\Utils\prepare_taxonomy_terms()}. * @param {int} $remote_post_id The original post ID. * @param {array} $post_array The arguments passed into wp_insert_post. * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. * * @return {bool} If Distributor should set the post terms. */ - if ( apply_filters( 'dt_pull_post_terms', true, $new_post_id, $post->terms, $item_array['remote_post_id'], $post_array, $this ) ) { - \Distributor\Utils\set_taxonomy_terms( $new_post_id, $post->terms ); + if ( apply_filters( 'dt_pull_post_terms', true, $new_post_id, $post['terms'], $item_array['remote_post_id'], $post_array, $this ) ) { + \Distributor\Utils\set_taxonomy_terms( $new_post_id, $post['terms'] ); } /** @@ -357,15 +346,15 @@ public function pull( $items ) { * * @param {bool} true If Distributor should set the post media. * @param {int} $new_post_id The newly created post ID. - * @param {array} $post->media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. + * @param {array} $post_media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. * @param {int} $remote_post_id The original post ID. * @param {array} $post_array The arguments passed into wp_insert_post. * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. * * @return {bool} If Distributor should set the post media. */ - if ( apply_filters( 'dt_pull_post_media', true, $new_post_id, $post->media, $item_array['remote_post_id'], $post_array, $this ) ) { - \Distributor\Utils\set_media( $new_post_id, $post->media, [ 'use_filesystem' => true ] ); + if ( apply_filters( 'dt_pull_post_media', true, $new_post_id, $post['media'], $item_array['remote_post_id'], $post_array, $this ) ) { + \Distributor\Utils\set_media( $new_post_id, $post['media'], [ 'use_filesystem' => true ] ); }; } @@ -504,11 +493,18 @@ public function get_post_types() { /** * Remotely get posts so we can list them for pulling * - * @param array $args Array of args for getting. * @since 0.8 + * @since 2.0.0 Added $new_post_args parameter. + * + * @param array $args Array of args for getting. + * @param array $new_post_args { + * Array of args for creating new post. + * + * @type string $post_status Post status for new post. + * } * @return array|WP_Post|bool */ - public function remote_get( $args = array() ) { + public function remote_get( $args = array(), $new_post_args = array() ) { $id = ( empty( $args['id'] ) ) ? false : $args['id']; @@ -593,7 +589,8 @@ public function remote_get( $args = array() ) { if ( empty( $post ) ) { $formatted_post = false; } else { - $formatted_post = Utils\prepare_post( $post ); + $dt_post = new DistributorPost( $post ); + $formatted_post = $dt_post->to_insert( $new_post_args ); } restore_current_blog(); From 77080f6f06bc80e84add68fc7e3058c170d6374c Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 27 Feb 2023 13:32:26 +1100 Subject: [PATCH 02/57] Rename get_pull_content to get_pull_content_list. --- includes/rest-api.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index c5d40f3c8..a1de4b4ee 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -149,7 +149,7 @@ function register_rest_routes() { 'distributor/list-pull-content', array( 'methods' => 'POST', - 'callback' => __NAMESPACE__ . '\get_pull_content', + 'callback' => __NAMESPACE__ . '\get_pull_content_list', 'permission_callback' => __NAMESPACE__ . '\\get_pull_content_permissions', 'args' => get_pull_content_list_args(), ) @@ -433,10 +433,12 @@ function check_post_types_permissions() { /** * Get a list of content to show on the Pull screen * + * @since x.x.x Renamed from get_pull_content() to get_pull_content_list(). + * * @param \WP_Rest_Request $request API request arguments * @return \WP_REST_Response|\WP_Error */ -function get_pull_content( $request ) { +function get_pull_content_list( $request ) { $args = [ 'posts_per_page' => isset( $request['posts_per_page'] ) ? $request['posts_per_page'] : 20, 'paged' => isset( $request['page'] ) ? $request['page'] : 1, @@ -519,6 +521,19 @@ function get_pull_content( $request ) { return $response; } +/** + * Get a list of content to show on the Pull screen + * + * @since x.x.x Deprecated in favour of get_pull_content_list(). + * + * @param array ...$args Arguments. + * @return \WP_REST_Response|\WP_Error + */ +function get_pull_content( ...$args ) { + _deprecated_function( __FUNCTION__, 'x.x.x', __NAMESPACE__ . '\\get_pull_content_list' ); + return get_pull_content_list( ...$args ); +} + /** * Checks if a post can be read. * From 7ab2daddab675bce409dc86e547724b0eb63bd46 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:29:00 +1100 Subject: [PATCH 03/57] Expose Distributor version in REST headers. --- includes/bootstrap.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/bootstrap.php b/includes/bootstrap.php index 1accb41a2..43cab6c6f 100644 --- a/includes/bootstrap.php +++ b/includes/bootstrap.php @@ -54,6 +54,7 @@ function() { 'rest_post_dispatch', function( $response ) { $response->header( 'X-Distributor', 'yes' ); + $response->header( 'X-Distributor-Version', DT_VERSION ); return $response; } From af11b17c6d0dff77d391edcd9cc70a9d8b7ad217 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:58:51 +1100 Subject: [PATCH 04/57] Include X-Distributor-Version header in requests. --- includes/classes/Authentication.php | 10 ++++++++++ includes/subscriptions.php | 6 +++++- includes/utils.php | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/includes/classes/Authentication.php b/includes/classes/Authentication.php index ec7043455..315ea0302 100644 --- a/includes/classes/Authentication.php +++ b/includes/classes/Authentication.php @@ -47,6 +47,11 @@ public function __construct( $args ) { * @return array */ public function format_get_args( $args = array(), $context = array() ) { + if ( ! isset( $args['headers'] ) ) { + $args['headers'] = array(); + } + $args['headers']['X-Distributor-Version'] = DT_VERSION; + /** * Format request args for a GET request so auth occurs. * @@ -71,6 +76,11 @@ public function format_get_args( $args = array(), $context = array() ) { * @return array */ public function format_post_args( $args, $context = array() ) { + if ( ! isset( $args['headers'] ) ) { + $args['headers'] = array(); + } + $args['headers']['X-Distributor-Version'] = DT_VERSION; + /** * Format request args for a POST request so auth occurs * diff --git a/includes/subscriptions.php b/includes/subscriptions.php index 2fd92df1d..242c2109f 100644 --- a/includes/subscriptions.php +++ b/includes/subscriptions.php @@ -219,6 +219,9 @@ function delete_subscriptions( $post_id ) { // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout 'timeout' => 5, 'blocking' => \Distributor\Utils\is_dt_debug(), + 'headers' => array( + 'X-Distributor-Version' => DT_VERSION, + ), 'body' => [ 'post_id' => $remote_post_id, 'signature' => $signature, @@ -313,7 +316,8 @@ function send_notifications( $post ) { 'timeout' => $request_timeout, 'body' => wp_json_encode( $post_body ), 'headers' => [ - 'Content-Type' => 'application/json', + 'Content-Type' => 'application/json', + 'X-Distributor-Version' => DT_VERSION, ], ]; diff --git a/includes/utils.php b/includes/utils.php index 587190986..f7158d25a 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -108,6 +108,9 @@ function check_license_key( $email, $license_key ) { [ // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout 'timeout' => 10, + 'headers' => [ + 'X-Distributor-Version' => DT_VERSION, + ], 'body' => [ 'license_key' => $license_key, 'email' => $email, From 4db47804e9014f1f56f8321d085482cb3183006b Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:13:45 +1100 Subject: [PATCH 05/57] Introduce helper function to determine if running a development version of the plugin. --- includes/settings.php | 2 +- includes/utils.php | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/includes/settings.php b/includes/settings.php index fcf544acb..3cf6705a6 100644 --- a/includes/settings.php +++ b/includes/settings.php @@ -126,7 +126,7 @@ function update_notice( $plugin_file, $plugin_data, $status ) { */ function maybe_notice() { if ( 0 === strpos( get_current_screen()->parent_base, 'distributor' ) ) { - if ( file_exists( DT_PLUGIN_PATH . 'composer.lock' ) ) { + if ( Utils\is_development_version() ) { ?>
diff --git a/includes/utils.php b/includes/utils.php index f7158d25a..d51dc05a3 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -9,6 +9,17 @@ use Distributor\DistributorPost; +/** + * Determine if this is a development install of Distributor. + * + * @since x.x.x + * + * @return bool True if this is a development install, false otherwise. + */ +function is_development_version() { + return file_exists( DT_PLUGIN_PATH . 'composer.lock' ); +} + /** * Determine if we are on VIP * From df37d7786e2176d7c801a190de744571b902cca3 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 16 Mar 2023 13:29:36 +1100 Subject: [PATCH 06/57] Ensure compatible version of Distributor is used. --- includes/rest-api.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/includes/rest-api.php b/includes/rest-api.php index a1de4b4ee..221c9665f 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -223,6 +223,32 @@ function get_pull_content_list_args() { * @return bool Whether the current user has permission to pull content. */ function get_pull_content_permissions( $request ) { + /* + * Ensure Distributor requests are coming from a supported version. + * + * Changes to this endpoint in Distributor 2.0.0 require both the source and remote + * sites use a 2.x release of Distributor. This check ensures that the remote site + * is running a version of Distributor that supports the new endpoint. + * + * Development versions of the plugin and Non-Distributor requests are allowed + * to pass through this check. + */ + if ( + true !== Utils\is_development_version() + && null !== $request->get_param( 'distributor_request' ) + && ( + null === $request->get_header( 'X-Distributor-Version' ) + || version_compare( $request->get_header( 'X-Distributor-Version' ), '2.0.0', '<' ) + ) + ) { + return new \WP_Error( + 'distributor_pull_content_permissions', + esc_html__( 'Pulling content from external connections requires Distributor version 2.0.0 or later.', 'distributor' ), + array( 'status' => 403 ) + ); + + } + $post_type = $request->get_param( 'post_type' ); if ( ! $post_type ) { return false; From a844ea35c2257e9dec9c6edcb91474bc5c2b8380 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 16 Mar 2023 14:53:57 +1100 Subject: [PATCH 07/57] Use post abstraction in pull list endpoint. --- includes/rest-api.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 221c9665f..464d0998a 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -7,6 +7,7 @@ namespace Distributor\RestApi; +use Distributor\DistributorPost; use Distributor\Utils; /** @@ -521,22 +522,8 @@ function get_pull_content_list( $request ) { continue; } - $formatted_posts[] = array( - 'id' => $post->ID, - 'title' => array( 'rendered' => $post->post_title ), - 'excerpt' => array( 'rendered' => $post->post_excerpt ), - 'content' => array( 'raw' => $post->post_content ), - 'password' => $post->post_password, - 'date' => $post->post_date, - 'date_gmt' => $post->post_date_gmt, - 'guid' => array( 'rendered' => $post->guid ), - 'modified' => $post->post_modified, - 'modified_gmt' => $post->post_modified_gmt, - 'type' => $post->post_type, - 'link' => get_the_permalink( $post ), - 'comment_status' => $post->comment_status, - 'ping_status' => $post->ping_status, - ); + $dt_post = new DistributorPost( $post->ID ); + $formatted_posts[] = $dt_post->to_insert(); } $response = rest_ensure_response( $formatted_posts ); From 12c28319be713cb40793dd6b2798a1eaf04511d2 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:30:34 +1100 Subject: [PATCH 08/57] Add method to include additional data required for external pull screens. --- includes/classes/DistributorPost.php | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/includes/classes/DistributorPost.php b/includes/classes/DistributorPost.php index e1e1400d3..c210b1cd6 100644 --- a/includes/classes/DistributorPost.php +++ b/includes/classes/DistributorPost.php @@ -44,6 +44,7 @@ * @method array get_media() * @method array post_data() * @method array to_insert( array $args = [] ) + * @method array to_pull_list( array $args = [] ) * @method array to_rest( array $args = [] ) */ class DistributorPost { @@ -816,6 +817,36 @@ protected function to_insert( $args = array() ) { return $insert; } + /** + * Get the post data in a format suitable for the pull screen. + * + * This is a wrapper for the ::to_insert() method that includes extra + * data required for the pull screen. + * + * @param mixed $args { + * Optional. Array of push arguments + * @see ::to_insert() for arguments. + * } + * @return array Post data formatted for the pull screen. + */ + protected function to_pull_list( $args = array() ) { + $display_data = $this->to_insert( $args ); + + // Additional information required for pull screen. + $display_data['ID'] = $this->post->ID; + $display_data['date'] = $this->post->post_date; + $display_data['date_gmt'] = $this->post->post_date_gmt; + $display_data['modified'] = $this->post->post_modified; + $display_data['modified_gmt'] = $this->post->post_modified_gmt; + $display_data['post_password'] = $this->post->post_password; + $display_data['guid'] = $this->post->guid; + $display_data['comment_status'] = $this->post->comment_status; + $display_data['ping_status'] = $this->post->ping_status; + $display_data['link'] = $this->get_permalink(); + + return $display_data; + } + /** * Get the post data in a format suitable for the distributor REST API endpoint. * From 70a894cc336116c3d5dc8c177d58e305b332e818 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:30:58 +1100 Subject: [PATCH 09/57] Use new method for pull screen lists. --- includes/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 464d0998a..bce84fc07 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -523,7 +523,7 @@ function get_pull_content_list( $request ) { } $dt_post = new DistributorPost( $post->ID ); - $formatted_posts[] = $dt_post->to_insert(); + $formatted_posts[] = $dt_post->to_pull_list(); } $response = rest_ensure_response( $formatted_posts ); From f0f76777f5510f7dc531620edbba51ad80aa39e3 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:55:45 +1100 Subject: [PATCH 10/57] Accept arrays of post types and statuses. --- includes/rest-api.php | 141 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 14 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index bce84fc07..ca3a279d1 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -9,6 +9,7 @@ use Distributor\DistributorPost; use Distributor\Utils; +use WP_Error; /** * Setup actions and filters @@ -191,9 +192,71 @@ function get_pull_content_list_args() { ), 'post_type' => array( 'description' => esc_html__( 'Limit results to content matching a certain type.', 'distributor' ), - 'type' => 'string', - 'default' => 'post', - 'sanitize_callback' => 'sanitize_text_field', + 'type' => array( 'array', 'string' ), + 'items' => array( + 'type' => 'string', + ), + 'default' => array( 'post' ), + 'validate_callback' => function( $param ) { + if ( is_string( $param ) ) { + return sanitize_key( $param ) === $param; + } + + foreach ( $param as $post_type ) { + if ( sanitize_key( $post_type ) !== $post_type ) { + return false; + } + } + + return true; + }, + 'sanitize_callback' => function( $param ) { + if ( is_string( $param ) ) { + $param = array( $param ); + } + + $allowed_post_types = array_keys( + get_post_types( + array( + 'show_in_rest' => true, + ) + ) + ); + + /* + * Only post types viewable on the front end should be allowed. + * + * Some post types may be visible in the REST API but not intended + * to be viewed on the front end. This removes any such posts from the + * list of allowed post types. + * + * `is_post_type_viewable()` is used to filter the results as + * WordPress applies different rules for custom and built in post + * types to determine whether they are viewable on the front end. + */ + $allowed_post_types = array_filter( $allowed_post_types, 'is_post_type_viewable' ); + + if ( in_array( 'any', $param, true ) ) { + $param = $allowed_post_types; + } else { + $param = array_intersect( $param, $allowed_post_types ); + } + + $param = array_filter( + $param, + function( $post_type ) { + $post_type_object = get_post_type_object( $post_type ); + return current_user_can( $post_type_object->cap->edit_posts ); + } + ); + + if ( empty( $param ) ) { + // This will cause the parameter to fall back to the default. + $param = null; + } + + return $param; + }, 'validate_callback' => 'rest_validate_request_arg', ), 'search' => array( @@ -201,14 +264,53 @@ function get_pull_content_list_args() { 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ), - 'post_status' => array( - 'default' => 'publish', - 'description' => esc_html__( 'Limit result set to content assigned one or more statuses.', 'distributor' ), - 'type' => 'array', - 'items' => array( - 'enum' => array_merge( array_keys( get_post_stati() ), array( 'any' ) ), + 'post_status' => array( + 'default' => array( 'publish' ), + 'description' => esc_html__( 'Limit result set to content assigned one or more statuses.', 'distributor' ), + 'type' => array( 'array', 'string' ), + 'items' => array( 'type' => 'string', ), + 'validate_callback' => function( $param ) { + if ( is_string( $param ) ) { + return sanitize_key( $param ) === $param; + } + + foreach ( $param as $post_status ) { + if ( sanitize_key( $post_status ) !== $post_status ) { + return false; + } + } + + return true; + }, + 'sanitize_callback' => function( $param ) { + if ( is_string( $param ) ) { + $param = array( $param ); + } + + /* + * Only show post statuses viewable post statues. + * + * `is_post_type_viewable()` is used to filter the results as + * WordPress applies a complex set of rules to determine if a post + * status is viewable. + */ + $allowed_statues = array_keys( array_filter( get_post_stati(), 'is_post_type_viewable' ) ); + + if ( in_array( 'any', $param, true ) ) { + return $allowed_statues; + } + + $param = array_intersect( $param, $allowed_statues ); + + if ( empty( $param ) ) { + // This will cause the parameter to fall back to the default. + $param = null; + } + + return $param; + }, ), ); } @@ -251,16 +353,27 @@ function get_pull_content_permissions( $request ) { } $post_type = $request->get_param( 'post_type' ); - if ( ! $post_type ) { + if ( empty( $post_type ) ) { return false; } - $post_type_object = get_post_type_object( $post_type ); - if ( ! $post_type_object ) { - return false; + if ( is_string( $post_type ) ) { + $post_type = array( $post_type ); + } + + foreach ( $post_type as $single_post_type ) { + $post_type_object = get_post_type_object( $single_post_type ); + if ( ! $post_type_object ) { + return false; + } + + if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) { + return false; + } } - return current_user_can( $post_type_object->cap->edit_posts ); + // User can edit all post types. + return true; } /** From a66607d4c0953fba0c2e3735765ada66c1c5e9ae Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:59:38 +1100 Subject: [PATCH 11/57] Introduce include parameter. --- includes/rest-api.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/includes/rest-api.php b/includes/rest-api.php index ca3a279d1..7e0477d7d 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -174,6 +174,21 @@ function get_pull_content_list_args() { ), 'default' => array(), ), + 'include' => array( + 'description' => esc_html__( 'Ensure result set includes specific IDs.', 'distributor' ), + 'type' => array( 'array', 'integer' ), + 'items' => array( + 'type' => 'integer', + ), + 'default' => array(), + 'sanitize_callback' => function( $param ) { + if ( ! is_array( $param ) ) { + $param = array( $param ); + } + + return wp_parse_id_list( $param ); + }, + ), 'page' => array( 'description' => esc_html__( 'Current page of the collection.', 'distributor' ), 'type' => 'integer', @@ -594,6 +609,10 @@ function get_pull_content_list( $request ) { $args['post__not_in'] = $request['exclude']; } + if ( ! empty( $request['include'] ) ) { + $args['post__in'] = $request['include']; + } + /** * Filters WP_Query arguments when querying posts via the REST API. * From ba34f34163a98024aa00436e1530c61c4618012e Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 14:29:15 +1100 Subject: [PATCH 12/57] Add original site details to pull formatted content. --- includes/classes/DistributorPost.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/includes/classes/DistributorPost.php b/includes/classes/DistributorPost.php index c210b1cd6..4766c6e6e 100644 --- a/includes/classes/DistributorPost.php +++ b/includes/classes/DistributorPost.php @@ -833,16 +833,18 @@ protected function to_pull_list( $args = array() ) { $display_data = $this->to_insert( $args ); // Additional information required for pull screen. - $display_data['ID'] = $this->post->ID; - $display_data['date'] = $this->post->post_date; - $display_data['date_gmt'] = $this->post->post_date_gmt; - $display_data['modified'] = $this->post->post_modified; - $display_data['modified_gmt'] = $this->post->post_modified_gmt; - $display_data['post_password'] = $this->post->post_password; - $display_data['guid'] = $this->post->guid; - $display_data['comment_status'] = $this->post->comment_status; - $display_data['ping_status'] = $this->post->ping_status; - $display_data['link'] = $this->get_permalink(); + $display_data['ID'] = $this->post->ID; + $display_data['date'] = $this->post->post_date; + $display_data['date_gmt'] = $this->post->post_date_gmt; + $display_data['modified'] = $this->post->post_modified; + $display_data['modified_gmt'] = $this->post->post_modified_gmt; + $display_data['post_password'] = $this->post->post_password; + $display_data['guid'] = $this->post->guid; + $display_data['comment_status'] = $this->post->comment_status; + $display_data['ping_status'] = $this->post->ping_status; + $display_data['link'] = $this->get_permalink(); + $display_data['distributor_original_site_name'] = $this->source_site['name']; + $display_data['distributor_original_site_url'] = $this->source_site['home_url']; return $display_data; } From d0592a57f3b4b1509937261f41dad3163a3ee39c Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 14:29:55 +1100 Subject: [PATCH 13/57] Modify method generating WP_Post object. --- .../WordPressExternalConnection.php | 53 ++++--------------- 1 file changed, 9 insertions(+), 44 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 9cecedf6c..6e5960d8f 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -940,55 +940,18 @@ private function not_distributor_internal_post_type( $post_type ) { * @return \WP_Post */ private function to_wp_post( $post ) { - $obj = new \stdClass(); + $obj = (object) $post; - $obj->ID = $post['id']; - $obj->post_title = $post['title']['rendered']; - - if ( isset( $post['excerpt']['raw'] ) ) { - $obj->post_excerpt = $post['excerpt']['raw']; - } elseif ( isset( $post['excerpt']['rendered'] ) ) { - $obj->post_excerpt = $post['excerpt']['rendered']; - } else { - $obj->post_excerpt = ''; - } - - $obj->post_status = 'draft'; - $obj->post_author = get_current_user_id(); - - $obj->post_password = $post['password']; - $obj->post_date = $post['date']; - $obj->post_date_gmt = $post['date_gmt']; - $obj->guid = $post['guid']['rendered']; - $obj->post_modified = $post['modified']; - $obj->post_modified_gmt = $post['modified_gmt']; - $obj->post_type = $post['type']; - $obj->link = $post['link']; - $obj->comment_status = $post['comment_status']; - $obj->ping_status = $post['ping_status']; - - if ( isset( $post['content']['raw'] ) ) { - // Use raw content if remote post uses Gutenberg and the local post type is compatible with it. - $obj->post_content = Utils\dt_use_block_editor_for_post_type( $obj->post_type ) && isset( $post['is_using_gutenberg'] ) ? - $post['content']['raw'] : - Utils\get_processed_content( $post['content']['raw'] ); - } elseif ( isset( $post['content']['rendered'] ) ) { - $obj->post_content = $post['content']['rendered']; - } else { - $obj->post_content = ''; - } - - - /** + /* * These will only be set if Distributor is active on the other side */ - $obj->meta = ( ! empty( $post['distributor_meta'] ) ) ? $post['distributor_meta'] : []; - $obj->terms = ( ! empty( $post['distributor_terms'] ) ) ? $post['distributor_terms'] : []; - $obj->media = ( ! empty( $post['distributor_media'] ) ) ? $post['distributor_media'] : []; $obj->original_site_name = ( ! empty( $post['distributor_original_site_name'] ) ) ? $post['distributor_original_site_name'] : null; $obj->original_site_url = ( ! empty( $post['distributor_original_site_url'] ) ) ? $post['distributor_original_site_url'] : null; - $obj->full_connection = ( ! empty( $post['full_connection'] ) ); + // Unset these as they are renamed above. + unset( $obj->distributor_original_site_name ); + unset( $obj->distributor_original_site_url ); + /** * Filter the post item. @@ -1001,7 +964,9 @@ private function to_wp_post( $post ) { * * @return {WP_Post} The WP_Post that is being pushed. */ - return apply_filters( 'dt_item_mapping', new \WP_Post( $obj ), $post, $this ); + $post_object = apply_filters( 'dt_item_mapping', new \WP_Post( $obj ), $post, $this ); + + return $post_object; } /** From 1d47baa2f2c014259fe002f4a0f4c395bad6a568 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 14:32:33 +1100 Subject: [PATCH 14/57] CS Fix. --- includes/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 7e0477d7d..d6a2204b9 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -279,7 +279,7 @@ function( $post_type ) { 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ), - 'post_status' => array( + 'post_status' => array( 'default' => array( 'publish' ), 'description' => esc_html__( 'Limit result set to content assigned one or more statuses.', 'distributor' ), 'type' => array( 'array', 'string' ), From 7407ebe3c2778c73b6520238350cb7d69aa2a88d Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 15:42:55 +1100 Subject: [PATCH 15/57] Use false for false check. --- includes/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index d6a2204b9..99f90052a 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -352,7 +352,7 @@ function get_pull_content_permissions( $request ) { * to pass through this check. */ if ( - true !== Utils\is_development_version() + false === Utils\is_development_version() && null !== $request->get_param( 'distributor_request' ) && ( null === $request->get_header( 'X-Distributor-Version' ) From b354d48066007503f0ff2cbd97dc5ca5b3176114 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 22 Mar 2023 15:44:32 +1100 Subject: [PATCH 16/57] Improve variable names. --- includes/rest-api.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 99f90052a..1308a04d8 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -367,17 +367,17 @@ function get_pull_content_permissions( $request ) { } - $post_type = $request->get_param( 'post_type' ); - if ( empty( $post_type ) ) { + $post_types = $request->get_param( 'post_type' ); + if ( empty( $post_types ) ) { return false; } - if ( is_string( $post_type ) ) { - $post_type = array( $post_type ); + if ( is_string( $post_types ) ) { + $post_types = array( $post_types ); } - foreach ( $post_type as $single_post_type ) { - $post_type_object = get_post_type_object( $single_post_type ); + foreach ( $post_types as $post_type ) { + $post_type_object = get_post_type_object( $post_type ); if ( ! $post_type_object ) { return false; } From b888f411fcbd6c1e2e043ba18ab7cbc1697cb9d7 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:30:07 +1100 Subject: [PATCH 17/57] Add version constant to bootstrap file. --- .github/release-pull-request-template.md | 2 +- tests/php/bootstrap.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/release-pull-request-template.md b/.github/release-pull-request-template.md index 84efa24f2..8dd1bdbec 100644 --- a/.github/release-pull-request-template.md +++ b/.github/release-pull-request-template.md @@ -1,5 +1,5 @@ - [x] Branch: Starting from `develop`, create a release branch named `release/X.Y.Z` for your changes. -- [ ] Version bump: Bump the version number in `distributor.php`, `package.json`, and `readme.txt` if it does not already reflect the version being released. In `distributor.php` update both the plugin "Version:" property and the plugin `DT_VERSION` constant. +- [ ] Version bump: Bump the version number in `distributor.php`, `package.json`, `readme.txt` and `tests/php/bootstrap.php` if it does not already reflect the version being released. In `distributor.php` update both the plugin "Version:" property and the plugin `DT_VERSION` constant. - [ ] New files: Ensure any new files, especially in the vendor folder, are correctly included in `webpack.config.release.js`. - [ ] Changelog: Add/update the changelog in `CHANGELOG.md`. - [ ] Props: Update `CREDITS.md` file with any new contributors, confirm maintainers are accurate. diff --git a/tests/php/bootstrap.php b/tests/php/bootstrap.php index 16602dd84..aa1585f2b 100644 --- a/tests/php/bootstrap.php +++ b/tests/php/bootstrap.php @@ -10,6 +10,7 @@ WP_Mock::bootstrap(); define( 'DT_PLUGIN_PATH', dirname( __DIR__, 2 ) ); +define( 'DT_VERSION', '1.9.1' ); require_once __DIR__ . '/includes/common.php'; require_once __DIR__ . '/includes/TestCase.php'; From dc307381bb9a0cd5c20372bd0bd2507248cfa13f Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:44:26 +1100 Subject: [PATCH 18/57] Add mocks. --- tests/php/NetworkSiteConnectionsTest.php | 53 ++++++++++++++++++- tests/php/WordPressExternalConnectionTest.php | 1 + 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/php/NetworkSiteConnectionsTest.php b/tests/php/NetworkSiteConnectionsTest.php index 14e3f5aa8..ad9094011 100644 --- a/tests/php/NetworkSiteConnectionsTest.php +++ b/tests/php/NetworkSiteConnectionsTest.php @@ -237,6 +237,24 @@ public function test_push() { * @runInSeparateProcess */ public function test_pull() { + $this->setup_post_meta_mock( array( + 'dt_connection_map' => array( array() ) + ) ); + \WP_Mock::userFunction( + 'get_bloginfo', + array( + 'return' => function( $info ) { + switch ( $info ) { + case 'charset': + return 'UTF-8'; + case 'name': + return 'Test Internal Origin'; + default: + return ''; + } + }, + ) + ); $this->connection_obj->site->blog_id = 2; @@ -252,11 +270,44 @@ public function test_pull() { 'get_post', [ 'return' => (object) [ 'ID' => 111, - 'post_tite' => 'My post title', + 'post_title' => 'My post title', + 'post_name' => 'my-post-title', + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_content' => 'My post content', + 'post_excerpt' => 'My post excerpt', 'meta' => [], ], ] ); + \WP_Mock::userFunction( + 'get_the_title', [ + 'return' => 'My post title', + ] + ); + \WP_Mock::userFunction( + 'has_blocks', + array( + 'return' => false, + ) + ); + \WP_Mock::userFunction( + 'get_attached_media', + array( + 'return' => array(), + ) + ); + \WP_Mock::userFunction( + 'get_post_thumbnail_id', + array( + 'return' => false, + ) + ); + \WP_Mock::userFunction( + 'get_current_user_id', [ + 'return' => 1, + ] + ); \WP_Mock::userFunction( 'get_permalink', [ diff --git a/tests/php/WordPressExternalConnectionTest.php b/tests/php/WordPressExternalConnectionTest.php index b74d1bc48..c6878d78e 100644 --- a/tests/php/WordPressExternalConnectionTest.php +++ b/tests/php/WordPressExternalConnectionTest.php @@ -254,6 +254,7 @@ public function test_push() { * @runInSeparateProcess */ public function test_pull() { + $this->setup_post_meta_mock( array() ); $post_id = 123; \WP_Mock::userFunction( 'wp_remote_retrieve_response_code' ); From e13c4400d7f3f2bd672ed47b9d1573d0cf25f798 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:07:28 +1100 Subject: [PATCH 19/57] Assign post data to post array. --- includes/classes/InternalConnections/NetworkSiteConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 47ac1935f..8def359ca 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -274,7 +274,7 @@ public function pull( $items ) { } $post_props = $post; - $post_array = array(); + $post_array = $post_props; $current_blog_id = get_current_blog_id(); if ( ! empty( $post_props['meta']['dt_connection_map'] ) ) { From 3f4ba027e9b6a6c69f0eb72c8b36bc1524a97157 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:24:25 +1100 Subject: [PATCH 20/57] Include connection map in remote_get. --- includes/classes/InternalConnections/NetworkSiteConnection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 8def359ca..94cf1dedd 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -591,6 +591,8 @@ public function remote_get( $args = array(), $new_post_args = array() ) { } else { $dt_post = new DistributorPost( $post ); $formatted_post = $dt_post->to_insert( $new_post_args ); + // The pull method requires the connection map despite it being on the deny list. + $formatted_post['meta']['dt_connection_map'] = get_post_meta( $id, 'dt_connection_map', true ); } restore_current_blog(); From dd8dd9c1ffd19d5c6d23599938444275fb09920d Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:57:54 +1100 Subject: [PATCH 21/57] Update post ID and post URL manually. --- includes/classes/InternalConnections/NetworkSiteConnection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 94cf1dedd..7cf127ace 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -302,6 +302,8 @@ public function pull( $items ) { if ( ! is_wp_error( $new_post_id ) ) { update_post_meta( $new_post_id, 'dt_original_blog_id', (int) $this->site->blog_id ); update_post_meta( $new_post_id, 'dt_syndicate_time', time() ); + update_post_meta( $new_post_id, 'dt_original_post_id', $new_post_args['meta_input']['dt_original_post_id'] ); + update_post_meta( $new_post_id, 'dt_original_post_url', $new_post_args['meta_input']['dt_original_post_url'] ); /** * Allow bypassing of all meta processing. From 3c4d1877676d63c9066ab40f66303f36bf6b4bc4 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:08:58 +1100 Subject: [PATCH 22/57] Update Source Post ID. --- tests/php/NetworkSiteConnectionsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/php/NetworkSiteConnectionsTest.php b/tests/php/NetworkSiteConnectionsTest.php index ad9094011..6a70c3fe9 100644 --- a/tests/php/NetworkSiteConnectionsTest.php +++ b/tests/php/NetworkSiteConnectionsTest.php @@ -324,7 +324,7 @@ public function test_pull() { \WP_Mock::userFunction( 'update_post_meta', [ 'times' => 1, - 'args' => [ \WP_Mock\Functions::type( 'int' ), 'dt_original_post_id', 2 ], + 'args' => [ \WP_Mock\Functions::type( 'int' ), 'dt_original_post_id', 111 ], 'return' => [], ] ); From 76fa01a1dcec87cf10ec7511d3b37bdcc4a63bb6 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:33:22 +1100 Subject: [PATCH 23/57] Remove mock that uses the general post_meta mock. --- tests/php/NetworkSiteConnectionsTest.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/php/NetworkSiteConnectionsTest.php b/tests/php/NetworkSiteConnectionsTest.php index 6a70c3fe9..d1947a40a 100644 --- a/tests/php/NetworkSiteConnectionsTest.php +++ b/tests/php/NetworkSiteConnectionsTest.php @@ -353,14 +353,6 @@ public function test_pull() { ] ); - \WP_Mock::userFunction( - 'get_post_meta', [ - 'times' => 1, - 'args' => [ \WP_Mock\Functions::type( 'int' ), 'dt_connection_map', true ], - 'return' => [], - ] - ); - \WP_Mock::userFunction( 'update_post_meta', [ 'times' => 1, From 97dd3a19e523c64d5fc8224b804ddaaa5f1ac20c Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:41:31 +1100 Subject: [PATCH 24/57] Add mocks required by new code structure. --- tests/php/NetworkSiteConnectionsTest.php | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/php/NetworkSiteConnectionsTest.php b/tests/php/NetworkSiteConnectionsTest.php index d1947a40a..85f3b61c9 100644 --- a/tests/php/NetworkSiteConnectionsTest.php +++ b/tests/php/NetworkSiteConnectionsTest.php @@ -423,10 +423,66 @@ public function test_remote_get() { 'return' => (object) [ 'ID' => 111, 'post_title' => 'my title', + 'post_name' => 'my-title', + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_content' => 'My post content', + 'post_excerpt' => 'My post excerpt', ], ] ); + \WP_Mock::userFunction( + 'get_bloginfo', + array( + 'return' => function( $info ) { + switch ( $info ) { + case 'charset': + return 'UTF-8'; + case 'name': + return 'Test Internal Origin'; + default: + return ''; + } + }, + ) + ); + + \WP_Mock::userFunction( + 'get_current_blog_id', [ + 'return' => 1, + ] + ); + + \WP_Mock::userFunction( + 'get_the_title', [ + 'return' => 'my title', + ] + ); + \WP_Mock::userFunction( + 'has_blocks', + array( + 'return' => false, + ) + ); + \WP_Mock::userFunction( + 'get_attached_media', + array( + 'return' => array(), + ) + ); + \WP_Mock::userFunction( + 'get_post_thumbnail_id', + array( + 'return' => false, + ) + ); + \WP_Mock::userFunction( + 'get_current_user_id', [ + 'return' => 1, + ] + ); + $this->assertArrayHasKey( 'post_title', (array) $this->connection_obj->remote_get( [ From 40d03c619c72eb7310ca649b21848590dff77aa3 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:55:32 +1100 Subject: [PATCH 25/57] Add version header to various HTTP request mocks. --- tests/php/SubscriptionsTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/php/SubscriptionsTest.php b/tests/php/SubscriptionsTest.php index e46f25e37..089599def 100644 --- a/tests/php/SubscriptionsTest.php +++ b/tests/php/SubscriptionsTest.php @@ -98,6 +98,9 @@ public function test_delete_subscribed_post() { [ 'timeout' => 5, 'blocking' => \Distributor\Utils\is_dt_debug(), + 'headers' => [ + 'X-Distributor-Version' => DT_VERSION, + ], 'body' => [ 'post_id' => $remote_post_id, 'signature' => $signature, @@ -375,6 +378,7 @@ public function test_send_notifications_no_remote_post() { ] ), 'headers' => [ 'Content-Type' => 'application/json', + 'X-Distributor-Version' => DT_VERSION, ], ], ], @@ -566,6 +570,7 @@ public function test_send_notifications_remote_post_exists() { ] ), 'headers' => [ 'Content-Type' => 'application/json', + 'X-Distributor-Version' => DT_VERSION, ] ], ], @@ -761,6 +766,9 @@ public function test_create_remote_subscription() { [ 'timeout' => 5, 'blocking' => \Distributor\Utils\is_dt_debug(), + 'headers' => [ + 'X-Distributor-Version' => DT_VERSION, + ], 'body' => [ 'post_id' => $remote_post_id, 'remote_post_id' => $post_id, From a6db663a42bd951056182c3e02a522e1c793aad2 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:02:09 +1000 Subject: [PATCH 26/57] Always use POST requests on pull screen. --- .../ExternalConnections/WordPressExternalConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 6e5960d8f..c4dd13ea5 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -167,8 +167,8 @@ public function remote_get( $args = array() ) { } } - // When running a query for the Pull screen with excluded items, make a POST request instead - if ( empty( $id ) && isset( $args['post__not_in'] ) && isset( $args['dt_pull_list'] ) ) { + // When running a query for the Pull screen, make a POST request instead + if ( empty( $id ) && isset( $args['dt_pull_list'] ) ) { $query_args['post_type'] = isset( $post_type ) ? $post_type : 'post'; $query_args['posts_per_page'] = isset( $posts_per_page ) ? $posts_per_page : 20; From db58d8aa06783db72ff11ba200813b1b2528dc8b Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 6 Apr 2023 10:59:06 +1000 Subject: [PATCH 27/57] Add order parameter to the get pull content REST endpoint. --- includes/rest-api.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/includes/rest-api.php b/includes/rest-api.php index 1308a04d8..5535e8789 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -327,6 +327,12 @@ function( $post_type ) { return $param; }, ), + 'order' => array( + 'description' => esc_html__( 'Order sort attribute ascending or descending.', 'distributor' ), + 'type' => 'string', + 'default' => 'desc', + 'enum' => array( 'asc', 'desc' ), + ), ); } @@ -599,6 +605,7 @@ function get_pull_content_list( $request ) { 'paged' => isset( $request['page'] ) ? $request['page'] : 1, 'post_type' => isset( $request['post_type'] ) ? $request['post_type'] : 'post', 'post_status' => isset( $request['post_status'] ) ? $request['post_status'] : array( 'any' ), + 'order' => ! empty( $request['order'] ) ? strtoupper( $request['order'] ) : 'DESC', ]; if ( ! empty( $request['search'] ) ) { From 929547b0ec31bc2cc1d12aff77aba21d4d2717af Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:26:20 +1000 Subject: [PATCH 28/57] Add orderby parameter to the get pull content REST endpoint. --- includes/rest-api.php | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 5535e8789..8d91d3510 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -333,6 +333,22 @@ function( $post_type ) { 'default' => 'desc', 'enum' => array( 'asc', 'desc' ), ), + 'orderby' => array( + 'description' => esc_html__( 'Sort collection by object attribute.', 'distributor' ), + 'type' => 'string', + 'default' => 'date', + 'enum' => array( + 'author', + 'date', + 'id', + 'include', + 'modified', + 'parent', + 'relevance', + 'slug', + 'title', + ), + ), ); } @@ -609,7 +625,8 @@ function get_pull_content_list( $request ) { ]; if ( ! empty( $request['search'] ) ) { - $args['s'] = rawurldecode( $request['search'] ); + $args['s'] = rawurldecode( $request['search'] ); + $args['orderby'] = 'relevance'; } if ( ! empty( $request['exclude'] ) ) { @@ -620,6 +637,30 @@ function get_pull_content_list( $request ) { $args['post__in'] = $request['include']; } + if ( ! empty( $request['orderby'] ) ) { + $args['orderby'] = $request['orderby']; + + if ( 'id' === $request['orderby'] ) { + // Flip the case to uppercase for WP_Query. + $args['orderby'] = 'ID'; + } elseif ( 'slug' === $request['orderby'] ) { + $args['orderby'] = 'name'; + } elseif ( 'relevance' === $request['orderby'] ) { + $args['orderby'] = 'relevance'; + + // If ordering by relevance, a search term must be defined. + if ( empty( $request['search'] ) ) { + return new WP_Error( + 'rest_no_search_term_defined', + __( 'You need to define a search term to order by relevance.', 'distributor' ), + array( 'status' => 400 ) + ); + } + } elseif ( 'include' === $request['orderby'] ) { + $args['orderby'] = 'post__in'; + } + } + /** * Filters WP_Query arguments when querying posts via the REST API. * From 918af12f86fb23c13c2f31fa8da6b1513bcacf25 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:28:57 +1000 Subject: [PATCH 29/57] Update docblock for new parameters. --- includes/rest-api.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 8d91d3510..88c21608f 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -159,7 +159,9 @@ function register_rest_routes() { } /** - * Set the accepted arguments for the pull content list endpoint + * Set the accepted arguments for the pull content list endpoint. + * + * @since x.x.x Introduced the include, order and orderby arguments. * * @return array */ From 0f6df4979c6debaebaaf5cd84e55559da4631dec Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:12:13 +1000 Subject: [PATCH 30/57] Fix misnamed date properties. --- includes/classes/DistributorPost.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/classes/DistributorPost.php b/includes/classes/DistributorPost.php index 4766c6e6e..d0cc43050 100644 --- a/includes/classes/DistributorPost.php +++ b/includes/classes/DistributorPost.php @@ -834,10 +834,10 @@ protected function to_pull_list( $args = array() ) { // Additional information required for pull screen. $display_data['ID'] = $this->post->ID; - $display_data['date'] = $this->post->post_date; - $display_data['date_gmt'] = $this->post->post_date_gmt; - $display_data['modified'] = $this->post->post_modified; - $display_data['modified_gmt'] = $this->post->post_modified_gmt; + $display_data['post_date'] = $this->post->post_date; + $display_data['post_date_gmt'] = $this->post->post_date_gmt; + $display_data['post_modified'] = $this->post->post_modified; + $display_data['post_modified_gmt'] = $this->post->post_modified_gmt; $display_data['post_password'] = $this->post->post_password; $display_data['guid'] = $this->post->guid; $display_data['comment_status'] = $this->post->comment_status; From 9958f8c6aa200278dde411dc2acdec9235b487c9 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:12:53 +1000 Subject: [PATCH 31/57] Avoid conflicting include and exclude clauses. --- includes/rest-api.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 88c21608f..d371f6b97 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -631,11 +631,17 @@ function get_pull_content_list( $request ) { $args['orderby'] = 'relevance'; } - if ( ! empty( $request['exclude'] ) ) { + if ( ! empty( $request['exclude'] ) && ! empty( $request['include'] ) ) { + /* + * Use only `post__in` if both `include` and `exclude` are populated. + * + * Excluded posts take priority over included posts, if the same post is + * included in both arrays, it will be excluded. + */ + $args['post__in'] = array_diff( $request['include'], $request['exclude'] ); + } elseif ( ! empty( $request['exclude'] ) ) { $args['post__not_in'] = $request['exclude']; - } - - if ( ! empty( $request['include'] ) ) { + } elseif ( ! empty( $request['include'] ) ) { $args['post__in'] = $request['include']; } From ec02077516587b8a9c8a57fa29322ef79053b5ab Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:15:07 +1000 Subject: [PATCH 32/57] Use custom endpoint for pulling single post. --- .../WordPressExternalConnection.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index c4dd13ea5..11c1fec80 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -284,7 +284,20 @@ public function remote_get( $args = array() ) { } if ( ! empty( $id ) ) { - $posts_url = untrailingslashit( $types_urls[ $post_type ] ) . '/' . $id . '/?context=' . $context; + $query_args = array( + 'include' => (int) $id, + 'post_type' => isset( $args['post_type'] ) ? $args['post_type'] : 'any', + ); + $posts_response = $this->remote_post( + untrailingslashit( $this->base_url ) . '/' . self::$namespace . '/distributor/list-pull-content', + $query_args + ); + + if ( is_wp_error( $posts_response ) ) { + return $posts_response; + } + + return $posts_response['items'][0]; } else { $posts_url = untrailingslashit( $types_urls[ $post_type ] ) . '/?' . $args_str; } From 1f6de6e8acbdbe81a29c3e6bbcfe506bd86b5bc0 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:17:00 +1000 Subject: [PATCH 33/57] Unset remote site details on one location. --- .../WordPressExternalConnection.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 11c1fec80..02ef7a9da 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -522,26 +522,21 @@ public function pull( $items ) { $post_array[ $key ] = $value; } + // Unset data from remote site. + unset( $post_array['ID'] ); + unset( $post_array['post_parent'] ); + unset( $post_array['post_date'] ); + unset( $post_array['post_date_gmt'] ); + unset( $post_array['post_modified'] ); + unset( $post_array['post_modified_gmt'] ); if ( ! empty( $item_array['post_id'] ) ) { $post_array['ID'] = $item_array['post_id']; - } else { - unset( $post_array['ID'] ); - } - - if ( isset( $post_array['post_parent'] ) ) { - unset( $post_array['post_parent'] ); } if ( ! empty( $item_array['post_status'] ) ) { $post_array['post_status'] = $item_array['post_status']; } - // Remove date stuff - unset( $post_array['post_date'] ); - unset( $post_array['post_date_gmt'] ); - unset( $post_array['post_modified'] ); - unset( $post_array['post_modified_gmt'] ); - /** * Filter the arguments passed into wp_insert_post during a pull. * From cb79bf7764ef77a01c3e1807c30bf11aa27a3e34 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:19:12 +1000 Subject: [PATCH 34/57] Use custom endpoint for pulling individual posts. Do in bulk. --- .../WordPressExternalConnection.php | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 02ef7a9da..fd2940f50 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -502,18 +502,31 @@ public function remote_post( $url = '', $args = array() ) { public function pull( $items ) { $created_posts = array(); + $remote_post_args = array( + 'include' => array(), + 'post_type' => array(), + ); foreach ( $items as $item_array ) { - $post = $this->remote_get( - [ - 'id' => $item_array['remote_post_id'], - 'post_type' => $item_array['post_type'], - ] - ); + $remote_post_args['include'][] = $item_array['remote_post_id']; + $remote_post_args['post_type'][] = $item_array['post_type']; + } + $remote_post_args['include'] = array_unique( $remote_post_args['include'] ); + $remote_post_args['post_type'] = array_unique( $remote_post_args['post_type'] ); + $remote_post_args['posts_per_page'] = count( $remote_post_args['include'] ); + + // Get all remote posts in a single request. + $remote_posts = $this->remote_post( + untrailingslashit( $this->base_url ) . '/' . self::$namespace . '/distributor/list-pull-content', + $remote_post_args + ); - if ( is_wp_error( $post ) ) { - $created_posts[] = $post; + foreach ( $items as $item_array ) { + $post = wp_list_filter( $remote_posts['items'], array( 'ID' => $item_array['remote_post_id'] ) ); + if ( empty( $post ) ) { + $created_posts[] = new \WP_Error( 'no-post', esc_html__( 'No post found.', 'distributor' ) ); continue; } + $post = $post[0]; $post_props = get_object_vars( $post ); $post_array = array(); From 8111b781553aaedcac13a8e6d4598cf9a7290156 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:19:36 +1000 Subject: [PATCH 35/57] Use `wp_update_post()` when updating existing posts. --- .../ExternalConnections/WordPressExternalConnection.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index fd2940f50..a559427d8 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -535,6 +535,7 @@ public function pull( $items ) { $post_array[ $key ] = $value; } + $update = false; // Unset data from remote site. unset( $post_array['ID'] ); unset( $post_array['post_parent'] ); @@ -542,7 +543,9 @@ public function pull( $items ) { unset( $post_array['post_date_gmt'] ); unset( $post_array['post_modified'] ); unset( $post_array['post_modified_gmt'] ); + if ( ! empty( $item_array['post_id'] ) ) { + $update = true; $post_array['ID'] = $item_array['post_id']; } @@ -564,7 +567,11 @@ public function pull( $items ) { * @return {array} The post data to be inserted. */ $new_post_args = Utils\post_args_allow_list( apply_filters( 'dt_pull_post_args', $post_array, $item_array['remote_post_id'], $post, $this ) ); - $new_post = wp_insert_post( wp_slash( $new_post_args ) ); + if ( $update ) { + $new_post = wp_update_post( wp_slash( $new_post_args ) ); + } else { + $new_post = wp_insert_post( wp_slash( $new_post_args ) ); + } update_post_meta( $new_post, 'dt_original_post_id', (int) $item_array['remote_post_id'] ); update_post_meta( $new_post, 'dt_original_source_id', (int) $this->id ); From 958c8e02bf4d91e4d7cd9d8551e0e9d4e0c963a2 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:29:15 +1000 Subject: [PATCH 36/57] Exit if remote request fails. --- .../ExternalConnections/WordPressExternalConnection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index a559427d8..7afecaa95 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -520,6 +520,10 @@ public function pull( $items ) { $remote_post_args ); + if ( is_wp_error( $remote_posts ) ) { + return $remote_posts; + } + foreach ( $items as $item_array ) { $post = wp_list_filter( $remote_posts['items'], array( 'ID' => $item_array['remote_post_id'] ) ); if ( empty( $post ) ) { From 3080b9f79a42358baf3ccc7f77660843e175c041 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:29:36 +1000 Subject: [PATCH 37/57] Use correct names for original site data. --- .../ExternalConnections/WordPressExternalConnection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 7afecaa95..3555bafaa 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -581,8 +581,8 @@ public function pull( $items ) { update_post_meta( $new_post, 'dt_original_source_id', (int) $this->id ); update_post_meta( $new_post, 'dt_syndicate_time', time() ); update_post_meta( $new_post, 'dt_original_post_url', esc_url_raw( $post_array['link'] ) ); - update_post_meta( $new_post, 'dt_original_site_name', sanitize_text_field( $post_array['original_site_name'] ) ); - update_post_meta( $new_post, 'dt_original_site_url', sanitize_text_field( $post_array['original_site_url'] ) ); + update_post_meta( $new_post, 'dt_original_site_name', sanitize_text_field( $post_array['distributor_original_site_name'] ) ); + update_post_meta( $new_post, 'dt_original_site_url', sanitize_text_field( $post_array['distributor_original_site_url'] ) ); if ( ! empty( $post->post_parent ) ) { update_post_meta( $new_post, 'dt_original_post_parent', (int) $post->post_parent ); From 83ecd7c3c85f34cd43211e31e4e0731b35962ddd Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:29:59 +1000 Subject: [PATCH 38/57] Fix pull test. --- tests/php/WordPressExternalConnectionTest.php | 25 ++---- tests/php/includes/common.php | 84 +++++++++++-------- 2 files changed, 59 insertions(+), 50 deletions(-) diff --git a/tests/php/WordPressExternalConnectionTest.php b/tests/php/WordPressExternalConnectionTest.php index c6878d78e..299ba2b47 100644 --- a/tests/php/WordPressExternalConnectionTest.php +++ b/tests/php/WordPressExternalConnectionTest.php @@ -257,12 +257,13 @@ public function test_pull() { $this->setup_post_meta_mock( array() ); $post_id = 123; - \WP_Mock::userFunction( 'wp_remote_retrieve_response_code' ); \WP_Mock::userFunction( 'untrailingslashit' ); \WP_Mock::userFunction( 'sanitize_text_field' ); remote_get_setup(); + \WP_Mock::passthruFunction( 'wp_slash' ); + \WP_Mock::passthruFunction( 'update_post_meta' ); \WP_Mock::userFunction( 'get_current_user_id' ); \WP_Mock::userFunction( 'delete_post_meta' ); @@ -284,24 +285,16 @@ public function test_pull() { ] ); - \WP_Mock::userFunction( - 'add_query_arg', [ - 'times' => 1, + $pull_actual = $this->connection->pull( + [ + [ + 'remote_post_id' => $post_id, + 'post_type' => 'post', + ], ] ); - $this->assertTrue( - is_array( - $this->connection->pull( - [ - [ - 'remote_post_id' => $post_id, - 'post_type' => 'post', - ], - ] - ) - ) - ); + $this->assertIsArray( $pull_actual ); } /** diff --git a/tests/php/includes/common.php b/tests/php/includes/common.php index d0c59f04f..6a4a21f40 100644 --- a/tests/php/includes/common.php +++ b/tests/php/includes/common.php @@ -157,50 +157,66 @@ function remote_get_setup() { \WP_Mock::userFunction( 'get_option' ); - $post_type = 'post'; - $links = [ - '_links' => [ - 'wp:items' => [ - [ 'href' => 'http://url.com' ], + $rest_response = [ + [ + 'post_title' => 'My post title', + 'post_name' => 'my-post-title', + 'post_type' => 'post', + 'post_content' => '', + 'post_excerpt' => '', + 'post_status' => 'publish', + 'terms' => [], + 'meta' => [], + 'media' => [], + 'post_author' => 1, + 'meta_input' => [ + 'dt_original_post_id' => 123, + 'dt_original_post_url' => 'http://example.com/2023/04/11/my-post-title/', ], + 'ID' => 123, + 'post_date' => '2023-04-11 05:40:43', + 'post_date_gmt' => '2023-04-11 05:40:43', + 'post_modified' => '2023-04-11 05:40:43', + 'post_modified_gmt' => '2023-04-11 05:40:43', + 'post_password' => '', + 'guid' => 'http://example.com/?p=123', + 'comment_status' => 'open', + 'ping_status' => 'open', + 'link' => 'http://example.com/2023/04/11/my-post-title/', + 'distributor_original_site_name' => 'My site name', + 'distributor_original_site_url' => 'http://example.com/', ], ]; \WP_Mock::userFunction( - 'wp_remote_request', [ - 'return' => json_encode( - [ - $post_type => $links, - ] - ), + 'wp_remote_post', [ + 'return' => new stdClass(), + ] + ); + + \WP_Mock::userFunction( + 'wp_remote_retrieve_headers', [ + 'return' => [ + 'X-Distributor' => 'yes', + ], ] ); - // todo: the response is missing post_type \WP_Mock::userFunction( 'wp_remote_retrieve_body', [ - 'return' => json_encode( - [ - 'id' => 123, - 'title' => [ 'rendered' => 'My post title' ], - 'content' => [ 'rendered' => '', 'raw' => '' ], - 'excerpt' => [ 'rendered' => '' ], - 'date' => '', - 'date_gmt' => '', - 'guid' => [ 'rendered' => '' ], - 'modified' => '', - 'modified_gmt' => '', - 'type' => '', - 'link' => '', - 'distributor_meta' => [], - 'distributor_terms' => [], - 'distributor_media' => [], - $post_type => $links, - 'comment_status' => 'open', - 'ping_status' => 'open', - 'password' => '', - ] - ), + 'return' => json_encode( $rest_response ), + ] + ); + + \WP_Mock::userFunction( + 'wp_remote_retrieve_response_code', [ + 'return' => 200, + ] + ); + + \WP_Mock::userFunction( + 'wp_list_filter', [ + 'return' => [ new WP_Post( (object) $rest_response[0] ) ], ] ); } From 619d50a102b7281099c1658c5be9aac42e6c6275 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:58:51 +1000 Subject: [PATCH 39/57] Always use post request for list screen. --- .../classes/ExternalConnections/WordPressExternalConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 3555bafaa..15e270f50 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -168,7 +168,7 @@ public function remote_get( $args = array() ) { } // When running a query for the Pull screen, make a POST request instead - if ( empty( $id ) && isset( $args['dt_pull_list'] ) ) { + if ( empty( $id ) ) { $query_args['post_type'] = isset( $post_type ) ? $post_type : 'post'; $query_args['posts_per_page'] = isset( $posts_per_page ) ? $posts_per_page : 20; From a2b95c49fda1ba83b7c4b47f3c278302d2d0e537 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:59:39 +1000 Subject: [PATCH 40/57] Remove types check, pull no longer uses WP default endpoints. --- .../WordPressExternalConnection.php | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 15e270f50..f8622c751 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -180,54 +180,6 @@ public function remote_get( $args = array() ) { return $posts_response; } - static $types_urls; - $types_urls = array(); - - if ( empty( $types_urls[ $post_type ] ) ) { - /** - * First let's get the actual route if not cached. We don't know the "plural" of our post type - */ - - /** - * Todo: This should be cached in a transient - */ - - $path = self::$namespace; - - $types_path = untrailingslashit( $this->base_url ) . '/' . $path . '/types'; - - $types_response = Utils\remote_http_request( - $types_path, - $this->auth_handler->format_get_args( array( 'timeout' => self::$timeout ) ) - ); - - if ( is_wp_error( $types_response ) ) { - return $types_response; - } - - if ( 404 === wp_remote_retrieve_response_code( $types_response ) ) { - return new \WP_Error( 'bad-endpoint', esc_html__( 'Could not connect to API endpoint.', 'distributor' ) ); - } - - $types_body = wp_remote_retrieve_body( $types_response ); - - if ( empty( $types_body ) ) { - return new \WP_Error( 'no-response-body', esc_html__( 'Response body is empty.', 'distributor' ) ); - } - - $types_body_array = json_decode( $types_body, true ); - - if ( empty( $types_body_array ) || empty( $types_body_array[ $post_type ] ) ) { - return new \WP_Error( 'no-pull-post-type', esc_html__( 'Could not determine remote post type endpoint.', 'distributor' ) ); - } - - $types_urls[ $post_type ] = $this->parse_type_items_link( $types_body_array[ $post_type ] ); - - if ( empty( $types_urls[ $post_type ] ) ) { - return new \WP_Error( 'no-pull-post-type', esc_html__( 'Could not determine remote post type endpoint.', 'distributor' ) ); - } - } - $args_str = ''; if ( ! empty( $posts_per_page ) ) { From c8a11e7fb85162ab9e6b68536b206a898922e616 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:01:36 +1000 Subject: [PATCH 41/57] Use POST request for pulling single post. --- .../WordPressExternalConnection.php | 148 +----------------- 1 file changed, 8 insertions(+), 140 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index f8622c751..aff4b141a 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -186,152 +186,20 @@ public function remote_get( $args = array() ) { $args_str .= 'per_page=' . (int) $posts_per_page; } - /** - * Filter the remote_get query arguments - * - * @since 1.0 - * @hook dt_remote_get_query_args - * - * @param {array} $query_args The existing query arguments. - * @param {array} $args The arguments originally passed to `remote_get`. - * @param {object} $this The authentication class. - * - * @return {array} The existing query arguments. - */ - $query_args = apply_filters( 'dt_remote_get_query_args', $query_args, $args, $this ); - - foreach ( $query_args as $arg_key => $arg_value ) { - if ( is_array( $arg_value ) ) { - foreach ( $arg_value as $arg_value_value ) { - if ( ! empty( $args_str ) ) { - $args_str .= '&'; - } - - $args_str .= $arg_key . '[]=' . $arg_value_value; - } - } else { - if ( ! empty( $args_str ) ) { - $args_str .= '&'; - } - - $args_str .= $arg_key . '=' . $arg_value; - } - } - - $context = 'view'; - - $prelim_get_args = $this->auth_handler->format_get_args(); - - /** - * See if we are trying to authenticate - */ - if ( ! empty( $prelim_get_args ) && ! empty( $prelim_get_args['headers'] ) && ! empty( $prelim_get_args['headers']['Authorization'] ) ) { - $context = 'edit'; - - if ( ! empty( $args_str ) ) { - $args_str .= '&'; - } - - $args_str .= 'context=edit'; - } - - if ( ! empty( $id ) ) { - $query_args = array( - 'include' => (int) $id, - 'post_type' => isset( $args['post_type'] ) ? $args['post_type'] : 'any', - ); - $posts_response = $this->remote_post( - untrailingslashit( $this->base_url ) . '/' . self::$namespace . '/distributor/list-pull-content', - $query_args - ); - - if ( is_wp_error( $posts_response ) ) { - return $posts_response; - } - - return $posts_response['items'][0]; - } else { - $posts_url = untrailingslashit( $types_urls[ $post_type ] ) . '/?' . $args_str; - } - - // Add request parameter to specify Distributor request - $posts_url = add_query_arg( 'distributor_request', '1', $posts_url ); - - $posts_response = Utils\remote_http_request( - /** - * Filter the URL that remote_get will use - * - * @since 1.0 - * @hook dt_remote_get_url - * - * @param {string} $posts_url The posts URL - * @param {string} $args The arguments originally passed to `remote_get`. - * @param {object} $this The authentication class. - * - * @return {string} The posts URL. - */ - apply_filters( 'dt_remote_get_url', $posts_url, $args, $this ), - // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout -- false positive, shorter on VIP. - $this->auth_handler->format_get_args( array( 'timeout' => 45 ) ) + $query_args = array( + 'include' => (int) $id, + 'post_type' => isset( $args['post_type'] ) ? $args['post_type'] : 'any', + ); + $posts_response = $this->remote_post( + untrailingslashit( $this->base_url ) . '/wp/v2/distributor/list-pull-content', + $query_args ); if ( is_wp_error( $posts_response ) ) { return $posts_response; } - $response_code = wp_remote_retrieve_response_code( $posts_response ); - - if ( 200 !== $response_code ) { - - if ( 404 === $response_code ) { - return new \WP_Error( 'bad-endpoint', esc_html__( 'Could not connect to API endpoint.', 'distributor' ) ); - } - - $posts_body = json_decode( wp_remote_retrieve_body( $posts_response ), true ); - - $code = empty( $posts_body['code'] ) ? 'endpoint-error' : esc_html( $posts_body['code'] ); - $message = empty( $posts_body['message'] ) ? esc_html__( 'API endpoint error.', 'distributor' ) : esc_html( $posts_body['message'] ); - - return new \WP_Error( $code, $message ); - } - - $posts_body = wp_remote_retrieve_body( $posts_response ); - - if ( empty( $posts_body ) ) { - return new \WP_Error( 'no-response-body', esc_html__( 'Response body is empty.', 'distributor' ) ); - } - - $posts = json_decode( $posts_body, true ); - $formatted_posts = array(); - - $response_headers = wp_remote_retrieve_headers( $posts_response ); - - if ( empty( $id ) ) { - foreach ( $posts as $post ) { - $post['full_connection'] = ( ! empty( $response_headers['X-Distributor'] ) ); - - $formatted_posts[] = $this->to_wp_post( $post ); - } - - $total_posts = wp_remote_retrieve_header( $posts_response, 'X-WP-Total' ); - if ( empty( $total_posts ) ) { - $total_posts = count( $formatted_posts ); - } - - // Filter documented above. - return apply_filters( - 'dt_remote_get', - [ - 'items' => $formatted_posts, - 'total_items' => $total_posts, - ], - $args, - $this - ); - } else { - // Filter documented above. - return apply_filters( 'dt_remote_get', $this->to_wp_post( $posts ), $args, $this ); - } + return $posts_response['items'][0]; } /** From ba2c9074525fcabf608751bcd717d88efcbce400 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:02:47 +1000 Subject: [PATCH 42/57] Remove duplicate wp_remote_retrieve_headers mock. --- tests/php/includes/common.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/php/includes/common.php b/tests/php/includes/common.php index 6a4a21f40..3eb11a602 100644 --- a/tests/php/includes/common.php +++ b/tests/php/includes/common.php @@ -195,10 +195,8 @@ function remote_get_setup() { ); \WP_Mock::userFunction( - 'wp_remote_retrieve_headers', [ - 'return' => [ - 'X-Distributor' => 'yes', - ], + 'wp_remote_request', [ + 'return' => new stdClass(), ] ); From 24834904a2de50ae950ed324a7e73d36e199155b Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:03:18 +1000 Subject: [PATCH 43/57] Add wp_remote_retrieve_headers mock to pull test. --- tests/php/WordPressExternalConnectionTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/php/WordPressExternalConnectionTest.php b/tests/php/WordPressExternalConnectionTest.php index 299ba2b47..9d4dea74e 100644 --- a/tests/php/WordPressExternalConnectionTest.php +++ b/tests/php/WordPressExternalConnectionTest.php @@ -267,6 +267,14 @@ public function test_pull() { \WP_Mock::userFunction( 'get_current_user_id' ); \WP_Mock::userFunction( 'delete_post_meta' ); + \WP_Mock::userFunction( + 'wp_remote_retrieve_headers', [ + 'return' => [ + 'X-Distributor' => 'yes', + ], + ] + ); + \WP_Mock::userFunction( 'wp_insert_post', [ 'return' => 2, From bc5772d824b94277b5550cbccfcfa8076da1f47b Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:03:43 +1000 Subject: [PATCH 44/57] Modify mocks for new flow wrapping post requests. --- tests/php/WordPressExternalConnectionTest.php | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/tests/php/WordPressExternalConnectionTest.php b/tests/php/WordPressExternalConnectionTest.php index 9d4dea74e..6c8b587fa 100644 --- a/tests/php/WordPressExternalConnectionTest.php +++ b/tests/php/WordPressExternalConnectionTest.php @@ -321,7 +321,6 @@ public function test_remote_get() { \WP_Mock::userFunction( 'wp_remote_retrieve_response_code', [ - 'times' => 2, 'return' => 200, ] ); @@ -335,12 +334,6 @@ public function test_remote_get() { ] ); - \WP_Mock::userFunction( - 'add_query_arg', [ - 'times' => 1, - ] - ); - \WP_Mock::userFunction( 'post_type_exists', [ 'args' => [ '' ], @@ -362,15 +355,14 @@ public function test_remote_get() { ] ); - $this->assertInstanceOf( - \WP_Post::class, $this->connection->remote_get( - [ - 'id' => 111, - 'post_type' => 'post', - ] - ) + $actual = $this->connection->remote_get( + [ + 'id' => 111, + 'post_type' => 'post', + ] ); + $this->assertInstanceOf( \WP_Post::class, $actual ); } /** From e0f4d31af526ed27a043623e5b3282588a270dec Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:13:07 +1000 Subject: [PATCH 45/57] Remove unused variable. --- .../ExternalConnections/WordPressExternalConnection.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index aff4b141a..64812e294 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -180,12 +180,6 @@ public function remote_get( $args = array() ) { return $posts_response; } - $args_str = ''; - - if ( ! empty( $posts_per_page ) ) { - $args_str .= 'per_page=' . (int) $posts_per_page; - } - $query_args = array( 'include' => (int) $id, 'post_type' => isset( $args['post_type'] ) ? $args['post_type'] : 'any', From 0df5b63291697324184bc20334657331f4666ff0 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 18 Apr 2023 16:37:05 +1000 Subject: [PATCH 46/57] Use reset rather than zero as list_filter maintains key. --- .../classes/ExternalConnections/WordPressExternalConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 64812e294..2ab09e775 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -344,7 +344,7 @@ public function pull( $items ) { $created_posts[] = new \WP_Error( 'no-post', esc_html__( 'No post found.', 'distributor' ) ); continue; } - $post = $post[0]; + $post = reset( $post ); $post_props = get_object_vars( $post ); $post_array = array(); From e7cbbc3d4376e45324d2af014ad207e240903fd2 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 19 Apr 2023 12:12:59 +1000 Subject: [PATCH 47/57] Correct site meta source data. --- .../ExternalConnections/WordPressExternalConnection.php | 4 ++-- tests/php/includes/common.php | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 2ab09e775..ea1755837 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -395,8 +395,8 @@ public function pull( $items ) { update_post_meta( $new_post, 'dt_original_source_id', (int) $this->id ); update_post_meta( $new_post, 'dt_syndicate_time', time() ); update_post_meta( $new_post, 'dt_original_post_url', esc_url_raw( $post_array['link'] ) ); - update_post_meta( $new_post, 'dt_original_site_name', sanitize_text_field( $post_array['distributor_original_site_name'] ) ); - update_post_meta( $new_post, 'dt_original_site_url', sanitize_text_field( $post_array['distributor_original_site_url'] ) ); + update_post_meta( $new_post, 'dt_original_site_name', sanitize_text_field( $post_array['original_site_name'] ) ); + update_post_meta( $new_post, 'dt_original_site_url', sanitize_text_field( $post_array['original_site_url'] ) ); if ( ! empty( $post->post_parent ) ) { update_post_meta( $new_post, 'dt_original_post_parent', (int) $post->post_parent ); diff --git a/tests/php/includes/common.php b/tests/php/includes/common.php index 3eb11a602..84deb0257 100644 --- a/tests/php/includes/common.php +++ b/tests/php/includes/common.php @@ -188,6 +188,12 @@ function remote_get_setup() { ], ]; + $post_response = $rest_response[0]; + $post_response['original_site_name'] = $post_response['distributor_original_site_name']; + $post_response['original_site_url'] = $post_response['distributor_original_site_url']; + unset( $post_response['distributor_original_site_name'] ); + unset( $post_response['distributor_original_site_url'] ); + \WP_Mock::userFunction( 'wp_remote_post', [ 'return' => new stdClass(), @@ -214,7 +220,7 @@ function remote_get_setup() { \WP_Mock::userFunction( 'wp_list_filter', [ - 'return' => [ new WP_Post( (object) $rest_response[0] ) ], + 'return' => [ new WP_Post( (object) $post_response ) ], ] ); } From fa682463d32dc39877898af0270c75a6853f1f36 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 26 Apr 2023 10:35:22 +1000 Subject: [PATCH 48/57] Sanitize and slash post meta as appropriate. --- .../classes/InternalConnections/NetworkSiteConnection.php | 6 +++--- phpcs.xml | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 5a25d406b..25f67a617 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -301,9 +301,9 @@ public function pull( $items ) { if ( ! is_wp_error( $new_post_id ) ) { update_post_meta( $new_post_id, 'dt_original_blog_id', (int) $this->site->blog_id ); - update_post_meta( $new_post_id, 'dt_syndicate_time', time() ); - update_post_meta( $new_post_id, 'dt_original_post_id', $new_post_args['meta_input']['dt_original_post_id'] ); - update_post_meta( $new_post_id, 'dt_original_post_url', $new_post_args['meta_input']['dt_original_post_url'] ); + update_post_meta( $new_post_id, 'dt_syndicate_time', (int) time() ); + update_post_meta( $new_post_id, 'dt_original_post_id', (int) $new_post_args['meta_input']['dt_original_post_id'] ); + update_post_meta( $new_post_id, 'dt_original_post_url', wp_slash( sanitize_url( $new_post_args['meta_input']['dt_original_post_url'] ) ) ); /** * Allow bypassing of all meta processing. diff --git a/phpcs.xml b/phpcs.xml index 3288dc58d..df4ee2967 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,9 @@ - + + + + From 9dd57461fd98b2067c5f3e5ea051f9180edb6b24 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 26 Apr 2023 10:37:39 +1000 Subject: [PATCH 49/57] Replace placeholder versions with 2.0.0. --- includes/rest-api.php | 8 ++++---- includes/utils.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index d371f6b97..ebc628b0f 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -161,7 +161,7 @@ function register_rest_routes() { /** * Set the accepted arguments for the pull content list endpoint. * - * @since x.x.x Introduced the include, order and orderby arguments. + * @since 2.0.0 Introduced the include, order and orderby arguments. * * @return array */ @@ -612,7 +612,7 @@ function check_post_types_permissions() { /** * Get a list of content to show on the Pull screen * - * @since x.x.x Renamed from get_pull_content() to get_pull_content_list(). + * @since 2.0.0 Renamed from get_pull_content() to get_pull_content_list(). * * @param \WP_Rest_Request $request API request arguments * @return \WP_REST_Response|\WP_Error @@ -725,13 +725,13 @@ function get_pull_content_list( $request ) { /** * Get a list of content to show on the Pull screen * - * @since x.x.x Deprecated in favour of get_pull_content_list(). + * @since 2.0.0 Deprecated in favour of get_pull_content_list(). * * @param array ...$args Arguments. * @return \WP_REST_Response|\WP_Error */ function get_pull_content( ...$args ) { - _deprecated_function( __FUNCTION__, 'x.x.x', __NAMESPACE__ . '\\get_pull_content_list' ); + _deprecated_function( __FUNCTION__, '2.0.0', __NAMESPACE__ . '\\get_pull_content_list' ); return get_pull_content_list( ...$args ); } diff --git a/includes/utils.php b/includes/utils.php index d51dc05a3..c483a766c 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -12,7 +12,7 @@ /** * Determine if this is a development install of Distributor. * - * @since x.x.x + * @since 2.0.0 * * @return bool True if this is a development install, false otherwise. */ From 827d4d6d50b2be47636e2b990198730dcf1c7a1d Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 26 Apr 2023 10:41:14 +1000 Subject: [PATCH 50/57] Fix post status argument sanitization. --- includes/rest-api.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index ebc628b0f..94a93a66b 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -307,13 +307,13 @@ function( $post_type ) { } /* - * Only show post statuses viewable post statues. + * Only show viewable post statues. * - * `is_post_type_viewable()` is used to filter the results as + * `is_post_status_viewable()` is used to filter the results as * WordPress applies a complex set of rules to determine if a post * status is viewable. */ - $allowed_statues = array_keys( array_filter( get_post_stati(), 'is_post_type_viewable' ) ); + $allowed_statues = array_keys( array_filter( get_post_stati(), 'is_post_status_viewable' ) ); if ( in_array( 'any', $param, true ) ) { return $allowed_statues; From e747fb664af4d1e3b1fff4a0ba63b68d0b2353b0 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Wed, 26 Apr 2023 10:45:28 +1000 Subject: [PATCH 51/57] Mock sanitize_url() as passthrough function. --- tests/php/NetworkSiteConnectionsTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/php/NetworkSiteConnectionsTest.php b/tests/php/NetworkSiteConnectionsTest.php index 85f3b61c9..6cef652c8 100644 --- a/tests/php/NetworkSiteConnectionsTest.php +++ b/tests/php/NetworkSiteConnectionsTest.php @@ -265,6 +265,7 @@ public function test_pull() { \WP_Mock::userFunction( 'get_current_blog_id' ); \WP_Mock::userFunction( 'remove_filter' ); \WP_Mock::passthruFunction( 'wp_slash' ); + \WP_Mock::passthruFunction( 'sanitize_url' ); \WP_Mock::userFunction( 'get_post', [ From 27b729cb54299bb79cf0b9ab192631d696070f0d Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:18:08 +1000 Subject: [PATCH 52/57] Use absint for positive integers. --- .../WordPressExternalConnection.php | 2 +- .../InternalConnections/NetworkSiteConnection.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index ea1755837..7815c6b60 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -181,7 +181,7 @@ public function remote_get( $args = array() ) { } $query_args = array( - 'include' => (int) $id, + 'include' => absint( $id ), 'post_type' => isset( $args['post_type'] ) ? $args['post_type'] : 'any', ); $posts_response = $this->remote_post( diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 25f67a617..7a9861636 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -161,8 +161,8 @@ public function push( $post, $args = array() ) { $output['id'] = $new_post_id; - update_post_meta( $new_post_id, 'dt_original_blog_id', (int) $original_blog_id ); - update_post_meta( $new_post_id, 'dt_syndicate_time', time() ); + update_post_meta( $new_post_id, 'dt_original_blog_id', absint( $original_blog_id ) ); + update_post_meta( $new_post_id, 'dt_syndicate_time', absint( time() ) ); /** * Allow bypassing of all meta processing. @@ -300,9 +300,9 @@ public function pull( $items ) { remove_filter( 'wp_insert_post_data', array( '\Distributor\InternalConnections\NetworkSiteConnection', 'maybe_set_modified_date' ), 10, 2 ); if ( ! is_wp_error( $new_post_id ) ) { - update_post_meta( $new_post_id, 'dt_original_blog_id', (int) $this->site->blog_id ); - update_post_meta( $new_post_id, 'dt_syndicate_time', (int) time() ); - update_post_meta( $new_post_id, 'dt_original_post_id', (int) $new_post_args['meta_input']['dt_original_post_id'] ); + update_post_meta( $new_post_id, 'dt_original_blog_id', absint( $this->site->blog_id ) ); + update_post_meta( $new_post_id, 'dt_syndicate_time', absint( time() ) ); + update_post_meta( $new_post_id, 'dt_original_post_id', absint( $new_post_args['meta_input']['dt_original_post_id'] ) ); update_post_meta( $new_post_id, 'dt_original_post_url', wp_slash( sanitize_url( $new_post_args['meta_input']['dt_original_post_url'] ) ) ); /** From 62d165fe17ce35010d197ebdd4e076800f96a311 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:20:54 +1000 Subject: [PATCH 53/57] Escape `\` in callback. --- includes/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index 94a93a66b..c83cf08de 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -151,7 +151,7 @@ function register_rest_routes() { 'distributor/list-pull-content', array( 'methods' => 'POST', - 'callback' => __NAMESPACE__ . '\get_pull_content_list', + 'callback' => __NAMESPACE__ . '\\get_pull_content_list', 'permission_callback' => __NAMESPACE__ . '\\get_pull_content_permissions', 'args' => get_pull_content_list_args(), ) From 0efefdad961cb60de88776c43af3046c81065362 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:22:58 +1000 Subject: [PATCH 54/57] Remove duplicate `validate_callback` entry. --- includes/rest-api.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/rest-api.php b/includes/rest-api.php index c83cf08de..2445df0a5 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -274,7 +274,6 @@ function( $post_type ) { return $param; }, - 'validate_callback' => 'rest_validate_request_arg', ), 'search' => array( 'description' => esc_html__( 'Limit results to those matching a string.', 'distributor' ), From 671837f4e402c9457710c9b6a20e6a20b1598f43 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:29:16 +1000 Subject: [PATCH 55/57] Add since tag annotation. --- includes/classes/DistributorPost.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/classes/DistributorPost.php b/includes/classes/DistributorPost.php index d0cc43050..73278d7a8 100644 --- a/includes/classes/DistributorPost.php +++ b/includes/classes/DistributorPost.php @@ -823,6 +823,8 @@ protected function to_insert( $args = array() ) { * This is a wrapper for the ::to_insert() method that includes extra * data required for the pull screen. * + * @since 2.0.0 + * * @param mixed $args { * Optional. Array of push arguments * @see ::to_insert() for arguments. From 18eb1e4ca46abff50195e5cc748de3152dbde716 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:39:52 +1000 Subject: [PATCH 56/57] Reproduce absint for test suite. --- tests/php/includes/common.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/php/includes/common.php b/tests/php/includes/common.php index 84deb0257..e5100d37d 100644 --- a/tests/php/includes/common.php +++ b/tests/php/includes/common.php @@ -296,6 +296,20 @@ function wp_parse_args( $settings, $defaults ) { return array_merge( $defaults, $settings ); } +/** + * Mock absint() function. + * + * Copied from WordPress core. + * + * @since 2.0.0 + * + * @param mixed $maybeint Data you wish to have converted to a non-negative integer. + * @return int A non-negative integer. + */ +function absint( $maybeint ) { + return abs( (int) $maybeint ); +} + /** * Stub for remove_filter to avoid failure in test_remote_get() * From c83fde14ce73b81556adf6cd4f77ce0c63737fd1 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 25 May 2023 13:53:41 +1000 Subject: [PATCH 57/57] Version check external version of Distributor on pull screen. --- .../WordPressExternalConnection.php | 20 +++++++++++++++++ includes/pull-ui.php | 22 ++++++++++--------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 7815c6b60..01c5d9389 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -271,6 +271,26 @@ public function remote_post( $url = '', $args = array() ) { return new \WP_Error( 'no-response-body', esc_html__( 'Response body is empty.', 'distributor' ) ); } + if ( + false === Utils\is_development_version() + && isset( $response_headers['x-distributor'] ) + && ( + ! isset( $response_headers['x-distributor-version'] ) + || version_compare( $response_headers['x-distributor-version'], '2.0.0', '<' ) + ) + ) { + $version_error = new \WP_Error(); + $version_error->add( + 'old-distributor-version', + esc_html__( 'Pulling content from external connections requires Distributor version 2.0.0 or later.', 'distributor' ) + ); + $version_error->add( + 'old-distributor-version', + esc_html__( 'Please update Distributor on the site you are pulling content from.', 'distributor' ) + ); + return $version_error; + } + $posts = json_decode( $posts_body, true ); $formatted_posts = array(); diff --git a/includes/pull-ui.php b/includes/pull-ui.php index 14226b813..f56c02a73 100644 --- a/includes/pull-ui.php +++ b/includes/pull-ui.php @@ -529,16 +529,18 @@ function dashboard() { prepare_items(); ?> pull_error ) ) : ?> -

-
    - pull_error as $error ) : ?> -
  • -
      -
    • -
    -
  • - -
+
+

+
    + pull_error as $error ) : ?> +
  • +
      +
    • +
    +
  • + +
+
views(); ?>