-
Notifications
You must be signed in to change notification settings - Fork 4
feat(content-distribution): migrator #185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
6400e65
feat(content-distribution): migrator
miguelpeixe 07f8774
feat: link existing incoming post
miguelpeixe 99b39d4
chore: fix docblock
miguelpeixe 22015c4
fix(content-distribution): persist site hash
miguelpeixe ed4cf73
Merge branch 'fix/content-distribution-site-hash' into feat/content-d…
miguelpeixe 243c6bf
feat: cli
miguelpeixe 39185b9
chore: remove copilot hallucination
miguelpeixe b73b6b3
fix: connection map clear
miguelpeixe 3751782
fix(cli): validate subscriptions before migrating
miguelpeixe daefaf1
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe 65804e7
feat(cli): support deletion
miguelpeixe 32baad4
chore: lint
miguelpeixe 4cb530a
feat: link payload and support unlinked state
miguelpeixe 46523d1
fix: optimize subscriptions query
miguelpeixe c57e511
fix(cli): edge cases and better messaging
miguelpeixe 2b337d9
fix(cli): add optional strict mode and migrate via posts
miguelpeixe 156a28a
chore: improve cli description
miguelpeixe c3f5c1f
chore: improve cli description
miguelpeixe 1850afd
fix: migrate incoming posts via data events
miguelpeixe e52bba2
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe 8853e8e
fix: log batch migrated
miguelpeixe a7d410e
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe 0c63bee
chore: update docblock
miguelpeixe c2f2dcc
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe 0bfa9a7
chore: remove unused imports
miguelpeixe ad8fdd5
fix: typo
miguelpeixe 893ed4e
fix: rename function
miguelpeixe ecd3d70
fix: typo
miguelpeixe 1237389
fix: add error
miguelpeixe 9bb8a15
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe 9d5a2b4
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe 1d5f4d1
fix: incoming post migration
miguelpeixe 6f7af2c
chore: improve log
miguelpeixe 65a3b09
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe eef612a
fix: remove distributor meta from payload
miguelpeixe 77ff94b
fix: network post id
miguelpeixe f6a439d
fix: use current post status as `status_on_create`
miguelpeixe ce95fa7
fix: ensure default post status
miguelpeixe 0a731b8
fix: use `wp_update_post()` for existing posts
miguelpeixe 5d991c1
fix: ensure the latest post data on insert
miguelpeixe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
234 changes: 234 additions & 0 deletions
234
includes/content-distribution/class-distributor-migrator.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
<?php | ||
/** | ||
* Newspack Network Content Distribution Distributor Migrator. | ||
* | ||
* @package Newspack | ||
*/ | ||
|
||
namespace Newspack_Network\Content_Distribution; | ||
|
||
use Newspack_Network\Content_Distribution; | ||
use Newspack_Network\Utils\Network; | ||
use WP_Error; | ||
use WP_Query; | ||
use WP_Post; | ||
use InvalidArgumentException; | ||
use WP_REST_Request; | ||
use WP_REST_Response; | ||
|
||
/** | ||
* Distributor Migrator Class. | ||
*/ | ||
class Distributor_Migrator { | ||
/** | ||
* Initialize hooks. | ||
*/ | ||
public static function init() { | ||
add_action( 'rest_api_init', [ __CLASS__, 'register_rest_routes' ] ); | ||
} | ||
|
||
/** | ||
* Register REST routes. | ||
*/ | ||
public static function register_rest_routes() { | ||
register_rest_route( | ||
'newspack-network/v1', | ||
'/content-distribution/distributor-migrator/link/(?P<post_id>\d+)', | ||
[ | ||
'methods' => 'POST', | ||
'callback' => [ __CLASS__, 'api_link_incoming_post' ], | ||
'args' => [ | ||
'subscription_signature' => [ | ||
'type' => 'string', | ||
'required' => true, | ||
], | ||
'network_post_id' => [ | ||
'type' => 'string', | ||
'required' => true, | ||
], | ||
], | ||
'permission_callback' => '__return_true', // TODO: Check network signature. | ||
miguelpeixe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
] | ||
); | ||
} | ||
|
||
/** | ||
* API callback to link a migrated incoming post. | ||
* | ||
* @param WP_REST_Request $request The REST request object. | ||
* | ||
* @return WP_REST_Response|WP_Error The REST response or error. | ||
*/ | ||
public static function api_link_incoming_post( $request ) { | ||
$post_id = $request->get_param( 'post_id' ); | ||
$subscription_signature = $request->get_param( 'subscription_signature' ); | ||
$network_post_id = $request->get_param( 'network_post_id' ); | ||
|
||
$response = self::link_incoming_post( $post_id, $subscription_signature, $network_post_id ); | ||
|
||
if ( is_wp_error( $response ) ) { | ||
return $response; | ||
} | ||
|
||
return new WP_REST_Response( null, 200 ); | ||
} | ||
|
||
/** | ||
* Link an incoming post given its subscription signature. | ||
* | ||
* @param int $post_id The ID of the post to link. | ||
* @param string $subscription_signature The signature of the subscription. | ||
* @param string $network_post_id The network post ID. | ||
* | ||
* @return WP_Error|void WP_Error on failure, void on success. | ||
*/ | ||
protected static function link_incoming_post( $post_id, $subscription_signature, $network_post_id ) { | ||
$post = get_post( $post_id ); | ||
if ( ! $post ) { | ||
return new WP_Error( 'post_not_found', __( 'Post not found.', 'newspack-network' ) ); | ||
} | ||
|
||
$post_signature = get_post_meta( $post_id, 'dt_subscription_signature', true ); | ||
if ( $post_signature !== $subscription_signature ) { | ||
return new WP_Error( 'subscription_signature_mismatch', __( 'Subscription signature mismatch.', 'newspack-network' ) ); | ||
} | ||
|
||
update_post_meta( $post_id, Incoming_Post::NETWORK_POST_ID_META, $network_post_id ); | ||
} | ||
|
||
/** | ||
* Get all Distributor subscriptions. | ||
* | ||
* @return WP_Post[] Array of WP_Post objects representing Distributor subscriptions. | ||
*/ | ||
public static function get_distributor_subscriptions() { | ||
$query = new WP_Query( | ||
[ | ||
'post_type' => 'dt_subscription', | ||
'posts_per_page' => '-1', | ||
] | ||
); | ||
return $query->posts; | ||
} | ||
|
||
/** | ||
* Migrate a post subscription from Distributor to Newspack Network Content Distribution. | ||
* | ||
* @param int $subscription_id The ID of the subscription to migrate. | ||
* @param boolean $distribute Whether to distribute the post after migrating the subscription. | ||
* | ||
* @return Outgoing_Post|WP_Error Outgoing_Post on success, WP_Error on failure. | ||
*/ | ||
public static function migrate_subscription( $subscription_id, $distribute = false ) { | ||
$subscription = get_post( $subscription_id ); | ||
if ( ! $subscription ) { | ||
return new WP_Error( 'subscription_not_found', __( 'Subscription not found.', 'newspack-network' ) ); | ||
} | ||
|
||
$post_id = get_post_meta( $subscription_id, 'dt_subscription_post_id', true ); | ||
$post = get_post( $post_id ); | ||
|
||
if ( ! $post_id || ! $post || empty( $post->ID ) ) { | ||
return new WP_Error( 'post_not_found', __( 'Post not found.', 'newspack-network' ) ); | ||
} | ||
|
||
$target_url = get_post_meta( $subscription_id, 'dt_subscription_target_url', true ); | ||
|
||
if ( ! $target_url ) { | ||
return new WP_Error( 'target_url_not_found', __( 'Target URL not found.', 'newspack-network' ) ); | ||
} | ||
|
||
$network_urls = Network::get_networked_urls(); | ||
$network_url = array_filter( | ||
$network_urls, | ||
function( $url ) use ( $target_url ) { | ||
return false !== strpos( $target_url, $url ); | ||
} | ||
); | ||
$network_url = array_shift( $network_url ); | ||
|
||
if ( empty( $network_url ) ) { | ||
return new WP_Error( | ||
'target_url_not_networked', | ||
sprintf( | ||
// translators: target URL. | ||
__( 'Target URL "%s" is not networked.', 'newspack-network' ), | ||
$target_url | ||
) | ||
); | ||
} | ||
|
||
// Configure distribution. | ||
try { | ||
$outgoing_post = new Outgoing_Post( $post ); | ||
} catch ( InvalidArgumentException $e ) { | ||
return new WP_Error( 'outgoing_post_error', $e->getMessage() ); | ||
} | ||
$distribution = $outgoing_post->set_distribution( [ $network_url ] ); | ||
if ( | ||
is_wp_error( $distribution ) && | ||
// Ignore error if the post is already distributed. | ||
'update_failed' !== $distribution->get_error_code() | ||
) { | ||
return $distribution; | ||
} | ||
|
||
// Link the migrated post. | ||
$link_result = self::link_migrated_subscription( $network_url, $subscription_id, $outgoing_post->get_network_post_id() ); | ||
if ( is_wp_error( $link_result ) ) { | ||
return $link_result; | ||
} | ||
|
||
if ( $distribute ) { | ||
Content_Distribution::distribute_post( $outgoing_post ); | ||
} | ||
|
||
// Delete the post meta. | ||
delete_post_meta( $post_id, 'dt_subscriptions' ); | ||
delete_post_meta( $post_id, 'dt_connection_map' ); | ||
|
||
// Delete the subscription. | ||
wp_delete_post( $subscription_id ); | ||
|
||
return $outgoing_post; | ||
} | ||
|
||
/** | ||
* Trigger the request to link the incoming post from a subscription being | ||
* migrated. | ||
* | ||
* @param string $site_url The URL of the site where the post is being migrated to. | ||
* @param int $subscription_id The ID of the subscription being migrated. | ||
* @param string $network_post_id The network post ID for the migrated post. | ||
* | ||
* @return WP_Error|void WP_Error on failure, void on success. | ||
*/ | ||
protected static function link_migrated_subscription( $site_url, $subscription_id, $network_post_id ) { | ||
$remote_post_id = get_post_meta( $subscription_id, 'dt_subscription_remote_post_id', true ); | ||
$signature = get_post_meta( $subscription_id, 'dt_subscription_signature', true ); | ||
$url = trailingslashit( $site_url ) . 'wp-json/newspack-network/v1/content-distribution/distributor-migrator/link/' . $remote_post_id; | ||
$response = wp_remote_post( | ||
$url, | ||
[ | ||
'body' => [ | ||
'subscription_signature' => $signature, | ||
'network_post_id' => $network_post_id, | ||
], | ||
'timeout' => 20, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout | ||
] | ||
); | ||
if ( is_wp_error( $response ) ) { | ||
return $response; | ||
} | ||
|
||
$link_response_code = wp_remote_retrieve_response_code( $response ); | ||
if ( 200 !== $link_response_code ) { | ||
$link_response_body = wp_remote_retrieve_body( $response ); | ||
$error_message = __( 'Error linking migrated post.', 'newspack-network' ); | ||
if ( $link_response_body ) { | ||
$error_message .= ' ' . $link_response_body; | ||
} | ||
return new WP_Error( 'link_migrated_subscription_error', $error_message ); | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.