diff --git a/class/wpsdb-media-files.php b/class/wpsdb-media-files.php index e113671..350df64 100644 --- a/class/wpsdb-media-files.php +++ b/class/wpsdb-media-files.php @@ -1,677 +1,739 @@ plugin_slug = 'wp-sync-db-media-files'; - $this->plugin_version = $GLOBALS['wpsdb_meta']['wp-sync-db-media-files']['version']; - - if( ! $this->meets_version_requirements( '1.4b1' ) ) return; - - add_action( 'wpsdb_after_advanced_options', array( $this, 'migration_form_controls' ) ); - add_action( 'wpsdb_load_assets', array( $this, 'load_assets' ) ); - add_action( 'wpsdb_js_variables', array( $this, 'js_variables' ) ); - add_filter( 'wpsdb_accepted_profile_fields', array( $this, 'accepted_profile_fields' ) ); - add_filter( 'wpsdb_establish_remote_connection_data', array( $this, 'establish_remote_connection_data' ) ); - add_filter( 'wpsdb_nonces', array( $this, 'add_nonces' ) ); - - // compatibility with CLI migraitons - add_filter( 'wpsdb_cli_finalize_migration', array( $this, 'cli_migration' ), 10, 4 ); - - // internal AJAX handlers - add_action( 'wp_ajax_wpsdbmf_determine_media_to_migrate', array( $this, 'ajax_determine_media_to_migrate' ) ); - add_action( 'wp_ajax_wpsdbmf_migrate_media', array( $this, 'ajax_migrate_media' ) ); - - // external AJAX handlers - add_action( 'wp_ajax_nopriv_wpsdbmf_get_remote_media_listing', array( $this, 'respond_to_get_remote_media_listing' ) ); - add_action( 'wp_ajax_nopriv_wpsdbmf_push_request', array( $this, 'respond_to_push_request' ) ); - add_action( 'wp_ajax_nopriv_wpsdbmf_remove_local_attachments', array( $this, 'respond_to_remove_local_attachments' ) ); - } - - function get_local_attachments() { - global $wpdb; - $prefix = $wpdb->prefix; - $temp_prefix = stripslashes( $_POST['temp_prefix'] ); - - /* - * We determine which media files need migrating BEFORE the database migration is finalized. - * Because of this we need to scan the *_post & *_postmeta that are prefixed using the temporary prefix. - * Though this should only happen when we're responding to a get_remote_media_listing() call AND it's a push OR - * we're scanning local files AND it's a pull. - */ - - if( - ( true == $this->responding_to_get_remote_media_listing && $_POST['intent'] == 'push' ) || - ( false == $this->responding_to_get_remote_media_listing && $_POST['intent'] == 'pull' ) - ) { - - $local_tables = array_flip( $this->get_tables() ); - - $posts_table_name = "{$temp_prefix}{$prefix}posts"; - $postmeta_table_name = "{$temp_prefix}{$prefix}postmeta"; - - if( isset( $local_tables[$posts_table_name] ) && isset( $local_tables[$postmeta_table_name] ) ) { - $prefix = $temp_prefix . $prefix; - } - - } - - $local_attachments = $wpdb->get_results( - "SELECT `{$prefix}posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata' - FROM `{$prefix}posts` - INNER JOIN `{$prefix}postmeta` pm1 ON `{$prefix}posts`.`ID` = pm1.`post_id` AND pm1.`meta_key` = '_wp_attached_file' - LEFT OUTER JOIN `{$prefix}postmeta` pm2 ON `{$prefix}posts`.`ID` = pm2.`post_id` AND pm2.`meta_key` = '_wp_attachment_metadata' - WHERE `{$prefix}posts`.`post_type` = 'attachment'", ARRAY_A - ); - - if( is_multisite() ) { - $blogs = $this->get_blogs(); - $prefix = $wpdb->prefix; - foreach( $blogs as $blog ) { - $posts_table_name = "{$temp_prefix}{$prefix}{$blog}_posts"; - $postmeta_table_name = "{$temp_prefix}{$prefix}{$blog}_postmeta"; - if( isset( $local_tables[$posts_table_name] ) && isset( $local_tables[$postmeta_table_name] ) ) { - $prefix = $temp_prefix . $prefix; - } - $attachments = $wpdb->get_results( - "SELECT `{$prefix}{$blog}_posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata', {$blog} AS 'blog_id' - FROM `{$prefix}{$blog}_posts` - INNER JOIN `{$prefix}{$blog}_postmeta` pm1 ON `{$prefix}{$blog}_posts`.`ID` = pm1.`post_id` AND pm1.`meta_key` = '_wp_attached_file' - LEFT OUTER JOIN `{$prefix}{$blog}_postmeta` pm2 ON `{$prefix}{$blog}_posts`.`ID` = pm2.`post_id` AND pm2.`meta_key` = '_wp_attachment_metadata' - WHERE `{$prefix}{$blog}_posts`.`post_type` = 'attachment'", ARRAY_A - ); - - $local_attachments = array_merge( $attachments, $local_attachments ); - } - } - - $local_attachments = array_map( array( $this, 'process_attachment_data' ), $local_attachments ); - $local_attachments = array_filter( $local_attachments ); - - return $local_attachments; - } - - function get_flat_attachments( $attachments ) { - $flat_attachments = array(); - foreach( $attachments as $attachment ) { - $flat_attachments[] = $attachment['file']; - if( isset( $attachment['sizes'] ) ) { - $flat_attachments = array_merge( $flat_attachments, $attachment['sizes'] ); - } - } - return $flat_attachments; - } - - function process_attachment_data( $attachment ) { - if ( isset( $attachment['blog_id'] ) ) { // used for multisite - if ( defined( 'UPLOADBLOGSDIR' ) ) { - $upload_dir = sprintf( '%s/files/', $attachment['blog_id'] ); - } else { - $upload_dir = sprintf( 'sites/%s/', $attachment['blog_id'] ); - } - $attachment['file'] = $upload_dir . $attachment['file']; - } - $upload_dir = str_replace( basename( $attachment['file'] ), '', $attachment['file'] ); - if ( ! empty( $attachment['metadata'] ) ) { - $attachment['metadata'] = @unserialize( $attachment['metadata'] ); - if ( ! empty( $attachment['metadata']['sizes'] ) && is_array( $attachment['metadata']['sizes'] ) ) { - foreach ( $attachment['metadata']['sizes'] as $size ) { - if ( empty( $size['file'] ) ) continue; - $attachment['sizes'][] = $upload_dir . $size['file']; - } - } - } - unset( $attachment['metadata'] ); - return $attachment; - } - - function uploads_dir() { - if( defined( 'UPLOADBLOGSDIR' ) ) { - $upload_dir = trailingslashit( ABSPATH ) . UPLOADBLOGSDIR; - } - else { - $upload_dir = wp_upload_dir(); - $upload_dir = $upload_dir['basedir']; - } - return trailingslashit( $upload_dir ); - } - - function get_local_media() { - $upload_dir = untrailingslashit( $this->uploads_dir() ); - if( ! file_exists( $upload_dir ) ) return array(); - - $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $upload_dir ), RecursiveIteratorIterator::SELF_FIRST ); - $local_media = array(); - - foreach( $files as $name => $object ){ - $name = str_replace( array( $upload_dir . DS, '\\' ), array( '', '/' ), $name ); - $local_media[$name] = $object->getSize(); - } - - return $local_media; - } - - function ajax_migrate_media() { - $this->check_ajax_referer( 'migrate-media' ); - $this->set_time_limit(); - - if ( $_POST['intent'] == 'pull' ) { - $result = $this->process_pull_request(); - return $result; - } - - $result = $this->process_push_request(); - return $result; - } - - function process_pull_request() { - $files_to_download = $_POST['file_chunk']; - $remote_uploads_url = trailingslashit( $_POST['remote_uploads_url'] ); - $parsed = parse_url( $_POST['url'] ); - if( ! empty( $parsed['user'] ) ) { - $credentials = sprintf( '%s:%s@', $parsed['user'], $parsed['pass'] ); - $remote_uploads_url = str_replace( '://', '://' . $credentials, $remote_uploads_url ); - } - - $upload_dir = $this->uploads_dir(); - - $errors = array(); - foreach( $files_to_download as $file_to_download ) { - $temp_file_path = $this->download_url( $remote_uploads_url . $file_to_download ); - - if( is_wp_error( $temp_file_path ) ) { - $download_error = $temp_file_path->get_error_message(); - $errors[] = __( sprintf( 'Could not download file: %1$s - %2$s', $remote_uploads_url . $file_to_download, $download_error ), 'wp-sync-db-media-files' ); - continue; - } - - $date = str_replace( basename( $file_to_download ), '', $file_to_download ); - $new_path = $upload_dir . $date . basename( $file_to_download ); - - $move_result = @rename( $temp_file_path, $new_path ); - - if( false === $move_result ) { - $folder = dirname( $new_path ); - if( @file_exists( $folder ) ) { - $errors[] = __( sprintf( 'Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', $temp_file_path, $new_path ), 'wp-sync-db-media-files' ) . ' (#103mf)'; - } - else{ - if( false === @mkdir( $folder, 0755, true ) ) { - $errors[] = __( sprintf( 'Error attempting to create required directory: %s', $folder ), 'wp-sync-db-media-files' ) . ' (#104mf)'; - } - else { - $move_result = @rename( $temp_file_path, $new_path ); - if( false === $move_result ) { - $errors[] = __( sprintf( 'Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', $temp_file_path, $new_path ), 'wp-sync-db-media-files' ) . ' (#105mf)'; - } - } - } - } - } - - if( ! empty( $errors ) ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => implode( '
', $errors ) . '
' - ); - $result = $this->end_ajax( json_encode( $return ) ); - return $result; - } - - // not required, just here because we have to return something otherwise the AJAX fails - $return['success'] = 1; - $result = $this->end_ajax( json_encode( $return ) ); - return $result; - } - - function process_push_request() { - $files_to_migrate = $_POST['file_chunk']; - - $upload_dir = $this->uploads_dir(); - - $body = ''; - foreach( $files_to_migrate as $file_to_migrate ) { - $body .= $this->file_to_multipart( $upload_dir . $file_to_migrate ); - } - - $post_args = array( - 'action' => 'wpsdbmf_push_request', - 'files' => serialize( $files_to_migrate ) - ); - - $post_args['sig'] = $this->create_signature( $post_args, $_POST['key'] ); - - $body .= $this->array_to_multipart( $post_args ); - - $args['body'] = $body; - $ajax_url = trailingslashit( $_POST['url'] ) . 'wp-admin/admin-ajax.php'; - $response = $this->remote_post( $ajax_url, '', __FUNCTION__, $args ); - $response = $this->verify_remote_post_response( $response ); - - $result = $this->end_ajax( json_encode( $response ) ); - return $result; - } - - function respond_to_push_request() { - $filtered_post = $this->filter_post_elements( $_POST, array( 'action', 'files' ) ); - $filtered_post['files'] = stripslashes( $filtered_post['files'] ); - if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => $this->invalid_content_verification_error . ' (#101mf)', - ); - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - if( ! isset( $_FILES['media'] ) ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => __( '$_FILES is empty, the upload appears to have failed', 'wp-sync-db-media-files' ) . ' (#106mf)', - ); - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - $upload_dir = $this->uploads_dir(); - - $files = $this->diverse_array( $_FILES['media'] ); - $file_paths = unserialize( $filtered_post['files'] ); - $i = 0; - $errors = array(); - foreach( $files as &$file ) { - $destination = $upload_dir . $file_paths[$i]; - $folder = dirname( $destination ); - - if( false === @file_exists( $folder ) && false === @mkdir( $folder, 0755, true ) ) { - $errors[] = __( sprintf( 'Error attempting to create required directory: %s', $folder ), 'wp-sync-db-media-files' ) . ' (#108mf)'; - ++$i; - continue; - } - - if( false === @move_uploaded_file( $file['tmp_name'], $destination ) ) { - $errors[] = __( sprintf( 'A problem occurred when attempting to move the temp file "%1$s" to "%2$s"', $file['tmp_name'], $destination ), 'wp-sync-db-media-files' ) . ' (#107mf)'; - } - ++$i; - } - - $return = array( 'success' => 1 ); - if( ! empty( $errors ) ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => implode( '
', $errors ) . '
' - ); - } - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - function ajax_determine_media_to_migrate() { - $this->check_ajax_referer( 'determine-media-to-migrate' ); - $this->set_time_limit(); - - $local_attachments = $this->get_local_attachments(); - $local_media = $this->get_local_media(); - - $data = array(); - $data['action'] = 'wpsdbmf_get_remote_media_listing'; - $data['temp_prefix'] = $this->temp_prefix; - $data['intent'] = $_POST['intent']; - $data['sig'] = $this->create_signature( $data, $_POST['key'] ); - $ajax_url = trailingslashit( $_POST['url'] ) . 'wp-admin/admin-ajax.php'; - $response = $this->remote_post( $ajax_url, $data, __FUNCTION__ ); - $response = $this->verify_remote_post_response( $response ); - - $upload_dir = $this->uploads_dir(); - - $remote_attachments = $response['remote_attachments']; - $remote_media = $response['remote_media']; - - $this->files_to_migrate = array(); - - if( $_POST['intent'] == 'pull' ) { - $this->media_diff( $local_attachments, $remote_attachments, $local_media, $remote_media ); - } - else { - $this->media_diff( $remote_attachments, $local_attachments, $remote_media, $local_media ); - } - - $return['files_to_migrate'] = $this->files_to_migrate; - $return['total_size'] = array_sum( $this->files_to_migrate ); - $return['remote_uploads_url'] = $response['remote_uploads_url']; - - // remove local/remote media if it doesn't exist on the local/remote site - if( $_POST['remove_local_media'] == '1' ) { - if( $_POST['intent'] == 'pull' ) { - $this->remove_local_attachments( $remote_attachments ); - } - else { - $data = array(); - $data['action'] = 'wpsdbmf_remove_local_attachments'; - $data['remote_attachments'] = serialize( $local_attachments ); - $data['sig'] = $this->create_signature( $data, $_POST['key'] ); - $ajax_url = trailingslashit( $_POST['url'] ) . 'wp-admin/admin-ajax.php'; - $response = $this->remote_post( $ajax_url, $data, __FUNCTION__ ); - // the response is ignored here (for now) as this is not a critical task - } - } - - $result = $this->end_ajax( json_encode( $return ) ); - return $result; - } - - function respond_to_remove_local_attachments() { - $filtered_post = $this->filter_post_elements( $_POST, array( 'action', 'remote_attachments' ) ); - $filtered_post['remote_attachments'] = stripslashes( $filtered_post['remote_attachments'] ); - if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => $this->invalid_content_verification_error . ' (#109mf)', - ); - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - $remote_attachments = @unserialize( $filtered_post['remote_attachments'] ); - if( false === $remote_attachments ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => __( 'Error attempting to unserialize the remote attachment data', 'wp-sync-db-media-files' ) . ' (#110mf)', - ); - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - $this->remove_local_attachments( $remote_attachments ); - - $return = array( - 'success' => 1, - ); - $result = serialize( json_encode( $return ) ); - return $result; - } - - function remove_local_attachments( $remote_attachments ) { - $flat_remote_attachments = array_flip( $this->get_flat_attachments( $remote_attachments ) ); - $local_media = $this->get_local_media(); - // remove local media if it doesn't exist on the remote site - $temp_local_media = array_keys( $local_media ); - $allowed_mime_types = array_flip( get_allowed_mime_types() ); - $upload_dir = $this->uploads_dir(); - foreach( $temp_local_media as $local_media_file ) { - // don't remove folders - if( false === is_file( $upload_dir . $local_media_file ) ) continue; - $filetype = wp_check_filetype( $local_media_file ); - // don't remove files that we shouldn't remove, e.g. .php, .sql, etc - if( false === isset( $allowed_mime_types[$filetype['type']] ) ) continue; - // don't remove files that exist on the remote site - if( true === isset( $flat_remote_attachments[$local_media_file] ) ) continue; - - @unlink( $upload_dir . $local_media_file ); - } - } - - function media_diff( $site_a_attachments, $site_b_attachments, $site_a_media, $site_b_media ) { - foreach( $site_b_attachments as $attachment ) { - $local_attachment_key = $this->multidimensional_search( array( 'file' => $attachment['file'] ), $site_a_attachments ); - if( false === $local_attachment_key ) continue; - $remote_timestamp = strtotime( $attachment['date'] ); - $local_timestamp = strtotime( $site_a_attachments[$local_attachment_key]['date'] ); - if( $local_timestamp >= $remote_timestamp ) { - if( ! isset( $site_a_media[$attachment['file']] ) ) { - $this->add_files_to_migrate( $attachment, $site_b_media ); - } - else { - $this->maybe_add_resized_images( $attachment, $site_b_media, $site_a_media ); - } - } - else { - $this->add_files_to_migrate( $attachment, $site_b_media ); - } - } - } - - function add_files_to_migrate( $attachment, $remote_media ) { - if( isset( $remote_media[$attachment['file']] ) ) { - $this->files_to_migrate[$attachment['file']] = $remote_media[$attachment['file']]; - } - if( empty( $attachment['sizes'] ) || apply_filters( 'wpsdb_exclude_resized_media', false ) ) return; - foreach( $attachment['sizes'] as $size ) { - if( isset( $remote_media[$size] ) ) { - $this->files_to_migrate[$size] = $remote_media[$size]; - } - } - } - - function maybe_add_resized_images( $attachment, $site_b_media, $site_a_media ) { - if( empty( $attachment['sizes'] ) || apply_filters( 'wpsdb_exclude_resized_media', false ) ) return; - foreach( $attachment['sizes'] as $size ) { - if( isset( $site_b_media[$size] ) && ! isset( $site_a_media[$size] ) ) { - $this->files_to_migrate[$size] = $site_b_media[$size]; - } - } - } - - function respond_to_get_remote_media_listing() { - $filtered_post = $this->filter_post_elements( $_POST, array( 'action', 'temp_prefix', 'intent' ) ); - if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) { - $return = array( - 'wpsdb_error' => 1, - 'body' => $this->invalid_content_verification_error . ' (#100mf)', - ); - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - if( defined( 'UPLOADBLOGSDIR' ) ) { - $upload_url = home_url( UPLOADBLOGSDIR ); - } - else { - $upload_dir = wp_upload_dir(); - $upload_url = $upload_dir['baseurl']; - } - - $this->responding_to_get_remote_media_listing = true; - - $return['remote_attachments'] = $this->get_local_attachments(); - $return['remote_media'] = $this->get_local_media(); - $return['remote_uploads_url'] = $upload_url; - - $result = $this->end_ajax( serialize( $return ) ); - return $result; - } - - function migration_form_controls() { - $this->template( 'migrate' ); - } - - function accepted_profile_fields( $profile_fields ) { - $profile_fields[] = 'media_files'; - $profile_fields[] = 'remove_local_media'; - return $profile_fields; - } - - function load_assets() { - $plugins_url = trailingslashit( plugins_url() ) . trailingslashit( $this->plugin_folder_name ); - $src = $plugins_url . 'asset/js/script.js'; - $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->plugin_version; - wp_enqueue_script( 'wp-sync-db-media-files-script', $src, array( 'jquery', 'wp-sync-db-common', 'wp-sync-db-hook', 'wp-sync-db-script' ), $version, true ); - - wp_localize_script( 'wp-sync-db-media-files-script', 'wpsdbmf_strings', array( - 'determining' => __( "Determining which media files to migrate, please wait...", 'wp-sync-db-media-files' ), - 'error_determining' => __( "Error while attempting to determine which media files to migrate.", 'wp-sync-db-media-files' ), - 'migration_failed' => __( "Migration failed", 'wp-sync-db-media-files' ), - 'problem_migrating_media' => __( "A problem occurred when migrating the media files.", 'wp-sync-db-media-files' ), - 'media_files' => __( "Media Files", 'wp-sync-db-media-files' ), - 'migrating_media_files' => __( "Migrating media files", 'wp-sync-db-media-files' ), - ) ); - - } - - function establish_remote_connection_data( $data ) { - $data['media_files_available'] = '1'; - $data['media_files_version'] = $this->plugin_version; - if( function_exists( 'ini_get' ) ) { - $max_file_uploads = ini_get( 'max_file_uploads' ); - } - $max_file_uploads = ( empty( $max_file_uploads ) ) ? 20 : $max_file_uploads; - $data['media_files_max_file_uploads'] = apply_filters( 'wpsdbmf_max_file_uploads', $max_file_uploads ); - return $data; - } - - function multidimensional_search( $needle, $haystack ) { - if( empty( $needle ) || empty( $haystack ) ) return false; - - foreach( $haystack as $key => $value ) { - foreach ( $needle as $skey => $svalue ) { - $exists = ( isset( $haystack[$key][$skey] ) && $haystack[$key][$skey] === $svalue ); - } - if( $exists ) return $key; - } - - return false; - } - - function get_blogs() { - global $wpdb; - - $blogs = $wpdb->get_results( - "SELECT blog_id - FROM {$wpdb->blogs} - WHERE spam = '0' - AND deleted = '0' - AND archived = '0' - AND blog_id != 1 - "); - - $clean_blogs = array(); - foreach( $blogs as $blog ) { - $clean_blogs[] = $blog->blog_id; - } - - return $clean_blogs; - } - - function download_url( $url, $timeout = 300 ) { - //WARNING: The file is not automatically deleted, The script must unlink() the file. - if ( ! $url ) - return new WP_Error('http_no_url', __('Invalid URL Provided.')); - - $tmpfname = wp_tempnam($url); - if ( ! $tmpfname ) - return new WP_Error('http_no_file', __('Could not create Temporary file.')); - - $response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname, 'reject_unsafe_urls' => false ) ); - - if ( is_wp_error( $response ) ) { - unlink( $tmpfname ); - return $response; - } - - if ( 200 != wp_remote_retrieve_response_code( $response ) ){ - unlink( $tmpfname ); - return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) ); - } - - return $tmpfname; - } - - function js_variables() { - ?> - var wpsdb_media_files_version = 'plugin_version; ?>'; - 1, 'body' => $this->error ); - $result = $this->end_ajax( json_encode( $return ) ); - return $result; - } - - if ( ! is_serialized( trim( $response ) ) ) { - $return = array( 'wpsdb_error' => 1, 'body' => $response ); - $result = $this->end_ajax( json_encode( $return ) ); - return $result; - } - - $response = unserialize( trim( $response ) ); - - if ( isset( $response['wpsdb_error'] ) ) { - $result = $this->end_ajax( json_encode( $response ) ); - return $result; - } - return $response; - } - - function add_nonces( $nonces ) { - $nonces['migrate_media'] = wp_create_nonce( 'migrate-media' ); - $nonces['determine_media_to_migrate'] = wp_create_nonce( 'determine-media-to-migrate' ); - return $nonces; - } - - function cli_migration( $outcome, $profile, $verify_connection_response, $initiate_migration_response ) { - global $wpsdb, $wpsdb_cli; - if ( true !== $outcome ) return $outcome; - if ( !isset( $profile['media_files'] ) || '1' !== $profile['media_files'] ) return $outcome; - - if ( !isset( $verify_connection_response['media_files_max_file_uploads'] ) ) { - return $wpsdb_cli->cli_error( __( 'WP Sync DB Media Files does not seems to be installed/active on the remote website.', 'wp-sync-db-media-files' ) ); - } - - $this->set_time_limit(); - $wpsdb->set_cli_migration(); - $this->set_cli_migration(); - - $connection_info = explode( "\n", $profile['connection_info'] ); - - $_POST['intent'] = $profile['action']; - $_POST['url'] = trim( $connection_info[0] ); - $_POST['key'] = trim( $connection_info[1] ); - $_POST['remove_local_media'] = ( isset( $profile['remove_local_media'] ) ) ? 1 : 0; - $_POST['temp_prefix'] = $verify_connection_response['temp_prefix']; - - do_action( 'wpsdb_cli_before_determine_media_to_migrate', $profile, $verify_connection_response, $initiate_migration_response ); - - $response = $this->ajax_determine_media_to_migrate(); - if( is_wp_error( $determine_media_to_migrate_response = $wpsdb_cli->verify_cli_response( $response, 'ajax_determine_media_to_migrate()' ) ) ) return $determine_media_to_migrate_response; - - $remote_uploads_url = $determine_media_to_migrate_response['remote_uploads_url']; - $files_to_migrate = $determine_media_to_migrate_response['files_to_migrate']; - // seems like this value needs to be different depending on pull/push? - $bottleneck = $wpsdb->get_bottleneck(); - - while ( !empty( $files_to_migrate ) ) { - $file_chunk_to_migrate = array(); - $file_chunk_size = 0; - $number_of_files_to_migrate = 0; - foreach ( $files_to_migrate as $file_to_migrate => $file_size ) { - if ( empty( $file_chunk_to_migrate ) ) { - $file_chunk_to_migrate[] = $file_to_migrate; - $file_chunk_size += $file_size; - unset( $files_to_migrate[$file_to_migrate] ); - ++$number_of_files_to_migrate; - } else { - if ( ( $file_chunk_size + $file_size ) > $bottleneck || $number_of_files_to_migrate >= $verify_connection_response['media_files_max_file_uploads'] ) { - break; - } else { - $file_chunk_to_migrate[] = $file_to_migrate; - $file_chunk_size += $file_size; - unset( $files_to_migrate[$file_to_migrate] ); - ++$number_of_files_to_migrate; - } - } - - $_POST['file_chunk'] = $file_chunk_to_migrate; - $_POST['remote_uploads_url'] = $remote_uploads_url; - - $response = $this->ajax_migrate_media(); - if( is_wp_error( $migrate_media_response = $wpsdb_cli->verify_cli_response( $response, 'ajax_migrate_media()' ) ) ) return $migrate_media_response; - } - } - return true; - } +class WPSDB_Media_Files extends WPSDB_Addon +{ + protected $files_to_migrate; + protected $responding_to_get_remote_media_listing = false; + + public function __construct($plugin_file_path) + { + parent::__construct($plugin_file_path); + + $this->plugin_slug = 'wp-sync-db-media-files'; + $this->plugin_version = $GLOBALS['wpsdb_meta']['wp-sync-db-media-files']['version']; + + if (!$this->meets_version_requirements('1.4b1')) { + return; + } + + add_action('wpsdb_after_advanced_options', array($this, 'migration_form_controls')); + add_action('wpsdb_load_assets', array($this, 'load_assets')); + add_action('wpsdb_js_variables', array($this, 'js_variables')); + add_filter('wpsdb_accepted_profile_fields', array($this, 'accepted_profile_fields')); + add_filter('wpsdb_establish_remote_connection_data', array($this, 'establish_remote_connection_data')); + add_filter('wpsdb_nonces', array($this, 'add_nonces')); + + // compatibility with CLI migraitons + add_filter('wpsdb_cli_finalize_migration', array($this, 'cli_migration'), 10, 4); + + // internal AJAX handlers + add_action('wp_ajax_wpsdbmf_determine_media_to_migrate', array($this, 'ajax_determine_media_to_migrate')); + add_action('wp_ajax_wpsdbmf_migrate_media', array($this, 'ajax_migrate_media')); + + // external AJAX handlers + add_action('wp_ajax_nopriv_wpsdbmf_get_remote_media_listing', array($this, 'respond_to_get_remote_media_listing')); + add_action('wp_ajax_nopriv_wpsdbmf_push_request', array($this, 'respond_to_push_request')); + add_action('wp_ajax_nopriv_wpsdbmf_remove_local_attachments', array($this, 'respond_to_remove_local_attachments')); + } + + public function get_local_attachments() + { + global $wpdb; + $prefix = $wpdb->prefix; + $temp_prefix = stripslashes($_POST['temp_prefix']); + + /* + * We determine which media files need migrating BEFORE the database migration is finalized. + * Because of this we need to scan the *_post & *_postmeta that are prefixed using the temporary prefix. + * Though this should only happen when we're responding to a get_remote_media_listing() call AND it's a push OR + * we're scanning local files AND it's a pull. + */ + + if ( + (true == $this->responding_to_get_remote_media_listing && $_POST['intent'] == 'push') || + (false == $this->responding_to_get_remote_media_listing && $_POST['intent'] == 'pull') + ) { + + $local_tables = array_flip($this->get_tables()); + + $posts_table_name = "{$temp_prefix}{$prefix}posts"; + $postmeta_table_name = "{$temp_prefix}{$prefix}postmeta"; + + if (isset($local_tables[$posts_table_name]) && isset($local_tables[$postmeta_table_name])) { + $prefix = $temp_prefix . $prefix; + } + + } + + $local_attachments = $wpdb->get_results( + "SELECT `{$prefix}posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata' + FROM `{$prefix}posts` + INNER JOIN `{$prefix}postmeta` pm1 ON `{$prefix}posts`.`ID` = pm1.`post_id` AND pm1.`meta_key` = '_wp_attached_file' + LEFT OUTER JOIN `{$prefix}postmeta` pm2 ON `{$prefix}posts`.`ID` = pm2.`post_id` AND pm2.`meta_key` = '_wp_attachment_metadata' + WHERE `{$prefix}posts`.`post_type` = 'attachment'", ARRAY_A + ); + + if (is_multisite()) { + $blogs = $this->get_blogs(); + $prefix = $wpdb->prefix; + foreach ($blogs as $blog) { + $posts_table_name = "{$temp_prefix}{$prefix}{$blog}_posts"; + $postmeta_table_name = "{$temp_prefix}{$prefix}{$blog}_postmeta"; + if (isset($local_tables[$posts_table_name]) && isset($local_tables[$postmeta_table_name])) { + $prefix = $temp_prefix . $prefix; + } + $attachments = $wpdb->get_results( + "SELECT `{$prefix}{$blog}_posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata', {$blog} AS 'blog_id' + FROM `{$prefix}{$blog}_posts` + INNER JOIN `{$prefix}{$blog}_postmeta` pm1 ON `{$prefix}{$blog}_posts`.`ID` = pm1.`post_id` AND pm1.`meta_key` = '_wp_attached_file' + LEFT OUTER JOIN `{$prefix}{$blog}_postmeta` pm2 ON `{$prefix}{$blog}_posts`.`ID` = pm2.`post_id` AND pm2.`meta_key` = '_wp_attachment_metadata' + WHERE `{$prefix}{$blog}_posts`.`post_type` = 'attachment'", ARRAY_A + ); + + $local_attachments = array_merge($attachments, $local_attachments); + } + } + + $local_attachments = array_map(array($this, 'process_attachment_data'), $local_attachments); + $local_attachments = array_filter($local_attachments); + + return $local_attachments; + } + + public function get_flat_attachments($attachments) + { + $flat_attachments = array(); + foreach ($attachments as $attachment) { + $flat_attachments[] = $attachment['file']; + if (isset($attachment['sizes'])) { + $flat_attachments = array_merge($flat_attachments, $attachment['sizes']); + } + } + return $flat_attachments; + } + + public function process_attachment_data($attachment) + { + if (isset($attachment['blog_id'])) { // used for multisite + if (defined('UPLOADBLOGSDIR')) { + $upload_dir = sprintf('%s/files/', $attachment['blog_id']); + } else { + $upload_dir = sprintf('sites/%s/', $attachment['blog_id']); + } + $attachment['file'] = $upload_dir . $attachment['file']; + } + $upload_dir = str_replace(basename($attachment['file']), '', $attachment['file']); + if (!empty($attachment['metadata'])) { + $attachment['metadata'] = @unserialize($attachment['metadata']); + if (!empty($attachment['metadata']['sizes']) && is_array($attachment['metadata']['sizes'])) { + foreach ($attachment['metadata']['sizes'] as $size) { + if (empty($size['file'])) { + continue; + } + + $attachment['sizes'][] = $upload_dir . $size['file']; + } + } + } + unset($attachment['metadata']); + return $attachment; + } + + public function uploads_dir() + { + if (defined('UPLOADBLOGSDIR')) { + $upload_dir = trailingslashit(ABSPATH) . UPLOADBLOGSDIR; + } else { + $upload_dir = wp_upload_dir(); + $upload_dir = $upload_dir['basedir']; + } + return trailingslashit($upload_dir); + } + + public function get_local_media() + { + $upload_dir = untrailingslashit($this->uploads_dir()); + if (!file_exists($upload_dir)) { + return array(); + } + + $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($upload_dir), RecursiveIteratorIterator::SELF_FIRST); + $local_media = array(); + + foreach ($files as $name => $object) { + $name = str_replace(array($upload_dir . DS, '\\'), array('', '/'), $name); + $local_media[$name] = $object->getSize(); + } + + return $local_media; + } + + public function ajax_migrate_media() + { + $this->check_ajax_referer('migrate-media'); + $this->set_time_limit(); + + if ($_POST['intent'] == 'pull') { + $result = $this->process_pull_request(); + return $result; + } + + $result = $this->process_push_request(); + return $result; + } + + public function process_pull_request() + { + $files_to_download = $_POST['file_chunk']; + $remote_uploads_url = trailingslashit($_POST['remote_uploads_url']); + $parsed = parse_url($_POST['url']); + if (!empty($parsed['user'])) { + $credentials = sprintf('%s:%s@', $parsed['user'], $parsed['pass']); + $remote_uploads_url = str_replace('://', '://' . $credentials, $remote_uploads_url); + } + + $upload_dir = $this->uploads_dir(); + + $errors = array(); + foreach ($files_to_download as $file_to_download) { + $temp_file_path = $this->download_url($remote_uploads_url . $file_to_download); + + if (is_wp_error($temp_file_path)) { + $download_error = $temp_file_path->get_error_message(); + $errors[] = __(sprintf('Could not download file: %1$s - %2$s', $remote_uploads_url . $file_to_download, $download_error), 'wp-sync-db-media-files'); + continue; + } + + $date = str_replace(basename($file_to_download), '', $file_to_download); + $new_path = $upload_dir . $date . basename($file_to_download); + + $move_result = @rename($temp_file_path, $new_path); + + if (false === $move_result) { + $folder = dirname($new_path); + if (@file_exists($folder)) { + $errors[] = __(sprintf('Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', $temp_file_path, $new_path), 'wp-sync-db-media-files') . ' (#103mf)'; + } else { + if (false === @mkdir($folder, 0755, true)) { + $errors[] = __(sprintf('Error attempting to create required directory: %s', $folder), 'wp-sync-db-media-files') . ' (#104mf)'; + } else { + $move_result = @rename($temp_file_path, $new_path); + if (false === $move_result) { + $errors[] = __(sprintf('Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', $temp_file_path, $new_path), 'wp-sync-db-media-files') . ' (#105mf)'; + } + } + } + } + } + + if (!empty($errors)) { + $return = array( + 'wpsdb_error' => 1, + 'body' => implode('
', $errors) . '
', + ); + $result = $this->end_ajax(json_encode($return)); + return $result; + } + + // not required, just here because we have to return something otherwise the AJAX fails + $return['success'] = 1; + $result = $this->end_ajax(json_encode($return)); + return $result; + } + + public function process_push_request() + { + $files_to_migrate = $_POST['file_chunk']; + + $upload_dir = $this->uploads_dir(); + + $body = ''; + foreach ($files_to_migrate as $file_to_migrate) { + $body .= $this->file_to_multipart($upload_dir . $file_to_migrate); + } + + $post_args = array( + 'action' => 'wpsdbmf_push_request', + 'files' => serialize($files_to_migrate), + ); + + $post_args['sig'] = $this->create_signature($post_args, $_POST['key']); + + $body .= $this->array_to_multipart($post_args); + + $args['body'] = $body; + $ajax_url = trailingslashit($_POST['url']) . 'wp-admin/admin-ajax.php'; + $response = $this->remote_post($ajax_url, '', __FUNCTION__, $args); + $response = $this->verify_remote_post_response($response); + + $result = $this->end_ajax(json_encode($response)); + return $result; + } + + public function respond_to_push_request() + { + $filtered_post = $this->filter_post_elements($_POST, array('action', 'files')); + $filtered_post['files'] = stripslashes($filtered_post['files']); + if (!$this->verify_signature($filtered_post, $this->settings['key'])) { + $return = array( + 'wpsdb_error' => 1, + 'body' => $this->invalid_content_verification_error . ' (#101mf)', + ); + $result = $this->end_ajax(serialize($return)); + return $result; + } + + if (!isset($_FILES['media'])) { + $return = array( + 'wpsdb_error' => 1, + 'body' => __('$_FILES is empty, the upload appears to have failed', 'wp-sync-db-media-files') . ' (#106mf)', + ); + $result = $this->end_ajax(serialize($return)); + return $result; + } + + $upload_dir = $this->uploads_dir(); + + $files = $this->diverse_array($_FILES['media']); + $file_paths = unserialize($filtered_post['files']); + $i = 0; + $errors = array(); + foreach ($files as &$file) { + $destination = $upload_dir . $file_paths[$i]; + $folder = dirname($destination); + + if (false === @file_exists($folder) && false === @mkdir($folder, 0755, true)) { + $errors[] = __(sprintf('Error attempting to create required directory: %s', $folder), 'wp-sync-db-media-files') . ' (#108mf)'; + ++$i; + continue; + } + + if (false === @move_uploaded_file($file['tmp_name'], $destination)) { + $errors[] = __(sprintf('A problem occurred when attempting to move the temp file "%1$s" to "%2$s"', $file['tmp_name'], $destination), 'wp-sync-db-media-files') . ' (#107mf)'; + } + ++$i; + } + + $return = array('success' => 1); + if (!empty($errors)) { + $return = array( + 'wpsdb_error' => 1, + 'body' => implode('
', $errors) . '
', + ); + } + $result = $this->end_ajax(serialize($return)); + return $result; + } + + public function ajax_determine_media_to_migrate() + { + $this->check_ajax_referer('determine-media-to-migrate'); + $this->set_time_limit(); + + $local_attachments = $this->get_local_attachments(); + $local_media = $this->get_local_media(); + + $data = array(); + $data['action'] = 'wpsdbmf_get_remote_media_listing'; + $data['temp_prefix'] = $this->temp_prefix; + $data['intent'] = $_POST['intent']; + $data['sig'] = $this->create_signature($data, $_POST['key']); + $ajax_url = trailingslashit($_POST['url']) . 'wp-admin/admin-ajax.php'; + $response = $this->remote_post($ajax_url, $data, __FUNCTION__); + $response = $this->verify_remote_post_response($response); + + $upload_dir = $this->uploads_dir(); + + $remote_attachments = $response['remote_attachments']; + $remote_media = $response['remote_media']; + + $this->files_to_migrate = array(); + + if ($_POST['intent'] == 'pull') { + $this->media_diff($local_attachments, $remote_attachments, $local_media, $remote_media); + } else { + $this->media_diff($remote_attachments, $local_attachments, $remote_media, $local_media); + } + + $return['files_to_migrate'] = $this->files_to_migrate; + $return['total_size'] = array_sum($this->files_to_migrate); + $return['remote_uploads_url'] = $response['remote_uploads_url']; + + // remove local/remote media if it doesn't exist on the local/remote site + if ($_POST['remove_local_media'] == '1') { + if ($_POST['intent'] == 'pull') { + $this->remove_local_attachments($remote_attachments); + } else { + $data = array(); + $data['action'] = 'wpsdbmf_remove_local_attachments'; + $data['remote_attachments'] = serialize($local_attachments); + $data['sig'] = $this->create_signature($data, $_POST['key']); + $ajax_url = trailingslashit($_POST['url']) . 'wp-admin/admin-ajax.php'; + $response = $this->remote_post($ajax_url, $data, __FUNCTION__); + // the response is ignored here (for now) as this is not a critical task + } + } + + $result = $this->end_ajax(json_encode($return)); + return $result; + } + + public function respond_to_remove_local_attachments() + { + $filtered_post = $this->filter_post_elements($_POST, array('action', 'remote_attachments')); + $filtered_post['remote_attachments'] = stripslashes($filtered_post['remote_attachments']); + if (!$this->verify_signature($filtered_post, $this->settings['key'])) { + $return = array( + 'wpsdb_error' => 1, + 'body' => $this->invalid_content_verification_error . ' (#109mf)', + ); + $result = $this->end_ajax(serialize($return)); + return $result; + } + + $remote_attachments = @unserialize($filtered_post['remote_attachments']); + if (false === $remote_attachments) { + $return = array( + 'wpsdb_error' => 1, + 'body' => __('Error attempting to unserialize the remote attachment data', 'wp-sync-db-media-files') . ' (#110mf)', + ); + $result = $this->end_ajax(serialize($return)); + return $result; + } + + $this->remove_local_attachments($remote_attachments); + + $return = array( + 'success' => 1, + ); + $result = serialize(json_encode($return)); + return $result; + } + + public function remove_local_attachments($remote_attachments) + { + $flat_remote_attachments = array_flip($this->get_flat_attachments($remote_attachments)); + $local_media = $this->get_local_media(); + // remove local media if it doesn't exist on the remote site + $temp_local_media = array_keys($local_media); + $allowed_mime_types = array_flip(get_allowed_mime_types()); + $upload_dir = $this->uploads_dir(); + foreach ($temp_local_media as $local_media_file) { + // don't remove folders + if (false === is_file($upload_dir . $local_media_file)) { + continue; + } + + $filetype = wp_check_filetype($local_media_file); + // don't remove files that we shouldn't remove, e.g. .php, .sql, etc + if (false === isset($allowed_mime_types[$filetype['type']])) { + continue; + } + + // don't remove files that exist on the remote site + if (true === isset($flat_remote_attachments[$local_media_file])) { + continue; + } + + @unlink($upload_dir . $local_media_file); + } + } + + public function media_diff($site_a_attachments, $site_b_attachments, $site_a_media, $site_b_media) + { + foreach ($site_b_attachments as $attachment) { + $local_attachment_key = $this->multidimensional_search(array('file' => $attachment['file']), $site_a_attachments); + if (false === $local_attachment_key) { + continue; + } + + $remote_timestamp = strtotime($attachment['date']); + $local_timestamp = strtotime($site_a_attachments[$local_attachment_key]['date']); + if ($local_timestamp >= $remote_timestamp) { + if (!isset($site_a_media[$attachment['file']])) { + $this->add_files_to_migrate($attachment, $site_b_media); + } else { + $this->maybe_add_resized_images($attachment, $site_b_media, $site_a_media); + } + } else { + $this->add_files_to_migrate($attachment, $site_b_media); + } + } + } + + public function add_files_to_migrate($attachment, $remote_media) + { + if (isset($remote_media[$attachment['file']])) { + $this->files_to_migrate[$attachment['file']] = $remote_media[$attachment['file']]; + } + if (empty($attachment['sizes']) || apply_filters('wpsdb_exclude_resized_media', false)) { + return; + } + + foreach ($attachment['sizes'] as $size) { + if (isset($remote_media[$size])) { + $this->files_to_migrate[$size] = $remote_media[$size]; + } + } + } + + public function maybe_add_resized_images($attachment, $site_b_media, $site_a_media) + { + if (empty($attachment['sizes']) || apply_filters('wpsdb_exclude_resized_media', false)) { + return; + } + + foreach ($attachment['sizes'] as $size) { + if (isset($site_b_media[$size]) && !isset($site_a_media[$size])) { + $this->files_to_migrate[$size] = $site_b_media[$size]; + } + } + } + + public function respond_to_get_remote_media_listing() + { + $filtered_post = $this->filter_post_elements($_POST, array('action', 'temp_prefix', 'intent')); + if (!$this->verify_signature($filtered_post, $this->settings['key'])) { + $return = array( + 'wpsdb_error' => 1, + 'body' => $this->invalid_content_verification_error . ' (#100mf)', + ); + $result = $this->end_ajax(serialize($return)); + return $result; + } + + if (defined('UPLOADBLOGSDIR')) { + $upload_url = home_url(UPLOADBLOGSDIR); + } else { + $upload_dir = wp_upload_dir(); + $upload_url = $upload_dir['baseurl']; + } + + $this->responding_to_get_remote_media_listing = true; + + $return['remote_attachments'] = $this->get_local_attachments(); + $return['remote_media'] = $this->get_local_media(); + $return['remote_uploads_url'] = $upload_url; + + $result = $this->end_ajax(serialize($return)); + return $result; + } + + public function migration_form_controls() + { + $this->template('migrate'); + } + + public function accepted_profile_fields($profile_fields) + { + $profile_fields[] = 'media_files'; + $profile_fields[] = 'remove_local_media'; + return $profile_fields; + } + + public function load_assets() + { + $plugins_url = trailingslashit(plugins_url()) . trailingslashit($this->plugin_folder_name); + $src = $plugins_url . 'asset/js/script.js'; + $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->plugin_version; + wp_enqueue_script('wp-sync-db-media-files-script', $src, array('jquery', 'wp-sync-db-common', 'wp-sync-db-hook', 'wp-sync-db-script'), $version, true); + + wp_localize_script('wp-sync-db-media-files-script', 'wpsdbmf_strings', array( + 'determining' => __("Determining which media files to migrate, please wait...", 'wp-sync-db-media-files'), + 'error_determining' => __("Error while attempting to determine which media files to migrate.", 'wp-sync-db-media-files'), + 'migration_failed' => __("Migration failed", 'wp-sync-db-media-files'), + 'problem_migrating_media' => __("A problem occurred when migrating the media files.", 'wp-sync-db-media-files'), + 'media_files' => __("Media Files", 'wp-sync-db-media-files'), + 'migrating_media_files' => __("Migrating media files", 'wp-sync-db-media-files'), + )); + + } + + public function establish_remote_connection_data($data) + { + $data['media_files_available'] = '1'; + $data['media_files_version'] = $this->plugin_version; + if (function_exists('ini_get')) { + $max_file_uploads = ini_get('max_file_uploads'); + } + $max_file_uploads = (empty($max_file_uploads)) ? 20 : $max_file_uploads; + $data['media_files_max_file_uploads'] = apply_filters('wpsdbmf_max_file_uploads', $max_file_uploads); + return $data; + } + + public function multidimensional_search($needle, $haystack) + { + if (empty($needle) || empty($haystack)) { + return false; + } + + foreach ($haystack as $key => $value) { + foreach ($needle as $skey => $svalue) { + $exists = (isset($haystack[$key][$skey]) && $haystack[$key][$skey] === $svalue); + } + if ($exists) { + return $key; + } + + } + + return false; + } + + public function get_blogs() + { + global $wpdb; + + $blogs = $wpdb->get_results( + "SELECT blog_id + FROM {$wpdb->blogs} + WHERE spam = '0' + AND deleted = '0' + AND archived = '0' + AND blog_id != 1 + "); + + $clean_blogs = array(); + foreach ($blogs as $blog) { + $clean_blogs[] = $blog->blog_id; + } + + return $clean_blogs; + } + + public function download_url($url, $timeout = 300) + { + //WARNING: The file is not automatically deleted, The script must unlink() the file. + if (!$url) { + return new WP_Error('http_no_url', __('Invalid URL Provided.')); + } + + $tmpfname = wp_tempnam($url); + if (!$tmpfname) { + return new WP_Error('http_no_file', __('Could not create Temporary file.')); + } + + $response = wp_remote_get($url, array('timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname, 'reject_unsafe_urls' => false)); + + if (is_wp_error($response)) { + unlink($tmpfname); + return $response; + } + + if (200 != wp_remote_retrieve_response_code($response)) { + unlink($tmpfname); + return new WP_Error('http_404', trim(wp_remote_retrieve_response_message($response))); + } + + return $tmpfname; + } + + public function js_variables() + { + ?> + var wpsdb_media_files_version = 'plugin_version; ?>'; + 1, 'body' => $this->error); + $result = $this->end_ajax(json_encode($return)); + return $result; + } + + if (!is_serialized(trim($response))) { + $return = array('wpsdb_error' => 1, 'body' => $response); + $result = $this->end_ajax(json_encode($return)); + return $result; + } + + $response = unserialize(trim($response)); + + if (isset($response['wpsdb_error'])) { + $result = $this->end_ajax(json_encode($response)); + return $result; + } + return $response; + } + + public function add_nonces($nonces) + { + $nonces['migrate_media'] = wp_create_nonce('migrate-media'); + $nonces['determine_media_to_migrate'] = wp_create_nonce('determine-media-to-migrate'); + return $nonces; + } + + public function cli_migration($outcome, $profile, $verify_connection_response, $initiate_migration_response) + { + global $wpsdb, $wpsdb_cli; + if (true !== $outcome) { + return $outcome; + } + + if (!isset($profile['media_files']) || '1' !== $profile['media_files']) { + return $outcome; + } + + if (!isset($verify_connection_response['media_files_max_file_uploads'])) { + return $wpsdb_cli->cli_error(__('WP Sync DB Media Files does not seems to be installed/active on the remote website.', 'wp-sync-db-media-files')); + } + + $this->set_time_limit(); + $wpsdb->set_cli_migration(); + $this->set_cli_migration(); + + $connection_info = explode("\n", $profile['connection_info']); + + $_POST['intent'] = $profile['action']; + $_POST['url'] = trim($connection_info[0]); + $_POST['key'] = trim($connection_info[1]); + $_POST['remove_local_media'] = (isset($profile['remove_local_media'])) ? 1 : 0; + $_POST['temp_prefix'] = $verify_connection_response['temp_prefix']; + + do_action('wpsdb_cli_before_determine_media_to_migrate', $profile, $verify_connection_response, $initiate_migration_response); + + $response = $this->ajax_determine_media_to_migrate(); + if (is_wp_error($determine_media_to_migrate_response = $wpsdb_cli->verify_cli_response($response, 'ajax_determine_media_to_migrate()'))) { + return $determine_media_to_migrate_response; + } + + $remote_uploads_url = $determine_media_to_migrate_response['remote_uploads_url']; + $files_to_migrate = $determine_media_to_migrate_response['files_to_migrate']; + // seems like this value needs to be different depending on pull/push? + $bottleneck = $wpsdb->get_bottleneck(); + + while (!empty($files_to_migrate)) { + $file_chunk_to_migrate = array(); + $file_chunk_size = 0; + $number_of_files_to_migrate = 0; + foreach ($files_to_migrate as $file_to_migrate => $file_size) { + if (empty($file_chunk_to_migrate)) { + $file_chunk_to_migrate[] = $file_to_migrate; + $file_chunk_size += $file_size; + unset($files_to_migrate[$file_to_migrate]); + ++$number_of_files_to_migrate; + } else { + if (($file_chunk_size + $file_size) > $bottleneck || $number_of_files_to_migrate >= $verify_connection_response['media_files_max_file_uploads']) { + break; + } else { + $file_chunk_to_migrate[] = $file_to_migrate; + $file_chunk_size += $file_size; + unset($files_to_migrate[$file_to_migrate]); + ++$number_of_files_to_migrate; + } + } + + $_POST['file_chunk'] = $file_chunk_to_migrate; + $_POST['remote_uploads_url'] = $remote_uploads_url; + + $response = $this->ajax_migrate_media(); + if (is_wp_error($migrate_media_response = $wpsdb_cli->verify_cli_response($response, 'ajax_migrate_media()'))) { + return $migrate_media_response; + } + + } + } + return true; + } } diff --git a/template/migrate.php b/template/migrate.php index bafc2ac..9f27620 100644 --- a/template/migrate.php +++ b/template/migrate.php @@ -1,23 +1,23 @@ - +
- -
- -
- - + +
+ +
+ +
diff --git a/wp-sync-db-media-files.php b/wp-sync-db-media-files.php index f4497c4..8c4067c 100644 --- a/wp-sync-db-media-files.php +++ b/wp-sync-db-media-files.php @@ -7,26 +7,32 @@ Author URI: http://slang.cx GitHub Plugin URI: wp-sync-db/wp-sync-db-media-files Network: True -*/ + */ require_once 'version.php'; -$GLOBALS['wpsdb_meta']['wp-sync-db-media-files']['folder'] = basename( plugin_dir_path( __FILE__ ) ); +$GLOBALS['wpsdb_meta']['wp-sync-db-media-files']['folder'] = basename(plugin_dir_path(__FILE__)); -function wp_sync_db_media_files_loaded() { - if ( ! class_exists( 'WPSDB_Addon' ) ) return; +function wp_sync_db_media_files_loaded() +{ + if (!class_exists('WPSDB_Addon')) { + return; + } - require_once 'class/wpsdb-media-files.php'; + require_once 'class/wpsdb-media-files.php'; - global $wpsdb_media_files; - $wpsdb_media_files = new WPSDB_Media_Files( __FILE__ ); + global $wpsdb_media_files; + $wpsdb_media_files = new WPSDB_Media_Files(__FILE__); } -add_action( 'plugins_loaded', 'wp_sync_db_media_files_loaded', 20 ); +add_action('plugins_loaded', 'wp_sync_db_media_files_loaded', 20); -function wp_sync_db_media_files_init() { - if ( ! class_exists( 'WPSDB_Addon' ) ) return; +function wp_sync_db_media_files_init() +{ + if (!class_exists('WPSDB_Addon')) { + return; + } - load_plugin_textdomain( 'wp-sync-db-media-files', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); + load_plugin_textdomain('wp-sync-db-media-files', false, dirname(plugin_basename(__FILE__)) . '/languages/'); } -add_action( 'admin_init', 'wp_sync_db_media_files_init', 20 ); +add_action('admin_init', 'wp_sync_db_media_files_init', 20);