Skip to content
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

feat(content-distribution): migrator #185

Merged
merged 40 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6400e65
feat(content-distribution): migrator
miguelpeixe Jan 14, 2025
07f8774
feat: link existing incoming post
miguelpeixe Jan 14, 2025
99b39d4
chore: fix docblock
miguelpeixe Jan 14, 2025
22015c4
fix(content-distribution): persist site hash
miguelpeixe Jan 15, 2025
ed4cf73
Merge branch 'fix/content-distribution-site-hash' into feat/content-d…
miguelpeixe Jan 15, 2025
243c6bf
feat: cli
miguelpeixe Jan 15, 2025
39185b9
chore: remove copilot hallucination
miguelpeixe Jan 15, 2025
b73b6b3
fix: connection map clear
miguelpeixe Jan 15, 2025
3751782
fix(cli): validate subscriptions before migrating
miguelpeixe Jan 15, 2025
daefaf1
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 15, 2025
65804e7
feat(cli): support deletion
miguelpeixe Jan 15, 2025
32baad4
chore: lint
miguelpeixe Jan 15, 2025
4cb530a
feat: link payload and support unlinked state
miguelpeixe Jan 16, 2025
46523d1
fix: optimize subscriptions query
miguelpeixe Jan 16, 2025
c57e511
fix(cli): edge cases and better messaging
miguelpeixe Jan 16, 2025
2b337d9
fix(cli): add optional strict mode and migrate via posts
miguelpeixe Jan 16, 2025
156a28a
chore: improve cli description
miguelpeixe Jan 16, 2025
c3f5c1f
chore: improve cli description
miguelpeixe Jan 16, 2025
1850afd
fix: migrate incoming posts via data events
miguelpeixe Jan 20, 2025
e52bba2
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 20, 2025
8853e8e
fix: log batch migrated
miguelpeixe Jan 20, 2025
a7d410e
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 20, 2025
0c63bee
chore: update docblock
miguelpeixe Jan 20, 2025
c2f2dcc
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 20, 2025
0bfa9a7
chore: remove unused imports
miguelpeixe Jan 21, 2025
ad8fdd5
fix: typo
miguelpeixe Jan 21, 2025
893ed4e
fix: rename function
miguelpeixe Jan 21, 2025
ecd3d70
fix: typo
miguelpeixe Jan 21, 2025
1237389
fix: add error
miguelpeixe Jan 21, 2025
9bb8a15
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 21, 2025
9d5a2b4
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 21, 2025
1d5f4d1
fix: incoming post migration
miguelpeixe Jan 21, 2025
6f7af2c
chore: improve log
miguelpeixe Jan 21, 2025
65a3b09
Merge branch 'trunk' into feat/content-distribution-migrator
miguelpeixe Jan 22, 2025
eef612a
fix: remove distributor meta from payload
miguelpeixe Jan 22, 2025
77ff94b
fix: network post id
miguelpeixe Jan 22, 2025
f6a439d
fix: use current post status as `status_on_create`
miguelpeixe Jan 22, 2025
ce95fa7
fix: ensure default post status
miguelpeixe Jan 22, 2025
0a731b8
fix: use `wp_update_post()` for existing posts
miguelpeixe Jan 22, 2025
5d991c1
fix: ensure the latest post data on insert
miguelpeixe Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions includes/class-accepted-actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Accepted_Actions {
'newspack_network_membership_plan_updated' => 'Membership_Plan_Updated',
'network_post_updated' => 'Network_Post_Updated',
'network_post_deleted' => 'Network_Post_Deleted',
'newspack_network_distributor_migrate_incoming_posts' => 'Distributor_Migrate_Incoming_Posts',
];

/**
Expand All @@ -65,5 +66,6 @@ class Accepted_Actions {
'newspack_network_membership_plan_updated',
'network_post_updated',
'network_post_deleted',
'newspack_network_distributor_migrate_incoming_posts',
];
}
3 changes: 2 additions & 1 deletion includes/class-content-distribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Newspack_Network\Content_Distribution\Canonical_Url;
use Newspack_Network\Content_Distribution\Incoming_Post;
use Newspack_Network\Content_Distribution\Outgoing_Post;
use Newspack_Network\Content_Distribution\Distributor_Migrator;
use WP_Post;

/**
Expand Down Expand Up @@ -58,8 +59,8 @@ public static function init() {
CLI::init();
API::init();
Editor::init();

Canonical_Url::init();
Distributor_Migrator::init();
}

/**
Expand Down
136 changes: 133 additions & 3 deletions includes/content-distribution/class-cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Newspack_Network\Utils\Network;
use WP_CLI;
use WP_CLI\ExitException;
use WP_Error;

/**
* Class Distribution.
Expand Down Expand Up @@ -38,13 +39,14 @@ public static function register_commands(): void {
'newspack network distribute post',
[ __CLASS__, 'cmd_distribute_post' ],
[
'shortdesc' => __( 'Distribute a post to all the network or the specified sites' ),
'shortdesc' => __( 'Distribute a post to all the network or the specified sites', 'newspack-network' ),
'synopsis' => [
[
'type' => 'positional',
'name' => 'post-id',
'description' => sprintf(
'The ID of the post to distribute. Supported post types are: %s',
// translators: %s: list of supported post types.
__( 'The ID of the post to distribute. Supported post types are: %s', 'newspack-network' ),
implode(
', ',
Content_Distribution::get_distributed_post_types()
Expand All @@ -56,7 +58,7 @@ public static function register_commands(): void {
[
'type' => 'assoc',
'name' => 'sites',
'description' => __( "Networked site url(s) comma separated to distribute the post to – or 'all' to distribute to all sites in the network." ),
'description' => __( "Networked site url(s) comma separated to distribute the post to – or 'all' to distribute to all sites in the network.", 'newspack-network' ),
'optional' => false,
],
[
Expand All @@ -68,6 +70,47 @@ public static function register_commands(): void {
],
]
);
WP_CLI::add_command(
'newspack network distributor migrate',
[ __CLASS__, 'cmd_distributor_migrate' ],
[
'shortdesc' => __( 'Migrate posts from Distributor to Newspack Network\'s content distribution', 'newspack-network' ),
'synopsis' => [
[
'type' => 'positional',
'name' => 'post-id',
'description' => __( 'The ID of the post to migrate.', 'newspack-network' ),
'repeating' => false,
'optional' => true,
],
[
'type' => 'flag',
'name' => 'all',
'description' => __( 'Migrate all posts.', 'newspack-network' ),
'optional' => true,
],
[
'type' => 'assoc',
'name' => 'batch-size',
'description' => __( 'Number of posts to migrate in each batch.', 'newspack-network' ),
'optional' => true,
'default' => 50,
],
[
'type' => 'flag',
'name' => 'strict',
'description' => __( 'Whether to only migrate if all distributed posts can be migrated.', 'newspack-network' ),
'optional' => true,
],
[
'type' => 'flag',
'name' => 'delete',
'description' => __( 'Whether to deactivate and delete the Distributor plugin after migrating all posts. This will only take effect if all posts were able to migrate.', 'newspack-network' ),
'optional' => true,
],
],
]
);
}

/**
Expand Down Expand Up @@ -107,4 +150,91 @@ public function cmd_distribute_post( array $pos_args, array $assoc_args ): void
WP_CLI::error( $e->getMessage() );
}
}

/**
* Callback for the `newspack-network distributor migrate` command.
*
* @param array $pos_args Positional arguments.
* @param array $assoc_args Associative arguments.
*
* @throws ExitException If something goes wrong.
*/
public function cmd_distributor_migrate( array $pos_args, array $assoc_args ): void {
$post_id = $pos_args[0] ?? null;
if ( ! is_numeric( $post_id ) && ! isset( $assoc_args['all'] ) ) {
WP_CLI::error( 'Post ID must be a number.' );
}

if ( is_numeric( $post_id ) && isset( $assoc_args['all'] ) ) {
WP_CLI::error( 'The --all flag cannot be used with a post ID.' );
}

if ( ! isset( $assoc_args['all'] ) && isset( $assoc_args['delete'] ) ) {
WP_CLI::error( 'The --delete flag can only be used with the --all flag.' );
}

if ( isset( $assoc_args['batch-size'] ) && ! is_numeric( $assoc_args['batch-size'] ) ) {
WP_CLI::error( 'Batch size must be a number.' );
}

if ( isset( $assoc_args['all'] ) ) {
$strict = isset( $assoc_args['strict'] );

$post_ids = Distributor_Migrator::get_posts_with_distributor_subscriptions();

if ( empty( $post_ids ) ) {
WP_CLI::success( 'No distributed posts found.' );
return;
}

WP_CLI::line( sprintf( 'Found %d posts.', count( $post_ids ) ) );

// In strict mode, only continue if all posts can be migrated.
if ( $strict ) {
$errors = [];
foreach ( $post_ids as $post_id ) {
$can_migrate = Distributor_Migrator::can_migrate_outgoing_post( $post_id );
if ( is_wp_error( $can_migrate ) ) {
$errors[] = sprintf( 'Unable to migrate post %d: %s', $post_id, $can_migrate->get_error_message() );
}
}
if ( ! empty( $errors ) ) {
WP_CLI::error( 'Strict mode is enabled: ' . PHP_EOL . implode( PHP_EOL, $errors ) );
}
}

// Migrate posts.
$errors = new WP_Error();
$batch_size = $assoc_args['batch-size'] ?? 50;
$batches = array_chunk( $post_ids, $batch_size );

foreach ( $batches as $i => $batch ) {
$result = Distributor_Migrator::migrate_outgoing_posts( $batch );
if ( is_wp_error( $result ) ) {
$message = sprintf( '(%d/%d) Error migrating batch: %s', $i + 1, count( $batches ), $result->get_error_message() );
if ( $strict ) {
WP_CLI::error( $message );
} else {
$errors->add( $result->get_error_code(), $result->get_error_message() );
WP_CLI::line( $message );
}
} else {
WP_CLI::line( sprintf( '(%d/%d) Batch migrated.', $i + 1, count( $batches ) ) );
}
}

if ( isset( $assoc_args['delete'] ) && ! $errors->has_errors() ) {
deactivate_plugins( [ 'distributor/distributor.php' ] );
delete_plugins( [ 'distributor/distributor.php' ] );
WP_CLI::line( 'Distributor plugin is deactivated and deleted.' );
}
} else {
$result = Distributor_Migrator::migrate_outgoing_post( $post_id );
if ( is_wp_error( $result ) ) {
WP_CLI::error( $result->get_error_message() );
}
}

WP_CLI::success( 'Migration completed.' );
}
}
Loading
Loading