Moving Posts between Sites in Multisite Network
Once you moved the posts from one website to another and if you would like to display them in a once place, you can try my plugin for this purpose.
Step 1. Add Bulk Actions
At the very first moment I was not sure how to implement the UI for our task, but I decided to use Bulk actions, because in many cases it will much more convenient to move multiple posts to another blog.
Working with bulk actions, you should also know, that for pages, the filter will look like bulk_actions-edit-page
, for custom post types bulk_actions-edit-{CPT name}
. Or better read a tutorial about bulk actions on my blog.
add_filter( 'bulk_actions-edit-post', 'misha_my_bulk_multisite_actions' ); function misha_my_bulk_multisite_actions( $bulk_array ) { if( $sites = get_sites( array( // 'site__in' => array( 1,2,3 ) 'site__not_in' => get_current_blog_id(), // excluding current blog 'number' => 50, ))) { foreach( $sites as $site ) { $bulk_array['move_to_'.$site->blog_id] = 'Move to "' .$site->blogname . '"'; } } return $bulk_array; }
Important notice about line 8 βΒ as you can see I set the maximum 50 sites to be displayed here. WordPress Multisite network supports unlimited amount of sites, so if you have 1000+ sites in your Network, you’d better think about another implementation of this step, I can give you two hints:
- Maybe you do not need all the sites, in that case
site_in
parameter will help you. - If you need all of them, you could try to replace the default bulk action element with a Select2 with AJAX search, read more about it here.
I have only 3 blogs in my Network, so my result looks like this:

Step 2. Move / Copy Posts
Do not worry about handle_bulk_actions-edit-post
filter hook here βΒ it will be the same for Posts, Pages and Custom Types.
add_filter( 'handle_bulk_actions-edit-post', 'misha_bulk_action_multisite_handler', 10, 3 ); function misha_bulk_action_multisite_handler( $redirect, $doaction, $object_ids ) { // we need query args to display correct admin notices $redirect = remove_query_arg( array( 'misha_posts_moved', 'misha_blogid' ), $redirect ); // our actions begin with "move_to_", so let's check if it is a target action if( strpos( $doaction, "move_to_" ) === 0 ) { $blog_id = str_replace( "move_to_", "", $doaction ); foreach ( $object_ids as $post_id ) { // get the original post object as an array $post = get_post( $post_id, ARRAY_A ); // if you need to apply terms (more info below the code) $post_terms = wp_get_object_terms($post_id, 'category', array('fields' => 'slugs')); // get all the post meta $data = get_post_custom($post_id); // empty ID field, to tell WordPress to create a new post, not update an existing one $post['ID'] = ''; switch_to_blog( $blog_id ); // insert the post $inserted_post_id = wp_insert_post($post); // insert the post // update post terms wp_set_object_terms($inserted_post_id, $post_terms, 'category', false); // add post meta foreach ( $data as $key => $values) { // if you do not want weird redirects if( $key == '_wp_old_slug' ) { continue; } foreach ($values as $value) { add_post_meta( $inserted_post_id, $key, $value ); } } restore_current_blog(); // if you want to copy posts, comment this line wp_delete_post( $post_id ); } $redirect = add_query_arg( array( 'misha_posts_moved' => count( $object_ids ), 'misha_blogid' => $blog_id ), $redirect ); } return $redirect; }
- Line 17, Line 29. As you can see I specify the taxonomy manually. So, if the same taxonomy exists on a target website, the terms with the same slugs will be added to posts (if no such terms are on the target website, they will be created). If you do not want to specify each taxonomy manually, you can use this code:
$taxonomies = get_object_taxonomies( $post['post_type'] ); foreach ( $taxonomies as $taxonomy ) { $post_terms = wp_get_object_terms( $post_id, $taxonomy, array('fields' => 'slugs') ); }
And
foreach ( $taxonomies as $taxonomy ) { wp_set_object_terms( $inserted_post_id, $post_terms, $taxonomy, false ); }
- Line 44. Remove this line of code, if you would like to duplicate posts, not to move them. By the way, if you set the second parameter to
wp_delete_post( $post_id, true );
, posts will be removed permanently, without moving to the trash.
Step 3. Show an appropriate notice
This step is just for a better usability, I mean everything should work even if you’ve implemented only Step 1 and Step 2 of this tutorial.
add_action( 'admin_notices', 'misha_bulk_multisite_notices' ); function misha_bulk_multisite_notices() { if( ! empty( $_REQUEST['misha_posts_moved'] ) ) { // because I want to add blog names to notices $blog = get_blog_details( $_REQUEST['misha_blogid'] ); // depending on ho much posts were changed, make the message different printf( '<div id="message" class="updated notice is-dismissible"><p>' . _n( '%d post has been moved to "%s".', '%d posts have been moved to "%s".', intval( $_REQUEST['misha_posts_moved'] ) ) . '</p></div>', intval( $_REQUEST['misha_posts_moved'] ), $blog->blogname ); } }
Finally we got this:

Comments — 17
Nice ! thanks !
Btw could you share the social sharing plugin that youre using ? its so minimalistic and clean !
Thank you, but it is not a plugin π just custom coding
Nice! would appreciate tutorial on that !
It best to get away with one plugin and go for custom coding :)
Hi Mischa, excellent work, thank you so much for sharing!
Hi Jakub,
Always welcome! π
Hi Mischa again,
could you help me include a post thumbnail, please? I added a snippet
after line 19
and then
after line 39
but the thumbnail wouldn’t copy.
thanks in advance!
Let me clarify β would you like just to set a thumbnail or to duplicate an attachment too?
I would like to copy a post including its thumbnail (featured image). In that moment, any post is copied including images within post content but featured image stays uncopied.
But does it set as a featured image for new posts?
Sorry Mischa, I don’t understand the question, I guess. It should not influence any future posts at all, just copy the featured image related to the selected post. Almost each post has its own featured image. Is this what are you asking?
Hi, well done, thanks for sharing.
Just a question, i only need to show on the “main blog” a preview of the posts written on the “satellite blogs”, something like title, excerpt, thumb and href to the original post; i don’t need to get all the post.
It’s possible?
Thanks
Enrico
Hello,
This method only moves the post to the other site.
How to copy/duplicate a post?
Thank you.
Hello Selvi,
Just remove/comment line 44 of this step.
Hello,
When copying a post, all custom fields are also copied. Is it possible to ignore e.g. fields such as “music_post”, “video_post” (this is meta key)?
Hello Arthur,
Just add more conditions similar to lines 33-35, example:
Hi Misha,
Thanks for the wonderful code! Is it possible to configure it to work for pages as well as posts?
Anthony
Hi Anthony,
Yes, it is possible.
Comments are closed.