Added dependency plugins

This commit is contained in:
Moris Zen
2018-06-25 00:00:37 +02:00
parent 720a1c31a4
commit f069f6782f
698 changed files with 289637 additions and 1 deletions

View File

@@ -0,0 +1 @@
.sub-option{margin:5px 0 0 25px}.option-section.sub-option{border:none;margin:10px 0 0;padding:0}.wpmdb .sub-option .checkbox-label,body.mp6 .wpmdb .sub-option .checkbox-label{font-size:14px;font-weight:normal}.media-progress{display:none}.media.progress-text{margin-bottom:15px}.compare-remove-warning{font-size:13px;font-weight:bold;color:#9c1f1f;margin-left:26px}div.mf-selected-subsites-wrap.select-wrap{display:block}

View File

@@ -0,0 +1,676 @@
var wpmdb = wpmdb || {};
wpmdb.mediaFiles = {
remote_media_files_unavailable: false
};
(function( $, wpmdb ) {
var remote_max_upload_size = 0;
var $mf_select_subsites_section = $( '#mf-select-subsites-section' );
var $mf_select_subsites = $( '#mf-select-subsites' );
// TODO: move polyfills and/or decide if they're necessary
// .length doesn't work on JS "associative arrays" i.e. objects with key/value elements, this does
if ( ! Object.size ) {
Object.size = function( obj ) {
var size = 0, key;
for ( key in obj ) {
if ( obj.hasOwnProperty( key ) ) {
size++;
}
}
return size;
};
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if ( ! Object.keys ) {
Object.keys = ( function() {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = ! ( { toString: null } ).propertyIsEnumerable( 'toString' ),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function( obj ) {
if ( 'object' !== typeof obj && ( 'function' !== typeof obj || null === obj ) ) {
throw new TypeError( 'Object.keys called on non-object' );
}
var result = [], prop, i;
for ( prop in obj ) {
if ( hasOwnProperty.call( obj, prop ) ) {
result.push( prop );
}
}
if ( hasDontEnumBug ) {
for ( i = 0; i < dontEnumsLength; i++ ) {
if ( hasOwnProperty.call( obj, dontEnums[ i ] ) ) {
result.push( dontEnums[ i ] );
}
}
}
return result;
};
}() );
}
var disable_media_files_option = function() {
$( '#media-files' ).attr( 'data-available', '0' );
$( '#media-files' ).prop( 'checked', false );
$( '#media-files' ).attr( 'disabled', 'disabled' );
$( '.media-files' ).addClass( 'disabled' );
$( '.media-files-options .expandable-content' ).hide();
};
var hide_show_options = function( unavailable ) {
var mig_type = wpmdb_migration_type();
if ( -1 !== $.inArray( mig_type, [ 'savefile', 'find_replace' ] ) ) {
$( '.media-files-options' ).hide();
return;
}
$( '.media-files-options' ).show();
$( '.media-files-push' ).hide();
if ( unavailable ) {
$( '.media-files-options ul' ).hide();
$( '.media-migration-unavailable' ).show();
disable_media_files_option();
return;
}
if ( 'undefined' !== typeof wpmdb.mediaFiles.remote_connection_data && wpmdb_data.media_files_version !== wpmdb.mediaFiles.remote_connection_data.media_files_version ) {
$( '.media-files-remote-location' ).html( wpmdb.mediaFiles.remote_connection_data.url );
$( '.media-file-remote-version' ).html( wpmdb.mediaFiles.remote_connection_data.media_files_version );
$( '.media-files-different-plugin-version-notice' ).show();
disable_media_files_option();
return;
}
if ( 'true' === wpmdb_data.is_multisite ) {
var subsites = get_subsites();
var $_mf_selected_subsites = $( '#_mf-selected-subsites' );
var selected_subsites = $( '#mf-selected-subsites' ).val();
if ( 'pull' === mig_type && 0 < Object.size( subsites ) && $_mf_selected_subsites.length ) {
selected_subsites = $.parseJSON( $_mf_selected_subsites.val() );
$_mf_selected_subsites.remove();
}
wpmdb.multisite.update_multiselect( '#mf-selected-subsites', subsites, selected_subsites );
var enable_select_subsites = $.wpmdb.apply_filters( 'wpmdbmf_enable_select_subsites', true );
if ( enable_select_subsites ) {
$mf_select_subsites_section.show();
} else {
$mf_select_subsites.prop( 'checked', false );
$mf_select_subsites_section.hide();
}
$mf_select_subsites.change();
maybe_show_data_and_files_differ_notice();
}
$( '.media-files-options ul' ).show();
$( '.media-migration-unavailable' ).hide();
$( '.media-files-different-plugin-version-notice' ).hide();
$( '#media-files' ).removeAttr( 'disabled' );
$( '.media-files' ).removeClass( 'disabled' );
$( '#media-files' ).attr( 'data-available', '1' );
};
wpmdb.functions.prepare_remove_all_files = function() {
wpmdb.mediaFiles.connection_info = $.trim( $( '.pull-push-connection-info' ).val() ).split( '\n' );
var media_type = $( 'input[name="media_migration_option"]:checked' ).val();
wpmdb.current_migration.model.setActiveStage( 'media' );
// this only needs to be run if we are skipping the comparison
if ( 'entire' === media_type ) {
var title = 'removing_all_files_' + wpmdb_migration_type();
wpmdb.current_migration.setText( wpmdbmf_strings[ title ] );
// start recursive batch delete of local files
var args = {};
args.remove_files = 1;
args.compare = 0;
args.offset = 0;
args.next_step_in_migration = wpmdb.functions.prepare_determine_media;
wpmdb.common.next_step_in_migration = { fn: wpmdb.functions.remove_files_recursive, args: [ args ] };
wpmdb.functions.execute_next_step();
} else {
// We are doing the comparison so lets start the determine
wpmdb.common.next_step_in_migration = { fn: wpmdb.functions.prepare_determine_media };
wpmdb.functions.execute_next_step();
}
};
wpmdb.functions.remove_files_recursive = function( args ) {
if ( 0 === args.remove_files ) {
// All files removed lets start the migration
if ( false !== args.next_step_in_migration ) {
wpmdb.common.next_step_in_migration = { fn: args.next_step_in_migration };
wpmdb.functions.execute_next_step();
} else {
wpmdb_call_next_hook();
}
return;
}
wpmdb.mediaFiles.connection_info = $.trim( $( '.pull-push-connection-info' ).val() ).split( '\n' );
var old_args = args;
$.ajax( {
url: ajaxurl,
type: 'POST',
dataType: 'text',
cache: false,
data: {
action: 'wpmdbmf_remove_files_recursive',
migration_state_id: wpmdb.migration_state_id,
compare: args.compare,
offset: JSON.stringify( args.offset ),
nonce: wpmdb_data.nonces.remove_files_recursive
},
error: function( jqXHR, textStatus, errorThrown ) {
wpmdb.current_migration.setState( wpmdbmf_strings.migration_failed, wpmdbGetAjaxErrors( wpmdbmf_strings.error_determining, '(#101mf)', jqXHR.responseText, jqXHR ), 'error' );
console.log( jqXHR );
console.log( textStatus );
console.log( errorThrown );
wpmdb.common.migration_error = true;
wpmdb.functions.migration_complete_events();
return;
},
success: function( data ) {
var original_data = data;
args = wpmdb_parse_json( $.trim( data ) );
if ( !args ) {
migration_failed( original_data );
return;
}
if ( 'undefined' !== typeof args.wpmdb_error && 1 === args.wpmdb_error ) {
migration_failed( args.body );
return;
}
if ( 'undefined' !== typeof args.wpmdb_non_fatal_error && 1 === args.wpmdb_non_fatal_error ) {
wpmdb.common.non_fatal_errors += args.body;
}
// persist settings
args.next_step_in_migration = old_args.next_step_in_migration;
wpmdb.common.next_step_in_migration = {
fn: wpmdb.functions.remove_files_recursive,
args: [ args ]
};
wpmdb.functions.execute_next_step();
}
} );
};
wpmdb.functions.prepare_determine_media = function() {
wpmdb.mediaFiles.connection_info = $.trim( $( '.pull-push-connection-info' ).val() ).split( '\n' );
remote_max_upload_size = 0;
var remove_local_media = 0;
var copy_entire_media = 0;
var media_type = $( 'input[name="media_migration_option"]:checked' ).val();
wpmdb.current_migration.setText( '0% - ' + wpmdbmf_strings.determining );
if ( 'compare-remove' === media_type ) {
media_type = 'compare';
remove_local_media = 1;
}
if ( 'entire' === media_type ) {
copy_entire_media = 1;
}
var args = {};
$.ajax( {
url: ajaxurl,
type: 'POST',
dataType: 'text',
cache: false,
data: {
action: 'wpmdbmf_prepare_determine_media',
migration_state_id: wpmdb.migration_state_id,
nonce: wpmdb_data.nonces.prepare_determine_media
},
error: function( jqXHR, textStatus, errorThrown ) {
wpmdb.current_migration.setState( wpmdbmf_strings.migration_failed, wpmdbGetAjaxErrors( wpmdbmf_strings.error_determining, '(#101mf)', jqXHR.responseText, jqXHR ), 'error' );
console.log( jqXHR );
console.log( textStatus );
console.log( errorThrown );
wpmdb.common.migration_error = true;
wpmdb.functions.migration_complete_events();
return;
},
success: function( data ) {
var original_data = data;
args = wpmdb_parse_json( $.trim( data ) );
if ( !args ) {
migration_failed( original_data );
return;
}
if ( 'undefined' !== typeof args.wpmdb_error && 1 === args.wpmdb_error ) {
migration_failed( args.body );
return;
}
remote_max_upload_size = args.remote_max_upload_size;
args.determine_progress = 0;
args.remove_local_media = remove_local_media;
args.copy_entire_media = copy_entire_media;
wpmdb.common.next_step_in_migration = {
fn: wpmdb.functions.determine_media_to_migrate_recursive,
args: [ args ]
};
wpmdb.functions.execute_next_step();
}
} );
};
wpmdb.functions.determine_media_to_migrate_recursive = function( args ) {
if ( args.determine_progress >= args.attachment_count ) {
// migrate files
wpmdb.common.next_step_in_migration = { fn: wpmdb.functions.media_successfully_determined, args: [ args ] };
wpmdb.functions.execute_next_step();
return;
}
wpmdb.mediaFiles.connection_info = $.trim( $( '.pull-push-connection-info' ).val() ).split( '\n' );
$.ajax( {
url: ajaxurl,
type: 'POST',
dataType: 'text',
cache: false,
data: {
action: 'wpmdbmf_determine_media_to_migrate_recursive',
migration_state_id: wpmdb.migration_state_id,
determine_progress: args.determine_progress,
attachment_count: args.attachment_count,
remote_uploads_url: args.remote_uploads_url,
remove_local_media: args.remove_local_media,
copy_entire_media: args.copy_entire_media,
blogs: args.blogs,
attachment_batch_limit: args.attachment_batch_limit,
nonce: wpmdb_data.nonces.determine_media_to_migrate_recursive
},
error: function( jqXHR, textStatus, errorThrown ) {
wpmdb.current_migration.setState( wpmdbmf_strings.migration_failed, wpmdbmf_strings.error_determining + ' (#101mf)', 'error' );
console.log( jqXHR );
console.log( textStatus );
console.log( errorThrown );
wpmdb.common.migration_error = true;
wpmdb.functions.migration_complete_events();
return;
},
success: function( data ) {
var original_data = data;
data = wpmdb_parse_json( $.trim( data ) );
if ( !data ) {
migration_failed( original_data );
return;
}
if ( 'undefined' !== typeof data.wpmdb_error && 1 === data.wpmdb_error ) {
migration_failed( data.body );
return;
}
args.blogs = data.blogs;
args.determine_progress = data.determine_progress;
args.total_size = args.total_size || 0;
args.total_size += data.total_size;
args.files_to_migrate = args.files_to_migrate || {};
_.each( data.files_to_migrate, function( data, filepath ) {
args.files_to_migrate[ filepath ] = data;
wpmdb.current_migration.model.addStageItem( 'media', filepath, parseInt( data / 1024 ) );
} );
wpmdb.current_migration.fixProgressStageWidthForScrollBar();
var percent = Math.min( 100, 100 * args.determine_progress / args.attachment_count );
var overall_percent = Math.floor( percent );
// Not "real" progress as far as model is concerned, so we force the progress bar to show media determine progress by accessing it directly
$( '.migration-progress-stage-container[data-stage=media]' ).addClass( 'determining-media' );
$( '.progress-bar', '.stage-progress.media' ).width( percent + '%' );
wpmdb.current_migration.setText( overall_percent + '% - ' + wpmdbmf_strings.determining );
wpmdb.common.next_step_in_migration = {
fn: wpmdb.functions.determine_media_to_migrate_recursive,
args: [ args ]
};
wpmdb.functions.execute_next_step();
}
} );
};
wpmdb.functions.media_successfully_determined = function( args ) {
if ( 'undefined' !== typeof args.wpmdb_error && 1 === args.wpmdb_error ) {
wpmdb.common.non_fatal_errors += data.body;
wpmdb.common.next_step_in_migration = { fn: wpmdb_call_next_hook };
wpmdb.functions.execute_next_step();
return;
}
args.media_progress = 0;
args.media_progress_image_number = 0;
args.bottleneck = wpmdb_data.max_request;
args.files_to_migrate = args.files_to_migrate || {};
var title = 'migrate_media_files_' + wpmdb_migration_type();
wpmdb.current_migration.setText( wpmdbmf_strings[ title ] );
$( '.migration-progress-stage-container[data-stage=media]' ).removeClass( 'determining-media' );
wpmdb.common.next_step_in_migration = { fn: migrate_media_files_recursive, args: [ args ] };
wpmdb.functions.execute_next_step();
};
function migrate_media_files_recursive( args ) {
if ( 0 === Object.size( args.files_to_migrate ) ) {
delete args.files_to_migrate;
delete args.total_size;
wpmdb.common.next_step_in_migration = {
fn: wpmdb.functions.finalise_media_migration,
args: [ args ]
};
wpmdb.functions.execute_next_step();
return;
}
var file_chunk_to_migrate = [];
var file_chunk_size = 0;
var number_of_files_to_migrate = 0;
$.each( args.files_to_migrate, function( index, value ) {
if ( 'push' === wpmdb_migration_type() && value > remote_max_upload_size ) {
var error_msg = wpmdbmf_strings.file_too_large + ' ' + index + ' (#124mf)<br>';
wpmdb.common.non_fatal_errors += error_msg;
} else if ( !file_chunk_to_migrate.length ) {
file_chunk_to_migrate.push( index );
file_chunk_size += value;
} else {
if ( ( file_chunk_size + value ) > args.bottleneck ||
number_of_files_to_migrate >= wpmdb.mediaFiles.remote_connection_data.media_files_max_file_uploads ) {
return false;
} else {
file_chunk_to_migrate.push( index );
file_chunk_size += value;
}
}
delete args.files_to_migrate[ index ];
++args.media_progress_image_number;
++number_of_files_to_migrate;
} );
if ( wpmdb.common.migration_error ) {
wpmdb.functions.migration_complete_events();
return;
}
// If nothing made it into this batch let the top of the function determine whether to do another or move on a step.
if ( !file_chunk_to_migrate.length ) {
wpmdb.common.next_step_in_migration = { fn: migrate_media_files_recursive, args: [ args ] };
wpmdb.functions.execute_next_step();
return;
}
$.ajax( {
url: ajaxurl,
type: 'POST',
dataType: 'text',
cache: false,
data: {
action: 'wpmdbmf_migrate_media',
migration_state_id: wpmdb.migration_state_id,
file_chunk: file_chunk_to_migrate,
nonce: wpmdb_data.nonces.migrate_media
},
error: function( jqXHR, textStatus, errorThrown ) {
wpmdb.current_migration.setState( wpmdbmf_strings.migration_failed, wpmdbGetAjaxErrors( wpmdbmf_strings.migration_failed, '(#102mf)', jqXHR.responseText, jqXHR ), 'error' );
console.log( jqXHR );
console.log( textStatus );
console.log( errorThrown );
wpmdb.common.migration_error = true;
wpmdb.functions.migration_complete_events();
return;
},
success: function( data ) {
var original_data = data;
data = wpmdb_parse_json( $.trim( data ) );
if ( !data ) {
migration_failed( original_data );
return;
}
if ( 'undefined' !== typeof data.wpmdb_error && 1 === data.wpmdb_error ) {
migration_failed( data.body );
return;
}
if ( 'undefined' !== typeof data.wpmdb_non_fatal_error && 1 === data.wpmdb_non_fatal_error ) {
wpmdb.common.non_fatal_errors += data.body;
}
if ( data.transfers && data.transfers.length ) {
var timeout = 0;
$.each( data.transfers, function( i, transfer ) {
// timeout used to stagger display of progress bars filling
setTimeout( function() {
wpmdb.current_migration.model.getStageModel( 'media' ).setItemComplete( transfer.file );
}, timeout );
timeout += 50;
} );
}
args.media_progress += file_chunk_size;
wpmdb.common.next_step_in_migration = { fn: migrate_media_files_recursive, args: [ args ] };
wpmdb.functions.execute_next_step();
}
} );
}
wpmdb.functions.finalise_media_migration = function( args ) {
// If removing local media not found on remote
if ( 1 === args.remove_local_media ) {
// Start recursive batch delete of local files not found on remote
var title = 'removing_files_' + wpmdb_migration_type();
wpmdb.current_migration.setText( wpmdbmf_strings[ title ] );
args = {};
args.remove_files = 1;
args.compare = 1;
args.offset = '';
args.next_step_in_migration = false;
wpmdb.common.next_step_in_migration = { fn: wpmdb.functions.remove_files_recursive, args: [ args ] };
wpmdb.functions.execute_next_step();
return;
}
wpmdb_call_next_hook();
};
function migration_failed( data ) {
wpmdb.current_migration.setState( wpmdbmf_strings.migration_failed, wpmdbGetAjaxErrors( '', '', data ), 'error' );
wpmdb.common.migration_error = true;
wpmdb.functions.migration_complete_events();
}
function is_media_migration() {
if ( '1' === $( '#media-files' ).attr( 'data-available' ) && $( '#media-files' ).is( ':checked' ) ) {
return true;
}
return false;
}
function filter_migration_profile_ready( value, args ) {
if ( 'savefile' !== wpmdb_migration_type() && $( '#media-files' ).is( ':checked' ) ) {
// Check that at least one subsite is selected if using limit to selected subsites option.
if ( 'true' === wpmdb_data.is_multisite && $mf_select_subsites.is( ':checked' ) ) {
if ( null === $( '#mf-selected-subsites' ).val() ) {
alert( wpmdbmf_strings.please_select_a_subsite );
value = false;
}
}
}
return value;
}
function compare_remove_warning_toggle() {
var element = $( 'input[name="media_migration_option"][value="compare-remove"]' );
if ( $( element ).is( ':checked' ) ) {
$( '.compare-remove-warning' ).show();
} else {
$( '.compare-remove-warning' ).hide();
}
}
function get_subsites() {
var subsites = {};
if ( 'pull' === wpmdb_migration_type() ) {
if ( 'undefined' !== typeof wpmdb.mediaFiles.remote_connection_data && 'undefined' !== typeof wpmdb.mediaFiles.remote_connection_data.subsites ) {
subsites = wpmdb.mediaFiles.remote_connection_data.subsites;
}
} else if ( undefined !== wpmdb_data.subsites ) {
subsites = wpmdb_data.subsites;
}
return subsites;
}
function maybe_show_data_and_files_differ_notice() {
var $notice = $( '.mf-selected-subsites-tables-differ' );
var selected_subsites = $( '#mf-selected-subsites' ).val();
var selected_tables = $.wpmdb.apply_filters( 'wpmdb_get_tables_to_migrate', null, null );
if ( 'true' === wpmdb_data.is_multisite &&
$mf_select_subsites.is( ':checked' ) &&
undefined !== selected_subsites &&
null !== selected_subsites &&
undefined !== selected_tables &&
null !== selected_tables &&
0 < selected_tables.length ) {
var table_prefix = $.wpmdb.apply_filters( 'wpmdb_get_table_prefix', null, null );
var files_differ = false;
$.each( selected_tables, function( index, table_name ) {
if ( wpmdb.table_is( table_prefix, 'posts', table_name ) || wpmdb.table_is( table_prefix, 'postmeta', table_name ) ) {
var id = wpmdb.subsite_for_table( table_prefix, table_name );
if ( 0 > $.inArray( id.toString(), selected_subsites ) ) {
files_differ = true;
return false;
}
}
} );
if ( files_differ ) {
$notice.show();
} else {
$notice.hide();
}
} else {
$notice.hide();
}
}
function maybe_add_mf_progress_stage( args ) {
if ( true === is_media_migration() && 'savefile' !== wpmdb_migration_type() ) {
wpmdb.current_migration.model.addStage( 'media', [], args.dataType, {
strings: {
itemsName: wpmdb_strings.files
}
} );
}
}
$( document ).ready( function() {
if ( -1 !== $.inArray( wpmdb_migration_type(), [ 'savefile', 'find_replace' ] ) ) {
$( '.media-files-options' ).hide();
}
$.wpmdb.add_action( 'move_connection_info_box', function() {
hide_show_options( wpmdb.mediaFiles.remote_media_files_unavailable );
wpmdb_toggle_migration_action_text();
} );
$.wpmdb.add_action( 'verify_connection_to_remote_site', function( connection_data ) {
wpmdb.mediaFiles.remote_connection_data = connection_data;
wpmdb.mediaFiles.remote_media_files_unavailable = ( 'undefined' === typeof connection_data.media_files_available );
hide_show_options( wpmdb.mediaFiles.remote_media_files_unavailable );
} );
$.wpmdb.add_action( 'wpmdbmst_select_subsite_changed', function() {
hide_show_options( wpmdb.mediaFiles.remote_media_files_unavailable );
} );
$.wpmdb.add_filter( 'wpmdb_before_migration_complete_hooks', function( hooks ) {
if ( false === is_media_migration() || 'savefile' === wpmdb_migration_type() ) {
return hooks;
}
hooks.push( wpmdb.functions.prepare_remove_all_files );
return hooks;
} );
$( 'body' ).on( 'change', '#mf-select-subsites', function() {
$.wpmdb.do_action( 'wpmdbmf_selected_subsites_changed' );
} );
$( 'body' ).on( 'change', '#mf-selected-subsites', function() {
$.wpmdb.do_action( 'wpmdbmf_selected_subsites_changed' );
} );
$.wpmdb.add_filter( 'wpmdb_migration_profile_ready', filter_migration_profile_ready );
$.wpmdb.add_action( 'wpmdb_tables_to_migrate_changed', maybe_show_data_and_files_differ_notice );
$.wpmdb.add_action( 'wpmdbmf_selected_subsites_changed', maybe_show_data_and_files_differ_notice );
$.wpmdb.add_action( 'wpmdb_add_migration_stages', maybe_add_mf_progress_stage );
$( 'input[name="media_migration_option"]' ).change( function() {
compare_remove_warning_toggle();
} );
compare_remove_warning_toggle();
} );
})( jQuery, wpmdb );

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
<?php
class WPMDBPro_Media_Files_CLI_Bar extends \cli\progress\Bar {
public function setMessage( $message ) {
$this->_message = $message;
}
}

View File

@@ -0,0 +1,194 @@
<?php
class WPMDBPro_Media_Files_CLI extends WPMDBPro_Media_Files {
function __construct( $plugin_file_path ) {
parent::__construct( $plugin_file_path );
// compatibility with CLI migrations
add_filter( 'wpmdb_pro_cli_finalize_migration', array( $this, 'cli_migration' ), 10, 4 );
}
/**
* Run the media migration from the CLI
*
* @param bool $outcome
* @param array $profile
* @param array $verify_connection_response
* @param array $initiate_migration_response
*
* @return bool
*/
function cli_migration( $outcome, $profile, $verify_connection_response, $initiate_migration_response ) {
global $wpmdbpro, $wpmdbpro_cli;
if ( true !== $outcome ) {
return $outcome;
}
if ( empty( $profile['media_files'] ) ) {
return $outcome;
}
if ( ! isset( $verify_connection_response['media_files_max_file_uploads'] ) ) {
return $wpmdbpro_cli->cli_error( __( 'WP Migrate DB Pro Media Files does not seem to be installed/active on the remote website.', 'wp-migrate-db-pro-media-files' ) );
}
WP_CLI::log( __( 'Initiating media migration...', 'wp-migrate-db-pro-media-files' ) );
$this->set_time_limit();
$wpmdbpro->set_cli_migration();
$this->set_cli_migration();
$this->media_files_local->set_cli_migration();
$intent = $profile['action'];
$_POST['migration_state_id'] = $initiate_migration_response['migration_state_id'];
$media_type = ( isset( $profile['media_migration_option'] ) ) ? $profile['media_migration_option'] : 'compare';
$copy_entire_media = ( 'entire' === $media_type ) ? 1 : 0;
$remove_local_media = ( 'compare' === $media_type && isset( $profile['remove_local_media'] ) ) ? $profile['remove_local_media'] : 0;
if ( 'compare-remove' == $media_type ) {
$media_type = 'compare';
$remove_local_media = 1;
}
// seems like this value needs to be different depending on pull/push?
$bottleneck = $wpmdbpro->get_bottleneck();
// if skipping comparison delete all files before migration
if ( 'entire' === $media_type ) {
do_action( 'wpmdb_cli_before_remove_files_recursive', $profile, $verify_connection_response, $initiate_migration_response );
WP_CLI::log( $this->get_string( 'removing_all_files_' . $intent ) . '...' );
$compare = 0;
$offset = 0;
$remove_files = 1;
while ( 1 == $remove_files ) {
$_POST['compare'] = $compare;
$_POST['offset'] = json_encode( $offset );
$response = $this->media_files_local->ajax_remove_files_recursive();
if ( is_wp_error( $remove_files_recursive_response = $wpmdbpro_cli->verify_cli_response( $response, 'ajax_remove_files_recursive()' ) ) ) {
return $remove_files_recursive_response;
}
$remove_files = $remove_files_recursive_response['remove_files'];
$compare = $remove_files_recursive_response['compare'];
$offset = $remove_files_recursive_response['offset'];
} // END recursive removal of files
}
// start the recursive determine
do_action( 'wpmdb_cli_before_determine_media_to_migrate', $profile, $verify_connection_response, $initiate_migration_response );
$response = $this->media_files_local->ajax_prepare_determine_media();
if ( is_wp_error( $prepare_media_to_migrate_response = $wpmdbpro_cli->verify_cli_response( $response, 'ajax_prepare_determine_media()' ) ) ) {
return $prepare_media_to_migrate_response;
}
$attachment_batch_limit = $prepare_media_to_migrate_response['attachment_batch_limit'];
$remote_uploads_url = $prepare_media_to_migrate_response['remote_uploads_url'];
$attachment_count = $prepare_media_to_migrate_response['attachment_count'];
$blogs = $prepare_media_to_migrate_response['blogs'];
$determine_progress = 0;
// determine the media to migrate in batches
while ( $determine_progress < $attachment_count ) {
$_POST['attachment_batch_limit'] = $attachment_batch_limit;
$_POST['remote_uploads_url'] = $remote_uploads_url;
$_POST['attachment_count'] = $attachment_count;
$_POST['blogs'] = $blogs;
$_POST['determine_progress'] = $determine_progress;
$_POST['copy_entire_media'] = $copy_entire_media;
$_POST['remove_local_media'] = $remove_local_media;
$response = $this->media_files_local->ajax_determine_media_to_migrate_recursive();
if ( is_wp_error( $determine_media_to_migrate_recursive_response = $wpmdbpro_cli->verify_cli_response( $response, 'ajax_determine_media_to_migrate_recursive_response()' ) ) ) {
return $determine_media_to_migrate_recursive_response;
}
$blogs = $determine_media_to_migrate_recursive_response['blogs'];
$determine_progress = $determine_media_to_migrate_recursive_response['determine_progress'];
$total_size = $determine_media_to_migrate_recursive_response['total_size'];
$files_to_migrate = $determine_media_to_migrate_recursive_response['files_to_migrate'];
$percent = ( $determine_progress / $attachment_count ) * 100;
WP_CLI::log( sprintf( $this->get_string( 'determining_progress' ), $determine_progress, $attachment_count, round( $percent ) ) );
$total_files = count( $files_to_migrate );
if ( $total_files > 0 ) {
$migrate_bar = new WPMDBPro_Media_Files_CLI_Bar( sprintf( $this->get_string( 'migrate_media_files_cli_' . $intent ), 0, $total_files ), 0 );
$migrate_bar->setTotal( $total_size );
$current_file_index = 0;
// start the recursive migration of the files we have just determined
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;
}
}
}
$current_file_index += $number_of_files_to_migrate;
$migrate_bar->setMessage( sprintf( $this->get_string( 'migrate_media_files_cli_' . $intent ), $current_file_index, $total_files ) );
$_POST['file_chunk'] = $file_chunk_to_migrate;
do_action( 'wpmdb_media_files_cli_before_migrate_media' );
$response = $this->media_files_local->ajax_migrate_media();
if ( is_wp_error( $migrate_media_response = $wpmdbpro_cli->verify_cli_response( $response, 'ajax_migrate_media()' ) ) ) {
return $migrate_media_response;
}
$migrate_bar->tick( $file_chunk_size );
} // END recursive media migration
// force migrate bar to show completion
$migrate_bar->setMessage( sprintf( $this->get_string( 'migrate_media_files_cli_' . $intent ), $total_files, $total_files ) );
$migrate_bar->finish();
}
} // END recursive media determine
// if removing local media not found on remote after comparison
if ( 1 == $remove_local_media ) {
// start recursive batch delete of local files not found on remote
do_action( 'wpmdb_cli_before_remove_files_not_found_recursive', $profile, $verify_connection_response, $initiate_migration_response );
WP_CLI::log( $this->get_string( 'removing_files_' . $intent ) . '...' );
$compare = 1;
$offset = '';
$remove_files = 1;
while ( 1 == $remove_files ) {
$_POST['compare'] = $compare;
$_POST['offset'] = json_encode( $offset );
$response = $this->media_files_local->ajax_remove_files_recursive();
if ( is_wp_error( $remove_files_recursive_response = $wpmdbpro_cli->verify_cli_response( $response, 'ajax_remove_files_recursive()' ) ) ) {
return $remove_files_recursive_response;
}
$remove_files = $remove_files_recursive_response['remove_files'];
$compare = $remove_files_recursive_response['compare'];
$offset = $remove_files_recursive_response['offset'];
} // END recursive removal of files
}
return true;
}
}

View File

@@ -0,0 +1,844 @@
<?php
/**
* Class WPMDBPro_Media_Files_Base
*
* Base class that holds common functionality required by both WPMDBPro_Media_Files_Local and
* WPMDBPro_Media_Files_Remote. Extends WPMDBPro_Addon as we require functionality included in the WPMDB base classes.
* Never instantiated on its own.
*/
class WPMDBPro_Media_Files_Base extends WPMDBPro_Addon {
/**
* The number of seconds a batch should run for when queueing or comparing attachments
*
* @var int $media_diff_batch_time
*/
protected $media_diff_batch_time;
/**
* The max number of attachments in a batch
*
* @var int $media_diff_batch_limit
*/
protected $media_diff_batch_limit;
/**
* The number of seconds a batch should run for when scanning for files
*
* @var int $media_files_batch_time_limit
*/
protected $media_files_batch_time_limit;
protected $accepted_fields;
public function __construct( $plugin_file_path ) {
parent::__construct( $plugin_file_path );
$this->media_diff_batch_time = apply_filters( 'wpmdb_media_diff_batch_time', 10 );
$this->media_diff_batch_limit = apply_filters( 'wpmdb_media_diff_batch_limit', 300 );
$this->media_files_batch_time_limit = apply_filters( 'wpmdb_media_files_batch_time_limit', 15 );
$this->accepted_fields = array(
'media_files',
'remove_local_media',
'media_migration_option',
'mf_select_subsites',
'mf_selected_subsites',
);
add_filter( 'wpmdb_accepted_profile_fields', array( $this, 'accepted_profile_fields' ) );
add_filter( 'wpmdbmf_include_subsite', array( $this, 'include_subsite' ), 10, 2 );
}
/**
* Whitelist media setting fields for use in AJAX save in core
*
* @param array $profile_fields Array of profile fields
*
* @return array Updated array of profile fields
*/
function accepted_profile_fields( $profile_fields ) {
return array_merge( $profile_fields, $this->accepted_fields );
}
/**
* Return total number of local attachments
*
* For multisite returns the total number of attachments for all blogs
*
* @return int Total number of local attachments
*/
function get_local_attachments_count() {
global $wpdb;
$count = 0;
if ( is_multisite() ) {
$blogs = $this->get_blog_ids();
foreach ( $blogs as $blog ) {
$blog_prefix = $wpdb->get_blog_prefix( $blog );
$count += $this->get_attachments_count( $blog_prefix );
}
} else {
$count += $this->get_attachments_count( $wpdb->base_prefix );
}
return $count;
}
/**
* Retrieve the count of attachments for a blog
*
* @param string $prefix Blog db prefix
*
* @return int Number of attachments
*/
function get_attachments_count( $prefix ) {
return $this->get_attachment_results( $prefix, 'count' );
}
/**
* Get all attachments for a blog
*
* @param string $prefix Blog db prefix
* @param int $blog Blog ID
* @param int $limit Limit passed to SQL query (for batching)
* @param array $offset Offset (blog ID, post ID) passed to SQL query (for batching)
*
* @return array Attachments
*/
function get_attachments( $prefix, $blog, $limit, $offset ) {
return $this->get_attachment_results( $prefix, 'rows', array( $blog, $offset, $limit ) );
}
/**
* Utility function for retrieving attachments
*
* @param string $prefix Blog db prefix
* @param string $result_type Type of result we want to retrieve (count / rows / row)
* @param array $args Dependant of $result_type.
* 'count' - none
* 'rows' - $blog_id, $offset, $limit
* 'row' - $blog_id, $filename
*
* @return array Attachments
*/
function get_attachment_results( $prefix, $result_type = 'rows', $args = array() ) {
global $wpdb;
$core = " 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' ";
if ( 'count' == $result_type ) {
$sql = 'SELECT COUNT(*)' . $core;
return $wpdb->get_var( $sql );
}
$select = "SELECT `{$prefix}posts`.`ID` AS 'ID', `{$prefix}posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata', %d AS 'blog_id'";
$sql = $select . $core;
if ( 'rows' == $result_type ) {
$action = 'get_results';
$sql .= "AND `{$prefix}posts`.`ID` > %d
ORDER BY `{$prefix}posts`.`ID`
LIMIT %d";
} else {
$action = 'get_row';
$sql .= 'AND pm1.`meta_value` = %s';
}
$sql = $wpdb->prepare( $sql, $args );
$results = $wpdb->$action( $sql, ARRAY_A );
return $results;
}
/**
* Return a batch of attachments across all blogs
*
* @param mixed $blogs Blogs
* @param int $limit Max attachments limit (for batching)
* @param array $offset Optional offset (blog ID, post ID) to use instead of $blog['last_post']
*
* @return array Local attachments and blogs
*/
function get_local_attachments_batch( $blogs, $limit, $offset = null ) {
$all_limit = $limit;
if ( ! is_array( $blogs ) ) {
$blogs = unserialize( stripslashes( $blogs ) );
}
$all_attachments = array();
$all_count = 0;
foreach ( $blogs as $blog_id => $blog ) {
if ( 1 == $blog['processed'] ) {
continue;
}
$blog_offset = $blog['last_post'];
if ( is_array( $offset ) ) {
if ( $offset[0] > $blog_id ) {
$blogs[ $blog_id ]['processed'] = 1;
continue;
} elseif ( $blog_id == $offset[0] ) {
$blog_offset = $offset[1];
}
}
$attachments = $this->get_attachments( $blog['prefix'], $blog_id, $limit, $blog_offset );
$count = count( $attachments );
if ( 0 == $count ) {
// no more attachments, record the blog ID to skip next time
$blogs[ $blog_id ]['processed'] = 1;
} else {
$all_count += $count;
// process attachments for sizes files
$attachments = array_map( array( $this, 'process_attachment_data' ), $attachments );
$attachments = array_filter( $attachments );
$all_attachments[ $blog_id ] = $attachments;
}
if ( $all_count >= $all_limit ) {
break;
}
$limit = $limit - $count;
}
$return = array(
'attachments' => $all_attachments,
'blogs' => $blogs,
);
return $return;
}
/**
* Return all attachment files across all blogs
*
* @param array $offset (blog ID, attachment ID) offset
*
* @return array Local media attachment files and last attachment ID
*/
function get_local_media_attachment_files_batch( $offset ) {
$local_media_attachment_files = array();
$last_attachment_id = 0;
$blogs = $this->get_blogs();
$local_media_attachments = $this->get_local_attachments_batch( $blogs, $this->media_diff_batch_limit, $offset );
$last_blog_id = 1;
// Get file paths from attachments
foreach ( $local_media_attachments['attachments'] as $blog_id => $attachments ) {
foreach ( $attachments as $attachment ) {
if ( ! empty( $attachment['file_size'] ) ) {
$local_media_attachment_files[] = $attachment['file'];
$last_blog_id = $blog_id;
$last_attachment_id = $attachment['ID'];
}
if ( isset( $attachment['sizes'] ) && ! empty( $attachment['sizes'] ) ) {
foreach ( $attachment['sizes'] as $size ) {
if ( ! empty( $size['file_size'] ) ) {
$local_media_attachment_files[] = $size['file'];
}
}
}
}
}
return array(
'files' => $local_media_attachment_files,
'last_blog_id' => $last_blog_id,
'last_attachment_id' => $last_attachment_id,
);
}
/**
* Return a batch of local media files
*
* Scans the uploads directory for physical files
*
* @param string $start_file The file or directory to start at
*
* @return array Local media files
*/
function get_local_media_files_batch( $start_file ) {
$local_media_files = array();
$upload_dir = $this->uploads_dir();
if ( ! $this->filesystem->file_exists( $upload_dir ) ) {
return $local_media_files;
}
// Check if we're just kicking off with the root uploads dir
if ( empty( $start_file ) ) {
$this->get_local_media_files_batch_recursive( '', '', $local_media_files );
} else {
$dir = dirname( $start_file );
$start_filename = basename( $start_file );
$this->get_local_media_files_batch_recursive( trailingslashit( $dir ), $start_filename, $local_media_files );
$dirs = explode( '/', $dir );
while ( $dirs ) {
$start_filename = array_pop( $dirs );
$dir = trailingslashit( implode( '/', $dirs ) );
$this->get_local_media_files_batch_recursive( $dir, $start_filename, $local_media_files );
}
}
return $local_media_files;
}
/**
* Recursively go through uploads directories and get a batch of media files.
* Stops when it has scanned all files/directories or after it has run for
* $this->media_files_batch_time_limit seconds, whichever comes first.
*
* @param string $dir The directory to start in
* @param string $start_filename The file or directory to start at within $dir
* @param array $local_media_files Array to populate with media files found
*/
function get_local_media_files_batch_recursive( $dir, $start_filename, &$local_media_files ) {
$upload_dir = $this->uploads_dir();
static $allowed_mime_types;
if ( is_null( $allowed_mime_types ) ) {
$allowed_mime_types = array_flip( get_allowed_mime_types() );
}
static $finish_time;
if ( is_null( $finish_time ) ) {
$finish_time = microtime( true ) + $this->media_files_batch_time_limit;
}
$dir = ( '/' == $dir ) ? '' : $dir;
$dir_path = $upload_dir . $dir;
$sub_paths = glob( $dir_path . '*', GLOB_MARK );
// Get all the files except the one we use to store backups.
$wpmdb_upload_folder = $this->get_upload_info();
$pattern = '/' . preg_quote( $wpmdb_upload_folder, '/' ) . '/';
$files = preg_grep( $pattern, $sub_paths ? $sub_paths : array(), PREG_GREP_INVERT );
$reached_start_file = false;
foreach ( $files as $file_path ) {
if ( microtime( true ) >= $finish_time ) {
break;
}
// Are we starting from a certain file within the directory?
// If so, we skip all the files that come before it.
if ( $start_filename ) {
if ( basename( $file_path ) == $start_filename ) {
$reached_start_file = true;
continue;
} elseif ( ! $reached_start_file ) {
continue;
}
}
$short_file_path = str_replace( array( $upload_dir, '\\' ), array( '', '/' ), $file_path );
// Is directory? We use this instead of is_dir() to save us an I/O call
if ( substr( $file_path, -1 ) == DIRECTORY_SEPARATOR ) {
$this->get_local_media_files_batch_recursive( $short_file_path, '', $local_media_files );
continue;
}
// ignore files that we shouldn't touch, e.g. .php, .sql, etc
$filetype = wp_check_filetype( $short_file_path );
if ( ! isset( $allowed_mime_types[ $filetype['type'] ] ) ) {
continue;
}
if ( apply_filters( 'wpmdbmf_exclude_local_media_file_from_removal', false, $upload_dir, $short_file_path, $this ) ) {
continue;
}
$local_media_files[] = $short_file_path;
}
}
/**
* Queues attachment file and image size files for migration if they exist on the source filesystem
*
* @param array $files_to_migrate List of files to migrate
* @param array $attachment Attachment data
* @param bool $local_attachment Used to compare if files actually exist locally
*/
function maybe_queue_attachment( &$files_to_migrate, $attachment, $local_attachment = false ) {
if ( isset( $attachment['file_size'] ) && ( ! $local_attachment || ( $local_attachment && ! isset( $local_attachment['file_size'] ) ) ) ) {
// if the remote attachment exists on the remote file system
// and if a local attachment is supplied, if the file doesn't exist on local file system
$files_to_migrate[ $attachment['file'] ] = $attachment['file_size'];
}
// check other image sizes of the attachment
if ( empty( $attachment['sizes'] ) || apply_filters( 'wpmdb_exclude_resized_media', false ) ) {
return;
}
foreach ( $attachment['sizes'] as $size ) {
$original_file_name = $size['file'];
// if dir_prefix is set, then the remote is a multisite and we need to compare the file without the subsite directory prefix
if ( ! is_multisite() && $attachment['dir_prefix'] ) {
$size['file'] = str_replace( $attachment['dir_prefix'], '', $size['file'] );
}
if ( isset( $size['file_size'] ) && ( ! $local_attachment || ( $local_attachment && ! $this->local_image_size_file_exists( $size, $local_attachment ) ) ) ) {
// if the remote image size file exists on the remote file system
$files_to_migrate[ $original_file_name ] = $size['file_size'];
}
}
}
/**
* Compare a batch of remote attachments with those on local site
*
* @param mixed $blogs Blogs
* @param mixed $all_attachments Batch of attachments
* @param int $progress Progress count
*
* @return array Data to return to AJAX response
*/
function compare_remote_attachments( $blogs, $all_attachments, $progress ) {
if ( ! is_array( $blogs ) ) {
$blogs = unserialize( stripslashes( $blogs ) );
}
if ( ! is_array( $all_attachments ) ) {
$all_attachments = unserialize( stripslashes( $all_attachments ) );
}
$files_to_migrate = array();
$finish = time() + $this->media_diff_batch_time;
foreach ( $all_attachments as $blog_id => $attachments ) {
foreach ( $attachments as $remote_attachment ) {
if ( time() >= $finish ) {
break;
}
// find local attachment
$local_attachment = $this->find_attachment( $remote_attachment );
if ( false === $local_attachment ) {
// local attachment doesn't exist, definitely migrate remote
$this->maybe_queue_attachment( $files_to_migrate, $remote_attachment );
} else {
// local attachment already exists
// check the timestamps on the attachment
$remote_timestamp = strtotime( $remote_attachment['date'] );
$local_timestamp = strtotime( $local_attachment['date'] );
if ( $remote_timestamp != $local_timestamp ) {
// timestamps are different, let's migrate remote
$this->maybe_queue_attachment( $files_to_migrate, $remote_attachment );
} else {
// only migrate if the local files are missing
$this->maybe_queue_attachment( $files_to_migrate, $remote_attachment, $local_attachment );
}
}
$blogs[ $blog_id ]['last_post'] = $remote_attachment['ID'];
$progress++;
}
}
$return = array(
'files_to_migrate' => $files_to_migrate,
'blogs' => $blogs,
'determine_progress' => $progress,
);
return $return;
}
/**
* Find an attachment in a specific blog
*
* @param array $attachment
*
* @return array|bool Attachment, false if not found
*/
function find_attachment( $attachment ) {
global $wpdb;
$prefix = $wpdb->base_prefix;
if ( is_multisite() ) {
$blog_ids = $this->get_blog_ids();
// check the blog exists
if ( ! in_array( $attachment['blog_id'], $blog_ids ) ) {
return false;
}
$prefix = $wpdb->get_blog_prefix( $attachment['blog_id'] );
}
$filename = $attachment['file'];
$dir_prefix = ( isset( $attachment['dir_prefix'] ) && strlen( $attachment['dir_prefix'] ) ) ? $attachment['dir_prefix'] : $this->get_dir_prefix( $attachment );
// file names are stored in DB without dir prefix, so if the file has one then we need to remove it
$filename = str_replace( $dir_prefix, '', $filename );
$local_attachment = $this->get_attachment_results( $prefix, 'row', array( $attachment['blog_id'], $filename ) );
if ( empty( $local_attachment ) ) {
return false;
}
$local_attachment = $this->process_attachment_data( $local_attachment );
return $local_attachment;
}
/**
* Process an attachment
*
* Adds the physical file size to an attachment including file sizes for all resized images
*
* @param array $attachment The attachment to process
*
* @return array The updated attachment
*/
function process_attachment_data( $attachment ) {
// prepend site directory prefix for multisite blogs
$attachment['dir_prefix'] = $this->get_dir_prefix( $attachment );
if ( is_multisite() && $attachment['dir_prefix'] ) {
$attachment['file'] = $attachment['dir_prefix'] . $attachment['file'];
}
// use the correct directory for image size files
$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;
}
$size_data = array( 'file' => $upload_dir . $size['file'] );
$size_data = $this->apply_file_size( $size_data );
$attachment['sizes'][] = $size_data;
}
}
}
unset( $attachment['metadata'] );
// get size of image on disk
$attachment = $this->apply_file_size( $attachment );
return $attachment;
}
/**
* Remove local files from the uploads directory
*
* @param mixed $local_files Files to remove
*
* @return array $errors Array of errors
*/
function remove_local_media_files( $local_files ) {
if ( ! is_array( $local_files ) ) {
$local_files = @unserialize( $local_files );
}
$errors = array();
if ( empty( $local_files ) ) {
return $errors;
}
$upload_dir = $this->uploads_dir();
foreach ( $local_files as $local_file ) {
if ( false === $this->filesystem->unlink( $upload_dir . $local_file ) && $this->filesystem->file_exists( $upload_dir . $local_file ) ) {
$errors[] = sprintf( __( 'Could not delete "%s"', 'wp-migrate-db-pro-media-files' ), $upload_dir . $local_file ) . ' (#122mf)';
}
}
return $errors;
}
/**
* Compare a set of files with those on the local filesystem
*
* @param mixed $files Files to compare
* @param string $intent
*
* @return array $files_to_remove Files that do not exist locally
*/
function get_files_not_on_local( $files, $intent ) {
if ( ! is_array( $files ) ) {
$files = @unserialize( $files );
}
$upload_dir = $this->uploads_dir();
$files_to_remove = array();
foreach ( $files as $file ) {
if ( ! $this->filesystem->file_exists( $upload_dir . apply_filters( 'wpmdbmf_file_not_on_local', $file, $intent, $this ) ) ) {
$files_to_remove[] = $file;
}
}
return $files_to_remove;
}
/**
* Check if a remote image size file exists locally
*
* @param array $remote_size Remote attachment size
* @param array $local_attachment Local attachment
*
* @return bool
*/
function local_image_size_file_exists( $remote_size, $local_attachment ) {
if ( empty( $local_attachment['sizes'] ) ) {
return false;
}
foreach ( $local_attachment['sizes'] as $size ) {
if ( $size['file'] == $remote_size['file'] ) {
if ( isset( $size['file_size'] ) ) {
return true;
}
}
}
return false;
}
/**
* Store the physical file size in an attachment
*
* Used for attachments files and resized images files
*
* @param array $attachment Attachment
*
* @return array Updated attachment
*/
function apply_file_size( $attachment ) {
if ( ! isset( $attachment['file'] ) ) {
return $attachment;
}
// get size of image on disk
$size = $this->get_file_size( $attachment['file'], ( isset( $attachment['dir_prefix'] ) ? $attachment['dir_prefix'] : '' ) );
if ( false !== $size ) {
$attachment['file_size'] = $size;
}
return $attachment;
}
/**
* Calculate size on disk of a file
*
* @param string $file File path
* @param string $dir_prefix Multisite blog specific directory
*
* @return int|bool File size if exists, false otherwise
*/
function get_file_size( $file, $dir_prefix = '' ) {
$upload_dir = untrailingslashit( $this->uploads_dir() );
if ( ! $this->filesystem->file_exists( $upload_dir ) ) {
return false;
}
if ( ! is_multisite() && $dir_prefix ) {
$file = str_replace( $dir_prefix, '', $file );
}
$file = $upload_dir . DIRECTORY_SEPARATOR . $file;
if ( ! $this->filesystem->file_exists( $file ) ) {
return false;
}
return $this->filesystem->filesize( $file );
}
/**
* Get the directory prefix for an attachment in a multisite blog
*
* @param array $attachment Attachment
*
* @return string Directory prefix
*/
function get_dir_prefix( $attachment ) {
$dir_prefix = ''; // nothing for default blogs
if ( isset( $attachment['blog_id'] ) && ! $this->is_current_blog( $attachment['blog_id'] ) ) {
if ( defined( 'UPLOADBLOGSDIR' ) ) {
$dir_prefix = sprintf( '%s/files/', $attachment['blog_id'] );
} else {
$dir_prefix = sprintf( 'sites/%s/', $attachment['blog_id'] );
}
}
return $dir_prefix;
}
/**
* Return the base uploads directory
*
* @return string Path to uploads directory
*/
function uploads_dir() {
static $upload_dir;
if ( ! is_null( $upload_dir ) ) {
return $upload_dir;
}
if ( defined( 'UPLOADBLOGSDIR' ) ) {
$upload_dir = trailingslashit( ABSPATH ) . UPLOADBLOGSDIR;
} else {
$upload_dir = wp_upload_dir();
$upload_dir = $upload_dir['basedir'];
}
if ( is_multisite() ) {
// Remove multisite postfix
$upload_dir = untrailingslashit( $upload_dir );
$upload_dir = preg_replace( '/\/sites\/(\d)+$/', '', $upload_dir );
}
$upload_dir = trailingslashit( $upload_dir );
return $upload_dir;
}
/**
* Get an array of the blogs in the site to be processed by the addon
*
* @return array Blogs to be processed
*/
function get_blogs() {
global $wpdb;
$blogs = array();
if ( is_multisite() ) {
$blog_ids = $this->get_blog_ids();
foreach ( $blog_ids as $blog_id ) {
$blogs[ $blog_id ] = array(
'prefix' => $wpdb->get_blog_prefix( $blog_id ),
'last_post' => 0,
'processed' => 0,
);
}
} else {
$blogs[1] = array(
'prefix' => $wpdb->base_prefix,
'last_post' => 0, // record last post id process to be used as an offset in the next batch for the blog
'processed' => 0, // flag to record if we have processed all attachments for the blog
);
}
return $blogs;
}
/**
* Get all the IDs of the blogs for the multisite
*
* @return array Blog ID's
*/
function get_blog_ids() {
$blog_ids = array();
if ( ! is_multisite() ) {
return $blog_ids;
}
$args = array(
'spam' => 0,
'deleted' => 0,
'archived' => 0,
'number' => false
);
if ( version_compare( $GLOBALS['wp_version'], '4.6', '>=' ) ) {
$blogs = get_sites( $args );
} else {
$blogs = wp_get_sites( $args );
}
foreach ( $blogs as $blog ) {
$blog = (array) $blog;
if ( apply_filters( 'wpmdbmf_include_subsite', true, $blog['blog_id'], $this ) ) {
$blog_ids[] = $blog['blog_id'];
}
}
return $blog_ids;
}
/**
* Compares the blog ID with the current site specified in wp-config.php
*
* @param int $blog_id Blog ID
*
* @return bool
*/
function is_current_blog( $blog_id ) {
$default = defined( 'BLOG_ID_CURRENT_SITE' ) ? BLOG_ID_CURRENT_SITE : 1;
if ( $default === (int) $blog_id ) {
return true;
}
return false;
}
/**
* Handler for "wpmdbmf_include_subsite" filter to disallow subsite's media to be migrated if not selected.
*
* @param bool $value
* @param int $blog_id
*
* @return bool
*/
public function include_subsite( $value, $blog_id ) {
$this->set_post_data();
if ( is_null( $this->form_data ) && ! empty( $this->state_data['form_data'] ) ) {
$this->form_data = $this->parse_migration_form_data( $this->state_data['form_data'] );
}
if ( false === $value || empty( $this->form_data['mf_select_subsites'] ) || empty( $this->form_data['mf_selected_subsites'] ) ) {
return $value;
}
if ( ! in_array( $blog_id, $this->form_data['mf_selected_subsites'] ) ) {
$value = false;
}
return $value;
}
/**
* Returns validated and sanitized form data.
*
* @param array|string $data
*
* @return array|string
*/
function parse_migration_form_data( $data ) {
$form_data = parent::parse_migration_form_data( $data );
$form_data = array_intersect_key( $form_data, array_flip( $this->accepted_fields ) );
return $form_data;
}
}

View File

@@ -0,0 +1,619 @@
<?php
/**
* Class WPMDBPro_Media_Files_Local
*
* Handles all functionality and AJAX requests that are only required on the "local" site.
*/
class WPMDBPro_Media_Files_Local extends WPMDBPro_Media_Files_Base {
public function __construct( $plugin_file_path ) {
parent::__construct( $plugin_file_path );
// Local AJAX handlers
add_action( 'wp_ajax_wpmdbmf_prepare_determine_media', array( $this, 'ajax_prepare_determine_media' ) );
add_action( 'wp_ajax_wpmdbmf_determine_media_to_migrate_recursive', array( $this, 'ajax_determine_media_to_migrate_recursive', ) );
add_action( 'wp_ajax_wpmdbmf_migrate_media', array( $this, 'ajax_migrate_media' ) );
add_action( 'wp_ajax_wpmdbmf_remove_files_recursive', array( $this, 'ajax_remove_files_recursive' ) );
}
/**
* AJAX initial request before determining media to migrate
*
* @see WPMDBPro_Media_Files_Remote::respond_to_get_remote_media_info
*
* @return bool|null
*/
public function ajax_prepare_determine_media() {
$this->check_ajax_referer( 'prepare-determine-media' );
$this->set_time_limit();
$key_rules = array(
'action' => 'key',
'migration_state_id' => 'key',
'nonce' => 'key',
);
$this->set_post_data( $key_rules );
$data = array();
$data['action'] = 'wpmdbmf_get_remote_media_info';
$data['remote_state_id'] = $this->state_data['remote_state_id'];
$data['intent'] = $this->state_data['intent'];
$data['sig'] = $this->create_signature( $data, $this->state_data['key'] );
$ajax_url = trailingslashit( $this->state_data['url'] ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
$response = $this->verify_remote_post_response( $response );
if ( isset( $response['wpmdb_error'] ) ) {
return $response;
}
$return['attachment_batch_limit'] = $this->media_diff_batch_limit;
$return['remote_uploads_url'] = $response['remote_uploads_url'];
$return['remote_max_upload_size'] = $response['remote_max_upload_size'];
// determine the size of the attachments in scope for migration
if ( 'pull' == $this->state_data['intent'] ) {
$return['attachment_count'] = $response['remote_total_attachments'];
$return['blogs'] = $response['blogs'];
} else {
$return['attachment_count'] = $this->get_local_attachments_count();
$return['blogs'] = serialize( $this->get_blogs() );
}
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
/**
* Callback used by the recursive AJAX request to determine media to migrate
*
* @see WPMDBPro_Media_Files_Remote::respond_to_get_remote_attachment_batch
* @see WPMDBPro_Media_Files_Remote::respond_to_compare_remote_attachments
*
* @return bool|null
*/
public function ajax_determine_media_to_migrate_recursive() {
$this->check_ajax_referer( 'determine-media-to-migrate-recursive' );
$this->set_time_limit();
$key_rules = array(
'action' => 'key',
'migration_state_id' => 'key',
'determine_progress' => 'positive_int',
'attachment_count' => 'positive_int',
'remote_uploads_url' => 'url',
'remove_local_media' => 'positive_int',
'copy_entire_media' => 'positive_int',
'blogs' => 'serialized',
'attachment_batch_limit' => 'positive_int',
'nonce' => 'key',
);
$this->set_post_data( $key_rules );
if ( ! in_array( $this->state_data['intent'], array( 'pull', 'push' ) ) ) {
$error_msg = __( 'Incorrect migration type supplied', 'wp-migrate-db-pro-media-files' ) . ' (#120mf)';
$return = array( 'wpmdb_error' => 1, 'body' => $error_msg );
$this->log_error( $error_msg );
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
// get batch of attachments and check if they need migrating
if ( 'pull' == $this->state_data['intent'] ) {
// get the remote batch
$data = array();
$data['action'] = 'wpmdbmf_get_remote_attachment_batch';
$data['remote_state_id'] = $this->state_data['remote_state_id'];
$data['intent'] = $this->state_data['intent'];
$data['blogs'] = stripslashes( $this->state_data['blogs'] );
$data['attachment_batch_limit'] = $this->state_data['attachment_batch_limit'];
$data['sig'] = $this->create_signature( $data, $this->state_data['key'] );
$ajax_url = trailingslashit( $this->state_data['url'] ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
$response = $this->verify_remote_post_response( $response );
if ( isset( $response['wpmdb_error'] ) ) {
return $response;
}
$response = apply_filters( 'wpmdbmf_get_remote_attachment_batch_response', $response, 'pull', $this );
if ( '1' == $this->state_data['copy_entire_media'] ) {
// skip comparison
$return = $this->queue_all_attachments( $response['blogs'], $response['remote_attachments'], $this->state_data['determine_progress'] );
} else {
// compare batch against local attachments
$return = $this->compare_remote_attachments( $response['blogs'], $response['remote_attachments'], $this->state_data['determine_progress'] );
}
} else {
// get the local batch
$batch = $this->get_local_attachments_batch( $this->state_data['blogs'], $this->state_data['attachment_batch_limit'] );
if ( '1' == $this->state_data['copy_entire_media'] ) {
// skip comparison
$return = $this->queue_all_attachments( $batch['blogs'], $batch['attachments'], $this->state_data['determine_progress'] );
} else {
// send batch to remote to compare against remote attachments
$data = array();
$data['action'] = 'wpmdbmf_compare_remote_attachments';
$data['remote_state_id'] = $this->state_data['remote_state_id'];
$data['intent'] = $this->state_data['intent'];
$data['blogs'] = serialize( $batch['blogs'] );
$data['determine_progress'] = $this->state_data['determine_progress'];
$data['remote_attachments'] = serialize( $batch['attachments'] );
$data['sig'] = $this->create_signature( $data, $this->state_data['key'] );
$data['remote_attachments'] = addslashes( $data['remote_attachments'] ); // will be unslashed before sig is checked
$ajax_url = trailingslashit( $this->state_data['url'] ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
$return = $this->verify_remote_post_response( $response );
if ( isset( $return['wpmdb_error'] ) ) {
return $return;
}
}
}
// persist settings across requests
$return['copy_entire_media'] = $this->state_data['copy_entire_media'];
$return['remove_local_media'] = $this->state_data['remove_local_media'];
$return['remote_uploads_url'] = $this->state_data['remote_uploads_url'];
$return['attachment_count'] = $this->state_data['attachment_count'];
$return['determine_progress'] = isset( $return['determine_progress'] ) ? $return['determine_progress'] : 0;
$return['blogs'] = serialize( $return['blogs'] );
$return['total_size'] = array_sum( $return['files_to_migrate'] );
$return['files_to_migrate'] = isset( $return['files_to_migrate'] ) ? $return['files_to_migrate'] : array();
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
/**
* AJAX wrapper for the push/pull migration of media files,
*
* @return bool|null
*/
public function ajax_migrate_media() {
$this->check_ajax_referer( 'migrate-media' );
$this->set_time_limit();
$key_rules = array(
'action' => 'key',
'migration_state_id' => 'key',
'file_chunk' => 'array',
'nonce' => 'key',
);
$this->set_post_data( $key_rules );
if ( 'pull' == $this->state_data['intent'] ) {
$result = $this->process_pull_request();
} else {
$result = $this->process_push_request();
}
return $result;
}
/**
* Download files from the remote site
*
* @return bool|null
*/
function process_pull_request() {
$files_to_download = $this->state_data['file_chunk'];
$remote_uploads_url = trailingslashit( $this->state_data['remote_uploads_url'] );
$parsed = $this->parse_url( $this->state_data['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();
$transfers = array();
foreach ( $files_to_download as $file_to_download ) {
$current_transfer = array( 'file' => $file_to_download, 'error' => false );
$remote_url = $remote_uploads_url . apply_filters( 'wpmdbmf_file_to_download', $file_to_download, 'pull', $this );
$temp_file_path = $this->download_url( $remote_url );
if ( is_wp_error( $temp_file_path ) ) {
$download_error = $temp_file_path->get_error_message();
$current_transfer['error'] = $download_error;
$errors[] = sprintf( __( 'Could not download file: %1$s - %2$s', 'wp-migrate-db-pro-media-files' ), $remote_url, $download_error );
$transfers[] = $current_transfer;
continue;
}
$date = str_replace( basename( $file_to_download ), '', $file_to_download );
$new_path = $upload_dir . $date . basename( $file_to_download );
$folder = dirname( $new_path );
// WPMDB_Filesystem::mkdir will return true straight away if the dir exists
if ( false === $this->filesystem->mkdir( $folder ) ) {
$error_string = sprintf( __( 'Error attempting to create required directory: %s', 'wp-migrate-db-pro-media-files' ), $folder ) . ' (#104mf)';
$errors[] = $error_string;
$current_transfer['error'] = $error_string;
} elseif ( false === $this->filesystem->move( $temp_file_path, $new_path ) ) {
$error_string = sprintf( __( 'Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', 'wp-migrate-db-pro-media-files' ), $temp_file_path, $new_path ) . ' (#105mf)';
$errors[] = $error_string;
$current_transfer['error'] = $error_string;
}
$transfers[] = $current_transfer;
// set default permissions on moved file
$this->filesystem->chmod( $new_path );
}
$return = array( 'success' => 1, 'transfers' => $transfers );
if ( ! empty( $errors ) ) {
$return['wpmdb_non_fatal_error'] = 1;
$return['cli_body'] = $errors;
$return['body'] = implode( '<br />', $errors ) . '<br />';
$error_msg = __( 'Failed attempting to process pull request', 'wp-migrate-db-pro-media-files' ) . ' (#112mf)';
$this->log_error( $error_msg, $errors );
}
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
/**
* Upload files to the remote site
*
* @see WPMDBPro_Media_Files_Remote::respond_to_push_request
*
* @return bool|null
*/
function process_push_request() {
$files_to_migrate = $this->state_data['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' => 'wpmdbmf_push_request',
'remote_state_id' => $this->state_data['remote_state_id'],
'files' => serialize( $files_to_migrate ),
);
$post_args['sig'] = $this->create_signature( $post_args, $this->state_data['key'] );
$body .= $this->array_to_multipart( $post_args );
$args['body'] = $body;
$ajax_url = trailingslashit( $this->state_data['url'] ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, '', __FUNCTION__, $args );
$response = $this->verify_remote_post_response( $response );
if ( isset( $response['wpmdb_error'] ) ) {
return $response;
}
$result = $this->end_ajax( json_encode( $response ) );
return $result;
}
/**
* Queue up all attachments in batch to be migrated
*
* @param mixed $blogs Blogs
* @param mixed $all_attachments Batch of attachments
* @param int $progress Progress count
*
* @return array Data to return to AJAX response
*/
function queue_all_attachments( $blogs, $all_attachments, $progress ) {
if ( ! is_array( $blogs ) ) {
$blogs = unserialize( stripslashes( $blogs ) );
}
if ( ! is_array( $all_attachments ) ) {
$all_attachments = unserialize( stripslashes( $all_attachments ) );
}
$files_to_migrate = array();
$finish = time() + $this->media_diff_batch_time;
foreach ( $all_attachments as $blog_id => $attachments ) {
foreach ( $attachments as $remote_attachment ) {
if ( time() >= $finish ) {
break;
}
$this->maybe_queue_attachment( $files_to_migrate, $remote_attachment );
$blogs[ $blog_id ]['last_post'] = $remote_attachment['ID'];
$progress++;
}
}
$return = array(
'files_to_migrate' => $files_to_migrate,
'blogs' => $blogs,
'determine_progress' => $progress,
);
return $return;
}
/**
* AJAX recursive request to remove all media files if skipping comparison in batches
*
* @return bool|null
*/
public function ajax_remove_files_recursive() {
$this->check_ajax_referer( 'remove-files-recursive' );
$this->set_time_limit();
$key_rules = array(
'action' => 'key',
'migration_state_id' => 'key',
'compare' => 'positive_int',
'offset' => 'string',
'nonce' => 'key',
);
$this->set_post_data( $key_rules );
$this->state_data['offset'] = (array) json_decode( $this->state_data['offset'] );
if ( 'pull' == $this->state_data['intent'] ) {
// send batch of files to be compared on the remote
// receive batch of files to be deleted
$return = $this->remove_local_files_recursive( $this->state_data['url'], $this->state_data['key'], $this->state_data['compare'], $this->state_data['offset'] );
} else {
// request a batch from the remote
// compare received batch of files with local filesystem
// send files to be deleted to the remote for deletion
$return = $this->remove_remote_files_recursive( $this->state_data['url'], $this->state_data['key'], $this->state_data['compare'], $this->state_data['offset'] );
}
// persist the comparison flag across recursive requests
$return['compare'] = $this->state_data['compare'];
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
/**
* Remove local media files in batches that can be called recursively
*
* Used in pull requests
*
* @see WPMDBPro_Media_Files_Remote::respond_to_compare_local_media_files
*
* @param string $remote_url The remote site URL
* @param string $remote_key The remote site key
* @param int $compare_with_remote 1 = Will compare files existence on remote, 0 = no comparison
* @param array $offset Offset (blog id, post id) of last file in batch
*
* @return array
*/
function remove_local_files_recursive( $remote_url, $remote_key, $compare_with_remote, $offset = null ) {
if ( 1 === ( int ) $compare_with_remote ) {
$local_media_files = $this->get_local_media_attachment_files_batch( $offset );
if ( empty( $local_media_files['files'] ) ) {
return array( 'offset' => '', 'remove_files' => 0 );
}
// send batch of files to be compared on the remote
$data = array();
$data['action'] = 'wpmdbmf_compare_local_media_files';
$data['remote_state_id'] = $this->state_data['remote_state_id'];
$data['files'] = serialize( $local_media_files['files'] );
$data['sig'] = $this->create_signature( $data, $remote_key );
$data['files'] = addslashes( $data['files'] ); // will be unslashed before sig is checked
$ajax_url = trailingslashit( $remote_url ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
$response = $this->verify_remote_post_response( $response );
if ( isset( $response['wpmdb_error'] ) ) {
return $response;
}
// files that don't exist returned as new batch to delete
$files_to_remove = isset( $response['files_to_remove'] ) ? $response['files_to_remove'] : array();
$return_offset = array( $local_media_files['last_blog_id'], $local_media_files['last_attachment_id'] );
} else {
$local_media_files = $this->get_local_media_files_batch( array_pop( $offset ) );
if ( ! $local_media_files ) {
return array( 'offset' => '', 'remove_files' => 0 );
}
$files_to_remove = $local_media_files;
$return_offset = end( $local_media_files );
}
$errors = $this->remove_local_media_files( $files_to_remove );
$return = array(
'offset' => $return_offset,
'remove_files' => 1,
);
if ( ! empty( $errors ) ) {
$return['wpmdb_non_fatal_error'] = 1;
$return['cli_body'] = $errors;
$return['body'] = implode( '<br />', $errors ) . '<br />';
$error_msg = __( 'There were errors when removing local media files', 'wp-migrate-db-pro-media-files' ) . ' (#123mf)';
$this->log_error( $error_msg, $errors );
}
return $return;
}
/**
* Remove remote media files in batches that can be called recursively
*
* Used in push requests
*
* @see WPMDBPro_Media_Files_Remote::respond_to_get_local_media_files_batch
* @see WPMDBPro_Media_Files_Remote::respond_to_remove_local_media_files
*
* @param string $remote_url The remote site URL
* @param string $remote_key The remote site key
* @param int $compare_with_remote 1 = Will compare files existence on remote, 0 = no comparison
* @param array $offset Offset (blog_id, post_id) of last file in previous batch to start this batch from
*
* @return array
*/
function remove_remote_files_recursive( $remote_url, $remote_key, $compare_with_remote, $offset = null ) {
// request a batch from the remote
$data = array();
$data['action'] = 'wpmdbmf_get_local_media_files_batch';
$data['remote_state_id'] = $this->state_data['remote_state_id'];
$data['compare'] = $compare_with_remote;
$data['offset'] = json_encode( $offset );
$data['sig'] = $this->create_signature( $data, $remote_key );
$ajax_url = trailingslashit( $remote_url ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
$response = $this->verify_remote_post_response( $response );
if ( isset( $response['wpmdb_error'] ) ) {
return $response;
}
if ( 1 === ( int ) $compare_with_remote ) {
$remote_media_files = $response['local_media_attachment_files'];
if ( empty( $remote_media_files['files'] ) ) {
return array( 'offset' => '', 'remove_files' => 0 );
}
// compare received batch of files with local filesystem
$files_to_remove = $this->get_files_not_on_local( $remote_media_files['files'], 'push' );
$return_offset = array( $remote_media_files['last_blog_id'], $remote_media_files['last_attachment_id'] );
} else {
$remote_media_files = $response['local_media_files'];
if ( ! $remote_media_files ) {
return array( 'offset' => '', 'remove_files' => 0 );
}
$files_to_remove = $remote_media_files;
$return_offset = end( $remote_media_files );
}
// send files not found on local to the remote for deletion
$data = array();
$data['action'] = 'wpmdbmf_remove_local_media_files';
$data['remote_state_id'] = $this->state_data['remote_state_id'];
$data['files_to_remove'] = serialize( $files_to_remove );
$data['sig'] = $this->create_signature( $data, $remote_key );
$data['files_to_remove'] = addslashes( $data['files_to_remove'] ); // will be unslashed before sig is checked
$ajax_url = trailingslashit( $remote_url ) . 'wp-admin/admin-ajax.php';
$response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
$response = $this->verify_remote_post_response( $response );
if ( isset( $response['wpmdb_error'] ) ) {
return $response;
}
$response['offset'] = $return_offset;
$response['remove_files'] = 1;
return $response;
}
/**
* Verify a remote response is valid
*
* @param mixed $response Response
*
* @return mixed Response if valid, error otherwise
*/
function verify_remote_post_response( $response ) {
if ( false === $response ) {
$return = array( 'wpmdb_error' => 1, 'body' => $this->error );
$error_msg = 'Failed attempting to verify remote post response (#114mf)';
$this->log_error( $error_msg, $this->error );
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
if ( ! is_serialized( trim( $response ) ) ) {
$return = array( 'wpmdb_error' => 1, 'body' => $response );
$error_msg = 'Failed as the response is not serialized string (#115mf)';
$this->log_error( $error_msg, $response );
$result = $this->end_ajax( json_encode( $return ) );
return $result;
}
$response = unserialize( trim( $response ) );
if ( isset( $response['wpmdb_error'] ) ) {
$this->log_error( $response['wpmdb_error'], $response );
$result = $this->end_ajax( json_encode( $response ) );
return $result;
}
return $response;
}
/**
* Download a remote media file
*
* @param string $url File to download
* @param int $timeout Timeout limit
*
* @return array|string|WP_Error
*/
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.' ) );
}
$sslverify = ( 1 == $this->settings['verify_ssl'] ) ? true : false;
$args = array(
'timeout' => $timeout,
'stream' => true,
'filename' => $tmpfname,
'reject_unsafe_urls' => false,
'sslverify' => $sslverify,
);
$response = wp_remote_get( $url, $args );
if ( is_wp_error( $response ) ) {
$this->filesystem->unlink( $tmpfname );
return $response;
}
if ( 200 != wp_remote_retrieve_response_code( $response ) ) {
$this->filesystem->unlink( $tmpfname );
return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );
}
return $tmpfname;
}
}

View File

@@ -0,0 +1,433 @@
<?php
/**
* Class WPMDBPro_Media_Files_Remote
*
* Handles all functionality and AJAX requests that are only required on the "remote" site.
*/
class WPMDBPro_Media_Files_Remote extends WPMDBPro_Media_Files_Base {
public function __construct( $plugin_file_path ) {
parent::__construct( $plugin_file_path );
// Remote AJAX handlers
add_action( 'wp_ajax_nopriv_wpmdbmf_get_remote_media_info', array( $this, 'respond_to_get_remote_media_info' ) );
add_action( 'wp_ajax_nopriv_wpmdbmf_get_remote_attachment_batch', array( $this, 'respond_to_get_remote_attachment_batch' ) );
add_action( 'wp_ajax_nopriv_wpmdbmf_compare_remote_attachments', array( $this, 'respond_to_compare_remote_attachments' ) );
add_action( 'wp_ajax_nopriv_wpmdbmf_push_request', array( $this, 'respond_to_push_request' ) );
add_action( 'wp_ajax_nopriv_wpmdbmf_get_local_media_files_batch', array( $this, 'respond_to_get_local_media_files_batch' ) );
add_action( 'wp_ajax_nopriv_wpmdbmf_compare_local_media_files', array( $this, 'respond_to_compare_local_media_files' ) );
add_action( 'wp_ajax_nopriv_wpmdbmf_remove_local_media_files', array( $this, 'respond_to_remove_local_media_files' ) );
}
/**
* Return information about remote site for use in media migration
*
* @return bool|null
*/
public function respond_to_get_remote_media_info() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'intent' => 'key',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'intent',
) );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#100mf)',
);
$this->log_error( $return['body'], $filtered_post );
$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'];
if ( is_multisite() ) {
// Remove multisite postfix
$upload_url = preg_replace( '/\/sites\/(\d)+$/', '', $upload_url );
}
}
$return['remote_total_attachments'] = $this->get_local_attachments_count();
$return['remote_uploads_url'] = $upload_url;
$return['blogs'] = serialize( $this->get_blogs() );
$return['remote_max_upload_size'] = $this->get_max_upload_size();
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
/**
* Return a batch of attachments from the remote site
*
* @return bool|null
*/
public function respond_to_get_remote_attachment_batch() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'intent' => 'key',
'blogs' => 'serialized',
'attachment_batch_limit' => 'positive_int',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'intent',
'blogs',
'attachment_batch_limit',
) );
$filtered_post['blogs'] = stripslashes( $filtered_post['blogs'] );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#116mf)',
);
$this->log_error( $return['body'], $filtered_post );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
$batch = $this->get_local_attachments_batch( $filtered_post['blogs'], $filtered_post['attachment_batch_limit'] );
$return['remote_attachments'] = addslashes( serialize( $batch['attachments'] ) );
$return['blogs'] = addslashes( serialize( $batch['blogs'] ) );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
/**
* Compare posted local files with those on the remote server
*
* @return bool|null
*/
public function respond_to_compare_remote_attachments() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'intent' => 'key',
'blogs' => 'serialized',
'determine_progress' => 'positive_int',
'remote_attachments' => 'serialized',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'intent',
'blogs',
'determine_progress',
'remote_attachments',
) );
$filtered_post['blogs'] = stripslashes( $filtered_post['blogs'] );
$filtered_post['remote_attachments'] = stripslashes( $filtered_post['remote_attachments'] );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#118mf)',
);
$this->log_error( $return['body'], $filtered_post );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
// compare_remote_attachments will unslash these values again
$filtered_post['blogs'] = addslashes( $filtered_post['blogs'] );
$filtered_post['remote_attachments'] = addslashes( $filtered_post['remote_attachments'] );
$return = $this->compare_remote_attachments( $filtered_post['blogs'], $filtered_post['remote_attachments'], $filtered_post['determine_progress'] );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
/**
* Move uploaded local site files from tmp to uploads directory
*
* @return bool|null
*/
public function respond_to_push_request() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'files' => 'serialized',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'files',
) );
$filtered_post['files'] = stripslashes( $filtered_post['files'] );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#111mf)',
);
$this->log_error( $return['body'], $filtered_post );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
if ( ! isset( $_FILES['media'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => __( '$_FILES is empty, the upload appears to have failed', 'wp-migrate-db-pro-media-files' ) . ' (#106mf)',
);
$this->log_error( $return['body'] );
$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();
$transfers = array();
foreach ( $files as &$file ) {
$destination = $upload_dir . apply_filters( 'wpmdbmf_destination_file_path', $file_paths[ $i ], 'push', $this );
$folder = dirname( $destination );
$current_transfer = array( 'file' => $file_paths[ $i ], 'error' => false );
if ( false === $this->filesystem->file_exists( $folder ) && false === $this->filesystem->mkdir( $folder ) ) {
$error_string = sprintf( __( 'Error attempting to create required directory: %s', 'wp-migrate-db-pro-media-files' ), $folder ) . ' (#108mf)';
$errors[] = $error_string;
$current_transfer['error'] = $error_string;
++$i;
$transfers[] = $current_transfer;
continue;
}
if ( false === $this->filesystem->move_uploaded_file( $file['tmp_name'], $destination ) ) {
$error_string = sprintf( __( 'A problem occurred when attempting to move the temp file "%1$s" to "%2$s"', 'wp-migrate-db-pro-media-files' ), $file['tmp_name'], $destination ) . ' (#107mf)';
$errors[] = $error_string;
$current_transfer['error'] = $error_string;
}
$transfers[] = $current_transfer;
++$i;
}
$return = array( 'success' => 1, 'transfers' => $transfers );
if ( ! empty( $errors ) ) {
$return['wpmdb_non_fatal_error'] = 1;
$return['cli_body'] = $errors;
$return['body'] = implode( '<br />', $errors ) . '<br />';
$error_msg = __( 'Failed attempting to respond to push request', 'wp-migrate-db-pro-media-files' ) . ' (#113mf)';
$this->log_error( $error_msg, $errors );
}
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
/**
* AJAX callback for returning a batch of local media files
*
* @return bool|null
*/
public function respond_to_get_local_media_files_batch() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'compare' => 'positive_int',
'offset' => 'string',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'compare',
'offset',
) );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#109mf)',
);
$this->log_error( $return['body'], $filtered_post );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
$offset = isset( $filtered_post['offset'] ) ? json_decode( $filtered_post['offset'] ) : '0';
$local_media_files = array();
$local_media_attachment_files = array();
if ( 1 === (int) $filtered_post['compare'] ) {
$local_media_attachment_files = $this->get_local_media_attachment_files_batch( $offset );
} else {
$local_media_files = $this->get_local_media_files_batch( array_pop( $offset ) );
}
$return = array(
'success' => 1,
'local_media_files' => $local_media_files,
'local_media_attachment_files' => $local_media_attachment_files,
);
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
/**
* AJAX callback to compare a posted batch of files with those on local site
*
* @return bool|null
*/
public function respond_to_compare_local_media_files() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'files' => 'serialized',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'files',
) );
$filtered_post['files'] = stripslashes( $filtered_post['files'] );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#117mf)',
);
$this->log_error( $return['body'], $filtered_post );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
// compare files to those on the local filesystem
$files_to_remove = $this->get_files_not_on_local( $filtered_post['files'], 'pull' );
$return = array(
'success' => 1,
'files_to_remove' => $files_to_remove,
);
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
/**
* AJAX callback to remove files for the local filesystem
*
* @return bool|null
*/
public function respond_to_remove_local_media_files() {
add_filter( 'wpmdb_before_response', array( $this, 'scramble' ) );
$key_rules = array(
'action' => 'key',
'remote_state_id' => 'key',
'files_to_remove' => 'serialized',
'sig' => 'string',
);
$this->set_post_data( $key_rules, 'remote_state_id' );
$filtered_post = $this->filter_post_elements( $this->state_data, array(
'action',
'remote_state_id',
'files_to_remove',
) );
$filtered_post['files_to_remove'] = stripslashes( $filtered_post['files_to_remove'] );
if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
$return = array(
'wpmdb_error' => 1,
'body' => $this->invalid_content_verification_error . ' (#119mf)',
);
$this->log_error( $return['body'], $filtered_post );
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
$errors = $this->remove_local_media_files( $filtered_post['files_to_remove'] );
$return['success'] = 1;
if ( ! empty( $errors ) ) {
$return['wpmdb_non_fatal_error'] = 1;
$return['cli_body'] = $errors;
$return['body'] = implode( '<br />', $errors ) . '<br />';
$error_msg = __( 'There were errors when removing local media files from the remote site', 'wp-migrate-db-pro-media-files' ) . ' (#121mf)';
$this->log_error( $error_msg, $errors );
}
$result = $this->end_ajax( serialize( $return ) );
return $result;
}
}

View File

@@ -0,0 +1,209 @@
<?php
/**
* Class WPMDBPro_Media_Files
*
* Handles the addon setup and settings
*/
class WPMDBPro_Media_Files extends WPMDBPro_Addon {
/**
* An array strings used for translations
*
* @var array $media_strings
*/
protected $media_strings;
/**
* An instance of WPMDBPro_Media_Files_Local
*
* @var object $media_files_local
*/
public $media_files_local;
/**
* An instance of WPMDBPro_Media_Files_Remote
*
* @var object $media_files_remote
*/
public $media_files_remote;
function __construct( $plugin_file_path ) {
parent::__construct( $plugin_file_path );
$this->plugin_slug = 'wp-migrate-db-pro-media-files';
$this->plugin_version = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro-media-files']['version'];
if ( ! $this->meets_version_requirements( '1.7.1' ) ) {
return;
}
add_action( 'wpmdb_after_advanced_options', array( $this, 'migration_form_controls' ) );
add_action( 'wpmdb_load_assets', array( $this, 'load_assets' ) );
add_action( 'wpmdb_diagnostic_info', array( $this, 'diagnostic_info' ) );
add_action( 'wpmdbmf_after_migration_options', array( $this, 'after_migration_options_template' ) );
add_filter( 'wpmdb_establish_remote_connection_data', array( $this, 'establish_remote_connection_data' ) );
add_filter( 'wpmdb_nonces', array( $this, 'add_nonces' ) );
add_filter( 'wpmdb_data', array( $this, 'js_variables' ) );
$this->media_files_local = new WPMDBPro_Media_Files_Local( $plugin_file_path );
$this->media_files_remote = new WPMDBPro_Media_Files_Remote( $plugin_file_path );
}
/**
* Adds the media settings to the migration setting page in core
*/
function migration_form_controls() {
$this->template( 'migrate' );
}
/**
* Get translated strings for javascript and other functions
*
* @return array Array of translations
*/
function get_strings() {
$strings = array(
'removing_all_files_pull' => __( 'Removing all local files before download of remote media', 'wp-migrate-db-pro-media-files' ),
'removing_all_files_push' => __( 'Removing all remote files before upload of local media', 'wp-migrate-db-pro-media-files' ),
'removing_files_pull' => __( 'Removing local files that are not found on the remote site', 'wp-migrate-db-pro-media-files' ),
'removing_files_push' => __( 'Removing remote files that are not found on the local site', 'wp-migrate-db-pro-media-files' ),
'determining' => __( 'Determining media to migrate', 'wp-migrate-db-pro-media-files' ),
'determining_progress' => __( 'Determining media to migrate - %1$d of %2$d attachments (%3$d%%)', 'wp-migrate-db-pro-media-files' ),
'error_determining' => __( 'Error while attempting to determine which attachments to migrate.', 'wp-migrate-db-pro-media-files' ),
'migration_failed' => __( 'Migration failed', 'wp-migrate-db-pro-media-files' ),
'problem_migrating_media' => __( 'A problem occurred when migrating the media files.', 'wp-migrate-db-pro-media-files' ),
'media_attachments' => __( 'Media Attachments', 'wp-migrate-db-pro-media-files' ),
'media_files' => __( 'Files', 'wp-migrate-db-pro-media-files' ),
'migrate_media_files_pull' => __( 'Downloading files', 'wp-migrate-db-pro-media-files' ),
'migrate_media_files_push' => __( 'Uploading files', 'wp-migrate-db-pro-media-files' ),
'migrate_media_files_cli_pull' => __( 'Downloading %d of %d files', 'wp-migrate-db-pro-media-files' ),
'migrate_media_files_cli_push' => __( 'Uploading %d of %d files', 'wp-migrate-db-pro-media-files' ),
'files_uploaded' => __( 'Files Uploaded', 'wp-migrate-db-pro-media-files' ),
'files_downloaded' => __( 'Files Downloaded', 'wp-migrate-db-pro-media-files' ),
'file_too_large' => __( 'The following file is too large to migrate:', 'wp-migrate-db-pro-media-files' ),
'please_select_a_subsite' => __( 'Please select at least one subsite to transfer media files for.', 'wp-migrate-db-pro-media-files' ),
);
if ( is_null( $this->media_strings ) ) {
$this->media_strings = $strings;
}
return $this->media_strings;
}
/**
* Retrieve a specific translated string
*
* @param string $key Array key
*
* @return string Translation
*/
function get_string( $key ) {
$strings = $this->get_strings();
return ( isset( $strings[ $key ] ) ) ? $strings[ $key ] : '';
}
/**
* Load media related assets in core plugin
*/
function load_assets() {
$plugins_url = trailingslashit( plugins_url( $this->plugin_folder_name ) );
$version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $this->plugin_version;
$ver_string = '-' . str_replace( '.', '', $this->plugin_version );
$min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
$src = $plugins_url . 'asset/dist/css/styles.css';
wp_enqueue_style( 'wp-migrate-db-pro-media-files-styles', $src, array( 'wp-migrate-db-pro-styles' ), $version );
$src = $plugins_url . "asset/dist/js/script{$ver_string}{$min}.js";
wp_enqueue_script( 'wp-migrate-db-pro-media-files-script', $src, array(
'jquery',
'wp-migrate-db-pro-common',
'wp-migrate-db-pro-hook',
'wp-migrate-db-pro-script',
), $version, true );
wp_localize_script( 'wp-migrate-db-pro-media-files-script', 'wpmdbmf_strings', $this->get_strings() );
}
/**
* Check the remote site has the media addon setup
*
* @param array $data Connection data
*
* @return array Updated connection data
*/
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( 'wpmdbmf_max_file_uploads', $max_file_uploads );
return $data;
}
/**
* Add media related javascript variables to the page
*
* @param array $data
*
* @return array
*/
function js_variables( $data ) {
$data['media_files_version'] = $this->plugin_version;
return $data;
}
/**
* Adds extra information to the core plugin's diagnostic info
*/
function diagnostic_info() {
// store the count of local attachments in a transient
// so not to impact performance with sites with large media libraries
if ( false === ( $attachment_count = get_transient( 'wpmdb_local_attachment_count' ) ) ) {
$attachment_count = $this->media_files_local->get_local_attachments_count();
set_transient( 'wpmdb_local_attachment_count', $attachment_count, 2 * HOUR_IN_SECONDS );
}
echo 'Media Files: ';
echo number_format( $attachment_count );
echo "\r\n";
echo 'Number of Image Sizes: ';
$sizes = count( get_intermediate_image_sizes() );
echo number_format( $sizes );
echo "\r\n";
echo "\r\n";
}
/**
* Media addon nonces for core javascript variables
*
* @param array $nonces Array of nonces
*
* @return array Updated array of nonces
*/
function add_nonces( $nonces ) {
$nonces['migrate_media'] = wp_create_nonce( 'migrate-media' );
$nonces['remove_files_recursive'] = wp_create_nonce( 'remove-files-recursive' );
$nonces['prepare_determine_media'] = wp_create_nonce( 'prepare-determine-media' );
$nonces['determine_media_to_migrate_recursive'] = wp_create_nonce( 'determine-media-to-migrate-recursive' );
return $nonces;
}
/**
* Handler for "wpmdbmf_after_migration_options" action to append subsite select UI.
*/
public function after_migration_options_template() {
if ( is_multisite() ) {
$this->template( 'select-subsites' );
}
}
}

View File

@@ -0,0 +1,300 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the wp-migrate-db-pro-media-files package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: wp-migrate-db-pro-media-files\n"
"Report-Msgid-Bugs-To: nom@deliciousbrains.com\n"
"POT-Creation-Date: 2016-12-15 11:23-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: class/cli/wpmdbpro-media-files-cli.php:32
msgid ""
"WP Migrate DB Pro Media Files does not seem to be installed/active on the "
"remote website."
msgstr ""
#: class/cli/wpmdbpro-media-files-cli.php:35
msgid "Initiating media migration..."
msgstr ""
#: class/wpmdbpro-media-files-base.php:566
#, php-format
msgid "Could not delete \"%s\""
msgstr ""
#: class/wpmdbpro-media-files-local.php:97
msgid "Incorrect migration type supplied"
msgstr ""
#: class/wpmdbpro-media-files-local.php:227
#, php-format
msgid "Could not download file: %1$s - %2$s"
msgstr ""
#: class/wpmdbpro-media-files-local.php:238
#: class/wpmdbpro-media-files-remote.php:240
#, php-format
msgid "Error attempting to create required directory: %s"
msgstr ""
#: class/wpmdbpro-media-files-local.php:243
#, php-format
msgid ""
"Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s"
msgstr ""
#: class/wpmdbpro-media-files-local.php:261
msgid "Failed attempting to process pull request"
msgstr ""
#: class/wpmdbpro-media-files-local.php:457
msgid "There were errors when removing local media files"
msgstr ""
#: class/wpmdbpro-media-files-local.php:585
msgid "Invalid URL Provided."
msgstr ""
#: class/wpmdbpro-media-files-local.php:590
msgid "Could not create Temporary file."
msgstr ""
#: class/wpmdbpro-media-files-remote.php:219
msgid "$_FILES is empty, the upload appears to have failed"
msgstr ""
#: class/wpmdbpro-media-files-remote.php:249
#, php-format
msgid ""
"A problem occurred when attempting to move the temp file \"%1$s\" to \"%2$s\""
msgstr ""
#: class/wpmdbpro-media-files-remote.php:264
msgid "Failed attempting to respond to push request"
msgstr ""
#: class/wpmdbpro-media-files-remote.php:424
msgid "There were errors when removing local media files from the remote site"
msgstr ""
#: class/wpmdbpro-media-files.php:67
msgid "Removing all local files before download of remote media"
msgstr ""
#: class/wpmdbpro-media-files.php:68
msgid "Removing all remote files before upload of local media"
msgstr ""
#: class/wpmdbpro-media-files.php:69
msgid "Removing local files that are not found on the remote site"
msgstr ""
#: class/wpmdbpro-media-files.php:70
msgid "Removing remote files that are not found on the local site"
msgstr ""
#: class/wpmdbpro-media-files.php:71
msgid "Determining media to migrate"
msgstr ""
#: class/wpmdbpro-media-files.php:72
#, php-format
msgid "Determining media to migrate - %1$d of %2$d attachments (%3$d%%)"
msgstr ""
#: class/wpmdbpro-media-files.php:73
msgid "Error while attempting to determine which attachments to migrate."
msgstr ""
#: class/wpmdbpro-media-files.php:74
msgid "Migration failed"
msgstr ""
#: class/wpmdbpro-media-files.php:75
msgid "A problem occurred when migrating the media files."
msgstr ""
#: class/wpmdbpro-media-files.php:76
msgid "Media Attachments"
msgstr ""
#: class/wpmdbpro-media-files.php:77
msgid "Files"
msgstr ""
#: class/wpmdbpro-media-files.php:78
msgid "Downloading files"
msgstr ""
#: class/wpmdbpro-media-files.php:79
msgid "Uploading files"
msgstr ""
#: class/wpmdbpro-media-files.php:80
#, php-format
msgid "Downloading %d of %d files"
msgstr ""
#: class/wpmdbpro-media-files.php:81
#, php-format
msgid "Uploading %d of %d files"
msgstr ""
#: class/wpmdbpro-media-files.php:82
msgid "Files Uploaded"
msgstr ""
#: class/wpmdbpro-media-files.php:83
msgid "Files Downloaded"
msgstr ""
#: class/wpmdbpro-media-files.php:84
msgid "The following file is too large to migrate:"
msgstr ""
#: class/wpmdbpro-media-files.php:85
msgid "Please select at least one subsite to transfer media files for."
msgstr ""
#: template/migrate.php:6
msgid "Media Files"
msgstr ""
#: template/migrate.php:24
msgid "Compare then upload"
msgstr ""
#: template/migrate.php:27
msgid ""
"Compare remote and local media files determining what files are missing or "
"have been updated and need to be uploaded. Great for syncing two Media "
"Libraries that only differ a little. For more details, see the <a href="
"\"https://deliciousbrains.com/wp-migrate-db-pro/doc/media-files-addon\" "
"target=\"_blank\">Media Files doc</a>."
msgstr ""
#: template/migrate.php:31
msgid "Compare then download"
msgstr ""
#: template/migrate.php:34
msgid ""
"Compare remote and local media files determining what files are missing or "
"have been updated and need to be downloaded. Great for syncing two Media "
"Libraries that only differ a little. For more details, see the <a href="
"\"https://deliciousbrains.com/wp-migrate-db-pro/doc/media-files-addon\" "
"target=\"_blank\">Media Files doc</a>."
msgstr ""
#: template/migrate.php:43
msgid "Compare, upload then remove"
msgstr ""
#: template/migrate.php:46
msgid ""
"Same as the above option, but also removes any remote files that are not "
"found in your local Media Library. Any files in the uploads folder that are "
"not part of the Media Library will remain untouched."
msgstr ""
#: template/migrate.php:49
msgid ""
"WARNING: Any files in the remote Media Library that are not in the local "
"Media Library will be removed."
msgstr ""
#: template/migrate.php:53
msgid "Compare, download then remove"
msgstr ""
#: template/migrate.php:56
msgid ""
"Same as the above option, but also removes any local files that are not "
"found in your remote Media Library. Any files in the uploads folder that are "
"not part of the Media Library will remain untouched."
msgstr ""
#: template/migrate.php:59
msgid ""
"WARNING: Any files in the local Media Library that are not in the remote "
"Media Library will be removed."
msgstr ""
#: template/migrate.php:68
msgid "Remove all then upload all"
msgstr ""
#: template/migrate.php:71
msgid ""
"Removes all files in the remote uploads folder and uploads all files in the "
"local uploads folder that are in the Media Library."
msgstr ""
#: template/migrate.php:75
msgid "Remove all then download all"
msgstr ""
#: template/migrate.php:78
msgid ""
"Removes all files in the local uploads folder and downloads all files in the "
"remote uploads folder that are in the Media Library."
msgstr ""
#: template/migrate.php:89
msgid "Addon Missing"
msgstr ""
#: template/migrate.php:89
msgid ""
"The Media Files addon is inactive on the <strong>remote site</strong>. "
"Please install and activate it to enable media file migration."
msgstr ""
#: template/migrate.php:93
msgid "Version Mismatch"
msgstr ""
#: template/migrate.php:93
#, php-format
msgid ""
"We have detected you have version <span class=\"media-file-remote-version"
"\"></span> of WP Migrate DB Pro Media Files at <span class=\"media-files-"
"remote-location\"></span> but are using %1$s here. Please go to the <a href="
"\"%2$s\">Plugins page</a> on both installs and check for updates."
msgstr ""
#: template/select-subsites.php:5
msgid "Only transfer files for selected subsites"
msgstr ""
#: template/select-subsites.php:41
msgid "Select All"
msgstr ""
#: template/select-subsites.php:43
msgid "Deselect All"
msgstr ""
#: template/select-subsites.php:45
msgid "Invert Selection"
msgstr ""
#: template/select-subsites.php:49
msgid "Migrating Media Library data and files differ"
msgstr ""
#: template/select-subsites.php:49
msgid ""
"There is a mismatch between Media Library table data being updated and the "
"files selected for transfer. You may end up with broken links to media."
msgstr ""

View File

@@ -0,0 +1,96 @@
<?php global $loaded_profile; ?>
<div class="option-section media-files-options">
<label class="media-files checkbox-label" for="media-files">
<input type="checkbox" name="media_files" value="1" data-available="1" id="media-files"<?php echo ( isset( $loaded_profile['media_files'] ) ? ' checked="checked"' : '' ); ?> />
<?php _e( 'Media Files', 'wp-migrate-db-pro-media-files' ); ?>
</label>
<div class="indent-wrap expandable-content">
<?php
do_action( 'wpmdbmf_before_migration_options' );
$media_migration_option = isset( $loaded_profile['media_migration_option'] ) ? $loaded_profile['media_migration_option'] : 'compare';
$remove_local_media = isset( $loaded_profile['remove_local_media'] ) ? $loaded_profile['remove_local_media'] : false;
if ( $remove_local_media ) {
$media_migration_option = 'compare-remove';
}
?>
<ul>
<li id="compare-media-list-item">
<label for="compare-media" class="compare-media">
<input type="radio" name="media_migration_option" value="compare" id="compare-media"<?php checked( $media_migration_option, 'compare', true ); ?> />
<span class="action-text push">
<?php _e( 'Compare then upload', 'wp-migrate-db-pro-media-files' ); ?>
<a href="#" class="general-helper replace-guid-helper js-action-link"></a>
<div class="mf-compare-message helper-message">
<?php _e( 'Compare remote and local media files determining what files are missing or have been updated and need to be uploaded. Great for syncing two Media Libraries that only differ a little. For more details, see the <a href="https://deliciousbrains.com/wp-migrate-db-pro/doc/media-files-addon" target="_blank">Media Files doc</a>.', 'wp-migrate-db' ); ?>
</div>
</span>
<span class="action-text pull">
<?php _e( 'Compare then download', 'wp-migrate-db-pro-media-files' ); ?>
<a href="#" class="general-helper replace-guid-helper js-action-link"></a>
<div class="mf-compare-message helper-message">
<?php _e( 'Compare remote and local media files determining what files are missing or have been updated and need to be downloaded. Great for syncing two Media Libraries that only differ a little. For more details, see the <a href="https://deliciousbrains.com/wp-migrate-db-pro/doc/media-files-addon" target="_blank">Media Files doc</a>.', 'wp-migrate-db' ); ?>
</div>
</span>
</label>
</li>
<li id="compare-remove-media-list-item">
<label for="compare-remove-media" class="compare-remove-media">
<input type="radio" name="media_migration_option" value="compare-remove" id="compare-remove-media"<?php checked( $media_migration_option, 'compare-remove', true ); ?> />
<span class="action-text push">
<?php _e( 'Compare, upload then remove', 'wp-migrate-db-pro-media-files' ); ?>
<a href="#" class="general-helper replace-guid-helper js-action-link"></a>
<div class="mf-compare-remove-message helper-message">
<?php _e( 'Same as the above option, but also removes any remote files that are not found in your local Media Library. Any files in the uploads folder that are not part of the Media Library will remain untouched.', 'wp-migrate-db' ); ?>
</div>
<div class="compare-remove-warning">
<?php _e( 'WARNING: Any files in the remote Media Library that are not in the local Media Library will be removed.', 'wp-migrate-db-pro-media-files' ); ?>
</div>
</span>
<span class="action-text pull">
<?php _e( 'Compare, download then remove', 'wp-migrate-db-pro-media-files' ); ?>
<a href="#" class="general-helper replace-guid-helper js-action-link"></a>
<div class="mf-compare-remove-message helper-message">
<?php _e( 'Same as the above option, but also removes any local files that are not found in your remote Media Library. Any files in the uploads folder that are not part of the Media Library will remain untouched.', 'wp-migrate-db' ); ?>
</div>
<div class="compare-remove-warning">
<?php _e( 'WARNING: Any files in the local Media Library that are not in the remote Media Library will be removed.', 'wp-migrate-db-pro-media-files' ); ?>
</div>
</span>
</label>
</li>
<li id="copy-entire-media-list-item">
<label for="copy-entire-media" class="copy-entire-media">
<input type="radio" name="media_migration_option" value="entire" id="copy-entire-media"<?php checked( $media_migration_option, 'entire', true ); ?> />
<span class="action-text push">
<?php _e( 'Remove all then upload all', 'wp-migrate-db-pro-media-files' ); ?>
<a href="#" class="general-helper replace-guid-helper js-action-link"></a>
<div class="mf-copy-entire-message helper-message">
<?php _e( 'Removes all files in the remote uploads folder and uploads all files in the local uploads folder that are in the Media Library.', 'wp-migrate-db' ); ?>
</div>
</span>
<span class="action-text pull">
<?php _e( 'Remove all then download all', 'wp-migrate-db-pro-media-files' ); ?>
<a href="#" class="general-helper replace-guid-helper js-action-link"></a>
<div class="mf-copy-entire-message helper-message">
<?php _e( 'Removes all files in the local uploads folder and downloads all files in the remote uploads folder that are in the Media Library.', 'wp-migrate-db' ); ?>
</div>
</span>
</label>
</li>
</ul>
<?php do_action( 'wpmdbmf_after_migration_options' ); ?>
</div>
<p class="media-migration-unavailable inline-message warning" style="display: none; margin: 10px 0 0 0;">
<strong><?php _e( 'Addon Missing', 'wp-migrate-db-pro-media-files' ); ?></strong> &mdash; <?php _e( 'The Media Files addon is inactive on the <strong>remote site</strong>. Please install and activate it to enable media file migration.', 'wp-migrate-db-pro-media-files' ); ?>
</p>
<p class="media-files-different-plugin-version-notice inline-message warning" style="display: none; margin: 10px 0 0 0;">
<strong><?php _e( 'Version Mismatch', 'wp-migrate-db-pro-media-files' ); ?></strong> &mdash; <?php printf( __( 'We have detected you have version <span class="media-file-remote-version"></span> of WP Migrate DB Pro Media Files at <span class="media-files-remote-location"></span> but are using %1$s here. Please go to the <a href="%2$s">Plugins page</a> on both installs and check for updates.', 'wp-migrate-db-pro-media-files' ), $GLOBALS['wpmdb_meta'][$this->plugin_slug]['version'], network_admin_url( 'plugins.php' ) ); ?>
</p>
</div>

View File

@@ -0,0 +1,52 @@
<?php global $loaded_profile; ?>
<div id="mf-select-subsites-section" class="option-section sub-option" style="display: block;">
<label for="mf-select-subsites" class="mf-select-subsites-checkbox checkbox-label">
<input type="checkbox" id="mf-select-subsites" value="1" autocomplete="off" name="mf_select_subsites"<?php $this->maybe_checked( $loaded_profile, 'mf_select_subsites' ); ?> />
<?php _e( 'Only transfer files for selected subsites', 'wp-migrate-db-pro-media-files' ); ?>
</label>
<div class="indent-wrap expandable-content mf-subsite-select-wrap" style="display: none;">
<div class="mf-selected-subsites-wrap select-wrap">
<?php
// If loading a Pull migration profile, we need to keep tabs on the original site selections until remote's data is available.
if ( 'pull' === $loaded_profile['action'] && ! empty( $loaded_profile['mf_selected_subsites'] ) ) {
?>
<input type="hidden" name="_mf_selected_subsites" id="_mf-selected-subsites" value="<?php echo esc_attr( json_encode( $loaded_profile['mf_selected_subsites'] ) ); ?>">
<?php
}
?>
<select multiple="multiple" name="mf_selected_subsites[]" id="mf-selected-subsites" class="multiselect" autocomplete="off">
<?php
if ( 'pull' !== $loaded_profile['action'] ) {
global $wpdb;
$table_prefix = $wpdb->base_prefix;
foreach ( $this->subsites_list() as $blog_id => $subsite_path ) {
$selected = '';
if ( empty( $loaded_profile['mf_selected_subsites'] ) ||
( ! empty( $loaded_profile['mf_selected_subsites'] ) && in_array( $blog_id, $loaded_profile['mf_selected_subsites'] ) )
) {
$selected = ' selected="selected"';
}
$subsite_path .= ' (' . $table_prefix . ( '1' !== $blog_id ) ? $blog_id . '_' : '' . ')';
printf(
'<option value="%1$s"' . $selected . '>%2$s</option>',
esc_attr( $blog_id ),
esc_html( $subsite_path )
);
}
}
?>
</select>
<br/>
<a href="#" class="multiselect-select-all js-action-link"><?php _e( 'Select All', 'wp-migrate-db-pro-media-files' ); ?></a>
<span class="select-deselect-divider">/</span>
<a href="#" class="multiselect-deselect-all js-action-link"><?php _e( 'Deselect All', 'wp-migrate-db-pro-media-files' ); ?></a>
<span class="select-deselect-divider">/</span>
<a href="#" class="multiselect-invert-selection js-action-link"><?php _e( 'Invert Selection', 'wp-migrate-db-pro-media-files' ); ?></a>
</div>
<p class="mf-selected-subsites-tables-differ inline-message warning" style="display: none; margin: 10px 0 0 0;">
<strong><?php _e( 'Migrating Media Library data and files differ', 'wp-migrate-db-pro-media-files' ); ?></strong> &mdash; <?php _e( 'There is a mismatch between Media Library table data being updated and the files selected for transfer. You may end up with broken links to media.', 'wp-migrate-db-pro-media-files' ); ?>
</p>
</div>
</div>

View File

@@ -0,0 +1,2 @@
<?php
$GLOBALS['wpmdb_meta']['wp-migrate-db-pro-media-files']['version'] = '1.4.7';

View File

@@ -0,0 +1,85 @@
<?php
/*
Plugin Name: WP Migrate DB Pro Media Files
Plugin URI: http://deliciousbrains.com/wp-migrate-db-pro/
Description: An extension to WP Migrate DB Pro, allows the migration of media files.
Author: Delicious Brains
Version: 1.4.7
Author URI: http://deliciousbrains.com
Network: True
*/
// Copyright (c) 2013 Delicious Brains. All rights reserved.
//
// Released under the GPL license
// http://www.opensource.org/licenses/gpl-license.php
//
// **********************************************************************
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// **********************************************************************
require_once 'version.php';
$GLOBALS['wpmdb_meta']['wp-migrate-db-pro-media-files']['folder'] = basename( plugin_dir_path( __FILE__ ) );
/**
* Populate the $wpmdbpro_media_files global with an instance of the WPMDBPro_Media_Files class and return it.
*
* @param bool $cli Running in WP-CLI environment.
*
* @return WPMDBPro_Media_Files The one true global instance of the WPMDBPro_Media_Files class.
*/
function wp_migrate_db_pro_media_files( $cli = false ) {
global $wpmdbpro_media_files;
if ( ! class_exists( 'WPMDBPro_Addon' ) ) {
return false;
}
// Allows hooks to bypass the regular admin / ajax checks to force load the Media Files addon (required for the CLI addon)
$force_load = apply_filters( 'wp_migrate_db_pro_media_files_force_load', false );
if ( false === $force_load && ! is_null( $wpmdbpro_media_files ) ) {
return $wpmdbpro_media_files;
}
if ( false === $force_load && ( ! function_exists( 'wp_migrate_db_pro_loaded' ) || ! wp_migrate_db_pro_loaded() ) ) {
return false;
}
load_plugin_textdomain( 'wp-migrate-db-pro-media-files', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
require_once dirname( __FILE__ ) . '/class/wpmdbpro-media-files.php';
require_once dirname( __FILE__ ) . '/class/wpmdbpro-media-files-base.php';
require_once dirname( __FILE__ ) . '/class/wpmdbpro-media-files-local.php';
require_once dirname( __FILE__ ) . '/class/wpmdbpro-media-files-remote.php';
if ( $cli ) {
require_once dirname( __FILE__ ) . '/class/cli/wpmdbpro-media-files-cli.php';
require_once dirname( __FILE__ ) . '/class/cli/wpmdbpro-media-files-cli-bar.php';
$wpmdbpro_media_files = new WPMDBPro_Media_Files_CLI( __FILE__ );
} else {
$wpmdbpro_media_files = new WPMDBPro_Media_Files( __FILE__ );
}
return $wpmdbpro_media_files;
}
/**
* By default load plugin on admin pages, a little later than WP Migrate DB Pro.
*/
add_action( 'admin_init', 'wp_migrate_db_pro_media_files', 20 );
/**
* Loads up an instance of the WPMDBPro_Media_Files class, allowing media files to be migrated during CLI migrations.
*/
function wp_migrate_db_pro_media_files_before_cli_load() {
// Force load the Media Files addon
add_filter( 'wp_migrate_db_pro_media_files_force_load', '__return_true' );
wp_migrate_db_pro_media_files( true );
}
add_action( 'wp_migrate_db_pro_cli_before_load', 'wp_migrate_db_pro_media_files_before_cli_load' );