Upload images from one site to another in Multisite

My multisite setup has one sort-of-admin site, where users can insert/edit posts and specify on which site they want ’em published. This is a contract requirement and cannot be changed.

EDIT: Check the accepted answer for a much simpler approach to this.

Read More


The basic steps in inserting content are done and properly working with the full power of switch_to_blog() but we’re facing a little discomfort with file uploads. We’ve decided to use the WP Uploader and as such, make the admin media library a mere sandbox for the files being uploaded.

So the process goes like this:

  1. Client inserts new post and images on a form.

  2. WP Uploader handles uploads, crunching, and places the files temporarily on the Admin Site Media Library with custom values representing his User ID and Session ID (we still don’t have a Post ID at this time).

  3. User clicks to save the post.

  4. System fires wp_insert_post() at the correct site and gets the returned Post ID and:

    <?php
    // functions.php inside the function triggered upon post save
    
    foreach ($files as $f) {
    restore_current_blog();
    /*
    * 5. System copies the files from the Admin Site Uploads Dir to the Selected Site Uploads Dir.
    */
    $filename = get_attached_file($f);
    $arq = get_post($f, ARRAY_A);
    
    $filedest = str_replace('blogs.dir/22/', 'blogs.dir/' . $marca . '/', $filename);
    
    if (copy($filename, $filedest)) {
            switch_to_blog($marca);
            unset($arq['ID']);
    
            /*
            * 6. System fires `wp_insert_attachment()`, `wp_generate_attachment_metadata()`
            * and `wp_update_attachment_metadata()` associating the newly moved files to the
            * returned Post ID from step 4.
            *
            */
    
            $att = wp_insert_attachment($arq, $filedest, $err);
            if ($att) {
    
                    $attach_data = wp_generate_attachment_metadata( $att, $filedest );
                    wp_update_attachment_metadata( $att,  $attach_data );
    
                        if ($f == $destaque) {
                            update_post_meta($err, '_thumbnail_id', $att);
                        }
    
            restore_current_blog();
            /*
            * 7. System deletes the old images from the Admin Media Library aka Sandbox.
            */
            $del = wp_delete_attachment( $f, true );
    
            $msg = '8. User is (should be) happy his files are properly put in place and goes on with his life.';
            } else {
                echo 'erro';
            }
        }
    }
    ?>
    

After the files are copied to the right path and the attachments are properly managed, we find that although the main file is present and attached to the right post, and we are calling wp_generate_attachment_metadata and wp_update_attachment_metadata inside switch_to_blog() the intermediate sizes defined on the target site are NOT being generated, and so aren’t the functions hooked to add_attachment.

I suspect this has something to do with this actions being fired from the admin site functions.php and not being able to access the other site’s functions.

So, is there a more clever way to move this images as if they were native to the target site?

Related posts

Leave a Reply

1 comment

  1. Who would say the solution would be this simple…

    After days battling with this, somehow it struck me: “what if we switch_to_blog() inside the media upload iframe?” What if we require the user to save the post before uploading any images?

    Turns out I had no answer to why we weren’t doing this on the first place so, having required that the user saved the post before uploading any images, the solution came short and sweet:

    1) Add site_id to the GET variables being passed to media-uploader:

    <script type="text/javascript">
        jQuery(function(){
                var site_id = <?php echo json_encode($siteid); ?>;
                var post_id = <?php echo json_encode($conteudo->ID); ?>;
    
                    jQuery('.btn_incluir_imagem').click(function() {
                         formfield = jQuery('#upload_image').attr('name');
                         tb_show('', uploader+'media-upload.php?sid='+site_id+'&amp;post_id='+post_id+'&amp;type=image&amp;TB_iframe=true&amp;');
                         return false;
                    });
    
        // More code here
        });
    </script>
    

    2) And retrieve it from inside the iframe, right after hooking the scripts and styles:

    function admin_styles_scripts_media_upload(){
        // Registers and enqueues
    
    
        if($_GET['sid']) { 
            switch_to_blog($_GET['sid']);
        }
    
        // Any images uploaded here will be directed straight to the post_id inside site_id
    
    }
    add_action('admin_print_scripts-media-upload-popup','admin_styles_scripts_media_upload');
    

    This simple trick allows you to upload media from any site to any other site inside a network, so long as you already have the destination’s site_id and post_id. No need to hand-copy files and re-generate metadata, lots of code straight to the garbage can, yay! 🙂

    Also, since we’re in an iframe and the only way to get out of it is to close the window, not calling restore_current_blog() doesn’t cause any adverse effects.