May 5, 2016
Copy Media File From One Site to Another Within a Multisite Network
This class shows how to copy a file from one site to other sites within the same WordPress multisite network. This code uses wp_handle_sideload()
to copy the file to all the sites on the network without attaching them to posts. If you DO want to attach the file to a post when it is sideloaded, you may want to consider using media_sideload_image()
instead.
class Copy_Media_File_To_Network_Sites {
/**
* Constructor
*/
public function __construct() {
$this->hooks();
}
/**
* Initiate our hooks
*/
public function hooks() {
add_action( 'update_option_my_options_page_key', array( $this, 'update_department_images' ), 10, 2 );
}
/**
* Update department images on all sites in the network
*
* @since 0.1.0
* @param array $old_values The field values before being updated.
* @param array $new_values The field values after being updated.
*/
public function update_department_images( $old_values, $new_values ) {
if ( ! is_array( $new_values ) ) {
return;
}
if ( ! isset( $new_values[ 'dept_image_' . $post_id . '_id' ] ) ) {
continue;
}
$attachment_id = $new_values[ 'dept_image_' . $post_id . '_id' ];
// Insert media file into each site in the network.
$this->insert_media_file_into_sites( $attachment_id );
}
/**
* Insert media file into all sites on the network
*
* @since 0.1.0
* @param int $attachment_id The attachment ID of the file to insert.page.
*/
private function insert_media_file_into_sites( $attachment_id ) {
if ( ! $attachment_id ) {
return;
}
$file_path = get_attached_file( $attachment_id );
$file_url = wp_get_attachment_url( $attachment_id );
$file_type_data = wp_check_filetype( basename( $file_path ), null );
$file_type = $file_type_data['type'];
$timeout_seconds = 5;
// Loop through sub-sites in network.
foreach ( prefix_get_sites_in_network() as $blog_id => $blog_name ) {
switch_to_blog( $blog_id );
// Sideload media file.
$sideload_result = $this->sideload_media_file( $file_url, $file_type, $timeout_seconds );
// If an error occurred while trying to sideload the media file; continue to next blog.
if ( ! $sideload_result || ! empty( $sideload_result['error'] ) ) {
restore_current_blog();
continue;
}
$new_file_path = $sideload_result['file'];
$new_file_type = $sideload_result['type'];
// Insert media file into uploads directory.
$inserted_attachment_id = $this->insert_media_file( $new_file_path, $new_file_type );
restore_current_blog();
}
}
/**
* Sideload Media File
*
* @since 0.1.0
* @param string $file_url The URL of the file to sideload.
* @param string $file_type The file type of the file to sideload.
* @param int $timeout_seconds The number of seconds to allow before sideloading times out.
* @return array On success, returns an associative array of file attributes. On failure, returns
* $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ).
*/
private function sideload_media_file( $file_url, $file_type, $timeout_seconds ) {
// Gives us access to the download_url() and wp_handle_sideload() functions.
require_once( ABSPATH . 'wp-admin/includes/file.php' );
// Download file to temp dir.
$temp_file = download_url( $file_url, $timeout_seconds );
if ( is_wp_error( $temp_file ) ) {
return false;
}
// Array based on $_FILE as seen in PHP file uploads.
$file = array(
'name' => basename( $file_url ),
'type' => $file_type,
'tmp_name' => $temp_file,
'error' => 0,
'size' => filesize( $temp_file ),
);
$overrides = array(
// Tells WordPress to not look for the POST form
// fields that would normally be present, default is true,
// we downloaded the file from a remote server, so there
// will be no form fields.
'test_form' => false,
// Setting this to false lets WordPress allow empty files – not recommended.
'test_size' => true,
// A properly uploaded file will pass this test.
// There should be no reason to override this one.
'test_upload' => true,
);
// Move the temporary file into the uploads directory.
return wp_handle_sideload( $file, $overrides );
}
/**
* Insert media file into the current site
*
* @since 0.1.0
* @param string $file_path The path to the media file.
* @param string $file_type The mime type of the media file.
* @return int $inserted_attachment_id The inserted attachment ID, or 0 on failure.
*/
private function insert_media_file( $file_path = '', $file_type = '' ) {
if ( ! $file_path || ! $file_type ) {
return;
}
// Get the path to the uploads directory.
$wp_upload_dir = wp_upload_dir();
// Prepare an array of post data for the attachment.
$attachment_data = array(
'guid' => $wp_upload_dir['url'] . '/' . basename( $file_path ),
'post_mime_type' => $file_type,
'post_title' => preg_replace( '/.[^.]+$/', '', basename( $file_path ) ),
'post_content' => '',
'post_status' => 'inherit',
);
// Insert the attachment.
$inserted_attachment_id = wp_insert_attachment( $attachment_data, $file_path );
$inserted_attachment_path = get_attached_file( $inserted_attachment_id );
// Update the attachment metadata.
$this->update_inserted_attachment_metadata( $inserted_attachment_id, $inserted_attachment_path );
return $inserted_attachment_id;
}
/**
* Update inserted attachment metadata
*
* @since 0.1.0
* @param int $inserted_attachment_id The inserted attachment ID.
* @param string $file_path The path to the media file.
*/
private function update_inserted_attachment_metadata( $inserted_attachment_id, $file_path ) {
// Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
require_once( ABSPATH . 'wp-admin/includes/image.php' );
// Generate metadata for the attachment and update the database record.
$attach_data = wp_generate_attachment_metadata( $inserted_attachment_id, $file_path );
wp_update_attachment_metadata( $inserted_attachment_id, $attach_data );
}
}