All-in-One WP Migration - Version 7.11

Version Description

Added

  • Support for Webba Booking plugin
  • Support for utf8mb4_0900_ai_ci collation in MySQL v8.0.1+
Download this release

Release Info

Developer bangelov
Plugin Icon 128x128 All-in-One WP Migration
Version 7.11
Comparing to
See all releases

Code changes from version 7.10 to 7.11

all-in-one-wp-migration.php CHANGED
@@ -5,7 +5,7 @@
5
  * Description: Migration tool for all your blog data. Import or Export your blog content with a single click.
6
  * Author: ServMask
7
  * Author URI: https://servmask.com/
8
- * Version: 7.10
9
  * Text Domain: all-in-one-wp-migration
10
  * Domain Path: /languages
11
  * Network: True
5
  * Description: Migration tool for all your blog data. Import or Export your blog content with a single click.
6
  * Author: ServMask
7
  * Author URI: https://servmask.com/
8
+ * Version: 7.11
9
  * Text Domain: all-in-one-wp-migration
10
  * Domain Path: /languages
11
  * Network: True
constants.php CHANGED
@@ -35,7 +35,7 @@ define( 'AI1WM_DEBUG', false );
35
  // ==================
36
  // = Plugin Version =
37
  // ==================
38
- define( 'AI1WM_VERSION', '7.10' );
39
 
40
  // ===============
41
  // = Plugin Name =
35
  // ==================
36
  // = Plugin Version =
37
  // ==================
38
+ define( 'AI1WM_VERSION', '7.11' );
39
 
40
  // ===============
41
  // = Plugin Name =
lib/model/class-ai1wm-compatibility.php CHANGED
@@ -49,15 +49,11 @@ class Ai1wm_Compatibility {
49
  $messages = array();
50
  foreach ( $extensions as $extension_name => $extension_data ) {
51
  if ( ! Ai1wm_Compatibility::check( $extension_data ) ) {
52
- $messages[] = sprintf(
53
- __(
54
- '<strong>%s</strong> is not the latest version. ' .
55
- 'You must <a href="%s">update the plugin</a> before you can use it. <br />',
56
- AI1WM_PLUGIN_NAME
57
- ),
58
- $extension_data['title'],
59
- $updater_url
60
- );
61
  }
62
  }
63
 
49
  $messages = array();
50
  foreach ( $extensions as $extension_name => $extension_data ) {
51
  if ( ! Ai1wm_Compatibility::check( $extension_data ) ) {
52
+ if ( defined( 'WP_CLI' ) ) {
53
+ $messages[] = sprintf( __( '%s is not the latest version. You must update the plugin before you can use it. ', AI1WM_PLUGIN_NAME ), $extension_data['title'] );
54
+ } else {
55
+ $messages[] = sprintf( __( '<strong>%s</strong> is not the latest version. You must <a href="%s">update the plugin</a> before you can use it. <br />', AI1WM_PLUGIN_NAME ), $extension_data['title'], $updater_url );
56
+ }
 
 
 
 
57
  }
58
  }
59
 
lib/model/export/class-ai1wm-export-clean.php CHANGED
@@ -30,10 +30,15 @@ if ( ! defined( 'ABSPATH' ) ) {
30
  class Ai1wm_Export_Clean {
31
 
32
  public static function execute( $params ) {
 
 
33
  Ai1wm_Directory::delete( ai1wm_storage_path( $params ) );
 
 
34
  if ( defined( 'WP_CLI' ) ) {
35
  return $params;
36
  }
 
37
  exit;
38
  }
39
  }
30
  class Ai1wm_Export_Clean {
31
 
32
  public static function execute( $params ) {
33
+
34
+ // Delete storage files
35
  Ai1wm_Directory::delete( ai1wm_storage_path( $params ) );
36
+
37
+ // Exit in console
38
  if ( defined( 'WP_CLI' ) ) {
39
  return $params;
40
  }
41
+
42
  exit;
43
  }
44
  }
lib/model/export/class-ai1wm-export-database.php CHANGED
@@ -128,6 +128,11 @@ class Ai1wm_Export_Database {
128
  }
129
  }
130
 
 
 
 
 
 
131
  // Set database options
132
  $mysql->set_old_table_prefixes( $old_table_prefixes )
133
  ->set_new_table_prefixes( $new_table_prefixes )
128
  }
129
  }
130
 
131
+ // Include table prefixes (Webba Booking)
132
+ foreach ( array( 'wbk_services', 'wbk_days_on_off', 'wbk_locked_time_slots', 'wbk_appointments', 'wbk_cancelled_appointments', 'wbk_email_templates', 'wbk_service_categories', 'wbk_gg_calendars', 'wbk_coupons' ) as $table_name ) {
133
+ $include_table_prefixes[] = $table_name;
134
+ }
135
+
136
  // Set database options
137
  $mysql->set_old_table_prefixes( $old_table_prefixes )
138
  ->set_new_table_prefixes( $new_table_prefixes )
lib/model/export/class-ai1wm-export-enumerate.php CHANGED
@@ -48,23 +48,6 @@ class Ai1wm_Export_Enumerate {
48
  // Set progress
49
  Ai1wm_Status::info( __( 'Retrieving a list of all WordPress files...', AI1WM_PLUGIN_NAME ) );
50
 
51
- if ( isset( $params['options']['no_media'] )
52
- && isset( $params['options']['no_themes'] )
53
- && isset( $params['options']['no_muplugins'] )
54
- && isset( $params['options']['no_plugins'] ) ) {
55
-
56
- // Create map file
57
- $filemap = ai1wm_open( ai1wm_filemap_path( $params ), 'w' );
58
-
59
- // Close the filemap file
60
- ai1wm_close( $filemap );
61
-
62
- // Set progress
63
- Ai1wm_Status::info( __( 'Done retrieving a list of all WordPress files.', AI1WM_PLUGIN_NAME ) );
64
-
65
- return $params;
66
- }
67
-
68
  // Set exclude filters
69
  $exclude_filters = ai1wm_content_filters();
70
 
@@ -123,7 +106,7 @@ class Ai1wm_Export_Enumerate {
123
  }
124
 
125
  // Exclude selected files
126
- if ( isset( $params['options']['exclude_files'] ) && isset( $params['excluded_files'] ) ) {
127
  $excluded_files = explode( ',', $params['excluded_files'] );
128
  if ( $excluded_files ) {
129
  $exclude_filters = array_merge( $exclude_filters, $excluded_files );
@@ -133,23 +116,27 @@ class Ai1wm_Export_Enumerate {
133
  // Create map file
134
  $filemap = ai1wm_open( ai1wm_filemap_path( $params ), 'w' );
135
 
136
- // Iterate over content directory
137
- $iterator = new Ai1wm_Recursive_Directory_Iterator( WP_CONTENT_DIR );
138
 
139
- // Exclude uploads, plugins or themes
140
- $iterator = new Ai1wm_Recursive_Exclude_Filter( $iterator, apply_filters( 'ai1wm_exclude_content_from_export', $exclude_filters ) );
141
 
142
- // Recursively iterate over content directory
143
- $iterator = new Ai1wm_Recursive_Iterator_Iterator( $iterator, RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD );
144
 
145
- // Write path line
146
- foreach ( $iterator as $item ) {
147
- if ( $item->isFile() ) {
148
- if ( ai1wm_write( $filemap, $iterator->getSubPathName() . PHP_EOL ) ) {
149
- $total_files_count++;
150
 
151
- // Add current file size
152
- $total_files_size += $iterator->getSize();
 
 
 
 
 
 
 
153
  }
154
  }
155
  }
48
  // Set progress
49
  Ai1wm_Status::info( __( 'Retrieving a list of all WordPress files...', AI1WM_PLUGIN_NAME ) );
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  // Set exclude filters
52
  $exclude_filters = ai1wm_content_filters();
53
 
106
  }
107
 
108
  // Exclude selected files
109
+ if ( isset( $params['options']['exclude_files'], $params['excluded_files'] ) ) {
110
  $excluded_files = explode( ',', $params['excluded_files'] );
111
  if ( $excluded_files ) {
112
  $exclude_filters = array_merge( $exclude_filters, $excluded_files );
116
  // Create map file
117
  $filemap = ai1wm_open( ai1wm_filemap_path( $params ), 'w' );
118
 
119
+ // Enumerate over content directory
120
+ if ( isset( $params['options']['no_media'], $params['options']['no_themes'], $params['options']['no_muplugins'], $params['options']['no_plugins'] ) === false ) {
121
 
122
+ // Iterate over content directory
123
+ $iterator = new Ai1wm_Recursive_Directory_Iterator( WP_CONTENT_DIR );
124
 
125
+ // Exclude uploads, plugins or themes
126
+ $iterator = new Ai1wm_Recursive_Exclude_Filter( $iterator, apply_filters( 'ai1wm_exclude_content_from_export', $exclude_filters ) );
127
 
128
+ // Recursively iterate over content directory
129
+ $iterator = new Ai1wm_Recursive_Iterator_Iterator( $iterator, RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD );
 
 
 
130
 
131
+ // Write path line
132
+ foreach ( $iterator as $item ) {
133
+ if ( $item->isFile() ) {
134
+ if ( ai1wm_write( $filemap, $iterator->getSubPathName() . PHP_EOL ) ) {
135
+ $total_files_count++;
136
+
137
+ // Add current file size
138
+ $total_files_size += $iterator->getSize();
139
+ }
140
  }
141
  }
142
  }
lib/model/import/class-ai1wm-import-blogs.php CHANGED
@@ -103,19 +103,13 @@ class Ai1wm_Import_Blogs {
103
  ),
104
  );
105
  } else {
106
- throw new Ai1wm_Import_Exception(
107
- __( 'The archive should contain <strong>Single WordPress</strong> site! Please revisit your export settings.', AI1WM_PLUGIN_NAME )
108
- );
109
  }
110
  } else {
111
- throw new Ai1wm_Import_Exception(
112
- __( 'At least <strong>one WordPress</strong> site should be presented in the archive.', AI1WM_PLUGIN_NAME )
113
- );
114
  }
115
  } else {
116
- throw new Ai1wm_Import_Exception(
117
- __( 'Unable to import <strong>WordPress Network</strong> into WordPress <strong>Single</strong> site.', AI1WM_PLUGIN_NAME )
118
- );
119
  }
120
  }
121
 
103
  ),
104
  );
105
  } else {
106
+ throw new Ai1wm_Import_Exception( __( 'The archive should contain <strong>Single WordPress</strong> site! Please revisit your export settings.', AI1WM_PLUGIN_NAME ) );
 
 
107
  }
108
  } else {
109
+ throw new Ai1wm_Import_Exception( __( 'At least <strong>one WordPress</strong> site should be presented in the archive.', AI1WM_PLUGIN_NAME ) );
 
 
110
  }
111
  } else {
112
+ throw new Ai1wm_Import_Exception( __( 'Unable to import <strong>WordPress Network</strong> into WordPress <strong>Single</strong> site.', AI1WM_PLUGIN_NAME ) );
 
 
113
  }
114
  }
115
 
lib/model/import/class-ai1wm-import-clean.php CHANGED
@@ -30,10 +30,27 @@ if ( ! defined( 'ABSPATH' ) ) {
30
  class Ai1wm_Import_Clean {
31
 
32
  public static function execute( $params ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  Ai1wm_Directory::delete( ai1wm_storage_path( $params ) );
 
 
34
  if ( defined( 'WP_CLI' ) ) {
35
  return $params;
36
  }
 
37
  exit;
38
  }
39
  }
30
  class Ai1wm_Import_Clean {
31
 
32
  public static function execute( $params ) {
33
+ global $wpdb;
34
+
35
+ // Get database client
36
+ if ( empty( $wpdb->use_mysqli ) ) {
37
+ $mysql = new Ai1wm_Database_Mysql( $wpdb );
38
+ } else {
39
+ $mysql = new Ai1wm_Database_Mysqli( $wpdb );
40
+ }
41
+
42
+ // Flush mainsite tables
43
+ $mysql->set_include_table_prefixes( array( ai1wm_table_prefix( 'mainsite' ) ) );
44
+ $mysql->flush();
45
+
46
+ // Delete storage files
47
  Ai1wm_Directory::delete( ai1wm_storage_path( $params ) );
48
+
49
+ // Exit in console
50
  if ( defined( 'WP_CLI' ) ) {
51
  return $params;
52
  }
53
+
54
  exit;
55
  }
56
  }
lib/model/import/class-ai1wm-import-confirm.php CHANGED
@@ -43,21 +43,29 @@ class Ai1wm_Import_Confirm {
43
  // Close handle
44
  ai1wm_close( $handle );
45
 
46
- // Set message
47
- $messages[] = __(
48
- 'The import process will overwrite your website including the database, media, plugins, and themes. ' .
49
- 'Please ensure that you have a backup of your data before proceeding to the next step.',
50
- AI1WM_PLUGIN_NAME
51
- );
 
 
 
 
 
 
 
 
52
 
53
  // Check compatibility of PHP versions
54
  if ( isset( $package['PHP']['Version'] ) ) {
55
  if ( version_compare( $package['PHP']['Version'], '7.0.0', '<' ) && version_compare( PHP_VERSION, '7.0.0', '>=' ) ) {
56
-
57
  if ( defined( 'WP_CLI' ) ) {
58
- WP_CLI::log(
59
- __( 'Your backup is from a PHP 5 but the site that you are importing to is PHP 7.', AI1WM_PLUGIN_NAME ) .
60
- __( 'This could cause the import to fail. Technical details: https://help.servmask.com/knowledgebase/migrate-wordpress-from-php-5-to-php-7', AI1WM_PLUGIN_NAME )
 
61
  );
62
  } else {
63
  $messages[] = __(
@@ -70,16 +78,13 @@ class Ai1wm_Import_Confirm {
70
  }
71
 
72
  if ( defined( 'WP_CLI' ) ) {
73
- $message = __(
74
- 'The import process will overwrite your website including the database, media, plugins, and themes. Are you sure to proceed?',
75
- AI1WM_PLUGIN_NAME
76
- );
77
-
78
  $assoc_args = array();
79
  if ( isset( $params['cli_args'] ) ) {
80
  $assoc_args = $params['cli_args'];
81
  }
82
- WP_CLI::confirm( $message, $assoc_args );
 
 
83
  return $params;
84
  }
85
 
43
  // Close handle
44
  ai1wm_close( $handle );
45
 
46
+ // Confirm message
47
+ if ( defined( 'WP_CLI' ) ) {
48
+ $messages[] = __(
49
+ 'The import process will overwrite your website including the database, media, plugins, and themes. ' .
50
+ 'Are you sure to proceed?',
51
+ AI1WM_PLUGIN_NAME
52
+ );
53
+ } else {
54
+ $messages[] = __(
55
+ 'The import process will overwrite your website including the database, media, plugins, and themes. ' .
56
+ 'Please ensure that you have a backup of your data before proceeding to the next step.',
57
+ AI1WM_PLUGIN_NAME
58
+ );
59
+ }
60
 
61
  // Check compatibility of PHP versions
62
  if ( isset( $package['PHP']['Version'] ) ) {
63
  if ( version_compare( $package['PHP']['Version'], '7.0.0', '<' ) && version_compare( PHP_VERSION, '7.0.0', '>=' ) ) {
 
64
  if ( defined( 'WP_CLI' ) ) {
65
+ $messages[] = __(
66
+ 'Your backup is from a PHP 5 but the site that you are importing to is PHP 7. ' .
67
+ 'This could cause the import to fail. Technical details: https://help.servmask.com/knowledgebase/migrate-wordpress-from-php-5-to-php-7/',
68
+ AI1WM_PLUGIN_NAME
69
  );
70
  } else {
71
  $messages[] = __(
78
  }
79
 
80
  if ( defined( 'WP_CLI' ) ) {
 
 
 
 
 
81
  $assoc_args = array();
82
  if ( isset( $params['cli_args'] ) ) {
83
  $assoc_args = $params['cli_args'];
84
  }
85
+
86
+ WP_CLI::confirm( implode( $messages ), $assoc_args );
87
+
88
  return $params;
89
  }
90
 
lib/model/import/class-ai1wm-import-database.php CHANGED
@@ -697,10 +697,6 @@ class Ai1wm_Import_Database {
697
  $old_table_prefixes = array();
698
  $new_table_prefixes = array();
699
 
700
- // Set main table prefixes
701
- $old_table_prefixes[] = ai1wm_servmask_prefix( 'mainsite' );
702
- $new_table_prefixes[] = ai1wm_table_prefix();
703
-
704
  // Set site table prefixes
705
  foreach ( $blogs as $blog ) {
706
  if ( ai1wm_main_site( $blog['Old']['BlogID'] ) === false ) {
@@ -709,6 +705,12 @@ class Ai1wm_Import_Database {
709
  }
710
  }
711
 
 
 
 
 
 
 
712
  // Set base table prefixes
713
  foreach ( $blogs as $blog ) {
714
  if ( ai1wm_main_site( $blog['Old']['BlogID'] ) === true ) {
@@ -717,7 +719,7 @@ class Ai1wm_Import_Database {
717
  }
718
  }
719
 
720
- // Set site table prefixes
721
  foreach ( $blogs as $blog ) {
722
  if ( ai1wm_main_site( $blog['Old']['BlogID'] ) === true ) {
723
  $old_table_prefixes[] = ai1wm_servmask_prefix( $blog['Old']['BlogID'] );
@@ -744,14 +746,6 @@ class Ai1wm_Import_Database {
744
  ->set_old_replace_raw_values( $old_replace_raw_values )
745
  ->set_new_replace_raw_values( $new_replace_raw_values );
746
 
747
- // Flush database
748
- if ( isset( $config['Plugin']['Version'] ) && ( $version = $config['Plugin']['Version'] ) ) {
749
- if ( $version !== 'develop' && version_compare( $version, '4.10', '<' ) ) {
750
- $mysql->set_include_table_prefixes( array( ai1wm_table_prefix() ) );
751
- $mysql->flush();
752
- }
753
- }
754
-
755
  // Set atomic tables (do not stop the current request for all listed tables if timeout has been exceeded)
756
  $mysql->set_atomic_tables( array( ai1wm_table_prefix() . 'options' ) );
757
 
697
  $old_table_prefixes = array();
698
  $new_table_prefixes = array();
699
 
 
 
 
 
700
  // Set site table prefixes
701
  foreach ( $blogs as $blog ) {
702
  if ( ai1wm_main_site( $blog['Old']['BlogID'] ) === false ) {
705
  }
706
  }
707
 
708
+ // Set global table prefixes
709
+ foreach ( $wpdb->global_tables as $table_name ) {
710
+ $old_table_prefixes[] = ai1wm_servmask_prefix( 'mainsite' ) . $table_name;
711
+ $new_table_prefixes[] = ai1wm_table_prefix() . $table_name;
712
+ }
713
+
714
  // Set base table prefixes
715
  foreach ( $blogs as $blog ) {
716
  if ( ai1wm_main_site( $blog['Old']['BlogID'] ) === true ) {
719
  }
720
  }
721
 
722
+ // Set main table prefixes
723
  foreach ( $blogs as $blog ) {
724
  if ( ai1wm_main_site( $blog['Old']['BlogID'] ) === true ) {
725
  $old_table_prefixes[] = ai1wm_servmask_prefix( $blog['Old']['BlogID'] );
746
  ->set_old_replace_raw_values( $old_replace_raw_values )
747
  ->set_new_replace_raw_values( $new_replace_raw_values );
748
 
 
 
 
 
 
 
 
 
749
  // Set atomic tables (do not stop the current request for all listed tables if timeout has been exceeded)
750
  $mysql->set_atomic_tables( array( ai1wm_table_prefix() . 'options' ) );
751
 
lib/model/import/class-ai1wm-import-upload.php CHANGED
@@ -31,24 +31,15 @@ class Ai1wm_Import_Upload {
31
 
32
  private static function validate() {
33
  if ( ! array_key_exists( 'upload-file', $_FILES ) || ! is_array( $_FILES['upload-file'] ) ) {
34
- throw new Ai1wm_Import_Retry_Exception(
35
- __( 'Missing upload file.', AI1WM_PLUGIN_NAME ),
36
- 400
37
- );
38
  }
39
 
40
  if ( ! array_key_exists( 'error', $_FILES['upload-file'] ) ) {
41
- throw new Ai1wm_Import_Retry_Exception(
42
- __( 'Missing error key in upload file.', AI1WM_PLUGIN_NAME ),
43
- 400
44
- );
45
  }
46
 
47
  if ( ! array_key_exists( 'tmp_name', $_FILES['upload-file'] ) ) {
48
- throw new Ai1wm_Import_Retry_Exception(
49
- __( 'Missing tmp_name in upload file.', AI1WM_PLUGIN_NAME ),
50
- 400
51
- );
52
  }
53
  }
54
 
@@ -65,47 +56,28 @@ class Ai1wm_Import_Upload {
65
  ai1wm_copy( $upload, $archive );
66
  ai1wm_unlink( $upload );
67
  } catch ( Exception $e ) {
68
- throw new Ai1wm_Import_Retry_Exception(
69
- sprintf(
70
- __( 'Unable to upload the file because %s', AI1WM_PLUGIN_NAME ),
71
- $e->getMessage()
72
- ),
73
- 400
74
- );
75
  }
76
  break;
 
77
  case UPLOAD_ERR_INI_SIZE:
78
  case UPLOAD_ERR_FORM_SIZE:
79
  case UPLOAD_ERR_PARTIAL:
80
  case UPLOAD_ERR_NO_FILE:
81
  // File is too large
82
- throw new Ai1wm_Import_Retry_Exception(
83
- __( 'The file is too large for this server.', AI1WM_PLUGIN_NAME ),
84
- 413
85
- );
86
  case UPLOAD_ERR_NO_TMP_DIR:
87
- throw new Ai1wm_Import_Retry_Exception(
88
- __( 'Missing a temporary folder.', AI1WM_PLUGIN_NAME ),
89
- 400
90
- );
91
  case UPLOAD_ERR_CANT_WRITE:
92
- throw new Ai1wm_Import_Retry_Exception(
93
- __( 'Failed to write file to disk.', AI1WM_PLUGIN_NAME ),
94
- 400
95
- );
96
  case UPLOAD_ERR_EXTENSION:
97
- throw new Ai1wm_Import_Retry_Exception(
98
- __( 'A PHP extension stopped the file upload.', AI1WM_PLUGIN_NAME ),
99
- 400
100
- );
101
  default:
102
- throw new Ai1wm_Import_Retry_Exception(
103
- sprintf(
104
- __( 'Unrecognized error %s during upload.', AI1WM_PLUGIN_NAME ),
105
- $error
106
- ),
107
- 400
108
- );
109
  }
110
 
111
  echo json_encode( array( 'errors' => array() ) );
31
 
32
  private static function validate() {
33
  if ( ! array_key_exists( 'upload-file', $_FILES ) || ! is_array( $_FILES['upload-file'] ) ) {
34
+ throw new Ai1wm_Import_Retry_Exception( __( 'Missing upload file.', AI1WM_PLUGIN_NAME ), 400 );
 
 
 
35
  }
36
 
37
  if ( ! array_key_exists( 'error', $_FILES['upload-file'] ) ) {
38
+ throw new Ai1wm_Import_Retry_Exception( __( 'Missing error key in upload file.', AI1WM_PLUGIN_NAME ), 400 );
 
 
 
39
  }
40
 
41
  if ( ! array_key_exists( 'tmp_name', $_FILES['upload-file'] ) ) {
42
+ throw new Ai1wm_Import_Retry_Exception( __( 'Missing tmp_name in upload file.', AI1WM_PLUGIN_NAME ), 400 );
 
 
 
43
  }
44
  }
45
 
56
  ai1wm_copy( $upload, $archive );
57
  ai1wm_unlink( $upload );
58
  } catch ( Exception $e ) {
59
+ throw new Ai1wm_Import_Retry_Exception( sprintf( __( 'Unable to upload the file because %s', AI1WM_PLUGIN_NAME ), $e->getMessage() ), 400 );
 
 
 
 
 
 
60
  }
61
  break;
62
+
63
  case UPLOAD_ERR_INI_SIZE:
64
  case UPLOAD_ERR_FORM_SIZE:
65
  case UPLOAD_ERR_PARTIAL:
66
  case UPLOAD_ERR_NO_FILE:
67
  // File is too large
68
+ throw new Ai1wm_Import_Retry_Exception( __( 'The file is too large for this server.', AI1WM_PLUGIN_NAME ), 413 );
69
+
 
 
70
  case UPLOAD_ERR_NO_TMP_DIR:
71
+ throw new Ai1wm_Import_Retry_Exception( __( 'Missing a temporary folder.', AI1WM_PLUGIN_NAME ), 400 );
72
+
 
 
73
  case UPLOAD_ERR_CANT_WRITE:
74
+ throw new Ai1wm_Import_Retry_Exception( __( 'Failed to write file to disk.', AI1WM_PLUGIN_NAME ), 400 );
75
+
 
 
76
  case UPLOAD_ERR_EXTENSION:
77
+ throw new Ai1wm_Import_Retry_Exception( __( 'A PHP extension stopped the file upload.', AI1WM_PLUGIN_NAME ), 400 );
78
+
 
 
79
  default:
80
+ throw new Ai1wm_Import_Retry_Exception( sprintf( __( 'Unrecognized error %s during upload.', AI1WM_PLUGIN_NAME ), $error ), 400 );
 
 
 
 
 
 
81
  }
82
 
83
  echo json_encode( array( 'errors' => array() ) );
lib/model/import/class-ai1wm-import-validate.php CHANGED
@@ -33,13 +33,7 @@ class Ai1wm_Import_Validate {
33
 
34
  // Verify file if size > 2GB and PHP = 32-bit
35
  if ( ! ai1wm_is_filesize_supported( ai1wm_archive_path( $params ) ) ) {
36
- throw new Ai1wm_Import_Exception(
37
- __(
38
- 'Your PHP is 32-bit. In order to import your file, please change your PHP version to 64-bit and try again. ' .
39
- '<a href="https://help.servmask.com/knowledgebase/php-32bit/" target="_blank">Technical details</a>',
40
- AI1WM_PLUGIN_NAME
41
- )
42
- );
43
  }
44
 
45
  // Set archive bytes offset
@@ -77,13 +71,7 @@ class Ai1wm_Import_Validate {
77
 
78
  // Validate the archive file consistency
79
  if ( ! $archive->is_valid() ) {
80
- throw new Ai1wm_Import_Exception(
81
- __(
82
- 'The archive file is corrupted. Follow ' .
83
- '<a href="https://help.servmask.com/knowledgebase/corrupted-archive/" target="_blank">this article</a> to resolve the problem.',
84
- AI1WM_PLUGIN_NAME
85
- )
86
- );
87
  }
88
 
89
  // Flag to hold if file data has been processed
@@ -106,13 +94,7 @@ class Ai1wm_Import_Validate {
106
 
107
  // Check package.json file
108
  if ( false === is_file( ai1wm_package_path( $params ) ) ) {
109
- throw new Ai1wm_Import_Exception(
110
- __(
111
- 'Please make sure that your file was exported using <strong>All-in-One WP Migration</strong> plugin. ' .
112
- '<a href="https://help.servmask.com/knowledgebase/invalid-backup-file/" target="_blank">Technical details</a>',
113
- AI1WM_PLUGIN_NAME
114
- )
115
- );
116
  }
117
 
118
  // Set progress
33
 
34
  // Verify file if size > 2GB and PHP = 32-bit
35
  if ( ! ai1wm_is_filesize_supported( ai1wm_archive_path( $params ) ) ) {
36
+ throw new Ai1wm_Import_Exception( __( 'Your PHP is 32-bit. In order to import your file, please change your PHP version to 64-bit and try again. <a href="https://help.servmask.com/knowledgebase/php-32bit/" target="_blank">Technical details</a>', AI1WM_PLUGIN_NAME ) );
 
 
 
 
 
 
37
  }
38
 
39
  // Set archive bytes offset
71
 
72
  // Validate the archive file consistency
73
  if ( ! $archive->is_valid() ) {
74
+ throw new Ai1wm_Import_Exception( __( 'The archive file is corrupted. Follow <a href="https://help.servmask.com/knowledgebase/corrupted-archive/" target="_blank">this article</a> to resolve the problem.', AI1WM_PLUGIN_NAME ) );
 
 
 
 
 
 
75
  }
76
 
77
  // Flag to hold if file data has been processed
94
 
95
  // Check package.json file
96
  if ( false === is_file( ai1wm_package_path( $params ) ) ) {
97
+ throw new Ai1wm_Import_Exception( __( 'Please make sure that your file was exported using <strong>All-in-One WP Migration</strong> plugin. <a href="https://help.servmask.com/knowledgebase/invalid-backup-file/" target="_blank">Technical details</a>', AI1WM_PLUGIN_NAME ) );
 
 
 
 
 
 
98
  }
99
 
100
  // Set progress
lib/vendor/servmask/database/class-ai1wm-database.php CHANGED
@@ -1214,12 +1214,15 @@ abstract class Ai1wm_Database {
1214
  if ( empty( $search ) || empty( $replace ) ) {
1215
  if ( ! $this->wpdb->has_cap( 'utf8mb4_520' ) ) {
1216
  if ( ! $this->wpdb->has_cap( 'utf8mb4' ) ) {
1217
- $search = array( 'utf8mb4_unicode_520_ci', 'utf8mb4' );
1218
- $replace = array( 'utf8_unicode_ci', 'utf8' );
1219
  } else {
1220
- $search = array( 'utf8mb4_unicode_520_ci' );
1221
- $replace = array( 'utf8mb4_unicode_ci' );
1222
  }
 
 
 
1223
  }
1224
  }
1225
 
1214
  if ( empty( $search ) || empty( $replace ) ) {
1215
  if ( ! $this->wpdb->has_cap( 'utf8mb4_520' ) ) {
1216
  if ( ! $this->wpdb->has_cap( 'utf8mb4' ) ) {
1217
+ $search = array( 'utf8mb4_0900_ai_ci', 'utf8mb4_unicode_520_ci', 'utf8mb4' );
1218
+ $replace = array( 'utf8_unicode_ci', 'utf8_unicode_ci', 'utf8' );
1219
  } else {
1220
+ $search = array( 'utf8mb4_0900_ai_ci', 'utf8mb4_unicode_520_ci' );
1221
+ $replace = array( 'utf8mb4_unicode_ci', 'utf8mb4_unicode_ci' );
1222
  }
1223
+ } else {
1224
+ $search = array( 'utf8mb4_0900_ai_ci' );
1225
+ $replace = array( 'utf8mb4_unicode_520_ci' );
1226
  }
1227
  }
1228
 
lib/view/assets/javascript/backups.min.js CHANGED
@@ -708,7 +708,6 @@ var $ = jQuery;
708
 
709
  var Modal = function Modal() {
710
  var self = this;
711
- this.view = null;
712
 
713
  // Error Modal
714
  this.error = function (params) {
@@ -758,63 +757,65 @@ var Modal = function Modal() {
758
 
759
  // Progress Modal
760
  this.progress = function (params) {
761
- if (this.view === 'progress') {
762
- // Update progress bar meter
763
  this.progress.progressBarMeter.width(params.percent + '%');
 
764
 
765
- // Update progress bar percent
766
- this.progress.progressBarPercent.text(params.percent + '%');
767
- } else {
768
- // Create the modal container
769
- var container = $('<div></div>');
770
 
771
- // Create section to hold title, message and action
772
- var section = $('<section></section>');
773
 
774
- // Create header to hold progress bar
775
- var header = $('<h1></h1>');
776
 
777
- // Create action section
778
- var action = $('<div></div>');
779
 
780
- // Create progress bar
781
- var progressBar = $('<span class="ai1wm-progress-bar"></span>');
782
 
783
- // Create progress bar meter
784
- this.progress.progressBarMeter = $('<span class="ai1wm-progress-bar-meter"></span>').width(params.percent + '%');
785
 
786
- // Create progress bar percent
787
- this.progress.progressBarPercent = $('<span class="ai1wm-progress-bar-percent"></span>').text(params.percent + '%');
788
 
789
- // Create stop import
790
- var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
791
- $(this).attr('disabled', 'disabled');
792
- self.onStop();
793
- });
 
 
 
794
 
795
- // Append text to stop button
796
- stopButton.append('<i class="ai1wm-icon-notification"></i> ' + ai1wm_locale.stop_import);
797
 
798
- // Append progress meter and progress percent
799
- progressBar.append(this.progress.progressBarMeter).append(this.progress.progressBarPercent);
800
 
801
- // Append stop button to action
802
- action.append(stopButton);
803
 
804
- // Append progress bar to section
805
- header.append(progressBar);
806
 
807
- // Append header to section
808
- section.append(header);
809
 
810
- // Append section and action to container
811
- container.append(section).append(action);
812
 
813
- // Render modal
814
- self.modal.html(container).show();
815
- self.modal.focus();
816
- self.overlay.show();
817
- }
818
  };
819
 
820
  // Pro Modal
@@ -885,13 +886,13 @@ var Modal = function Modal() {
885
 
886
  // Create close button
887
  var closeButton = $('<button type="button" class="ai1wm-button-gray"></button>').on('click', function () {
888
- $(this).attr('disabled', 'disabled');
889
  self.onStop();
890
  });
891
 
892
  // Create confirm button
893
  var confirmButton = $('<button type="button" class="ai1wm-button-green"></button>').on('click', function () {
894
- $(this).attr('disabled', 'disabled');
895
  self.onConfirm();
896
  });
897
 
@@ -925,7 +926,11 @@ var Modal = function Modal() {
925
  // Blogs Modal
926
  this.blogs = function (params) {
927
  // Create the modal container
928
- var container = $('<form></form>');
 
 
 
 
929
 
930
  // Create section to hold title, message and action
931
  var section = $('<section></section>');
@@ -943,10 +948,7 @@ var Modal = function Modal() {
943
  var title = $('<span></span>').addClass('ai1wm-title-grey').text(params.title);
944
 
945
  // Create continue button
946
- var continueButton = $('<button type="button" class="ai1wm-button-green"></button>').on('click', function () {
947
- $(this).attr('disabled', 'disabled');
948
- self.onBlogs($(this).closest('form').serializeArray());
949
- });
950
 
951
  // Append text to continue button
952
  continueButton.append(ai1wm_locale.continue_import);
@@ -1073,6 +1075,8 @@ var Modal = function Modal() {
1073
  };
1074
 
1075
  Modal.prototype.render = function (params) {
 
 
1076
  // Show modal
1077
  switch (params.type) {
1078
  case 'pro':
@@ -1103,8 +1107,6 @@ Modal.prototype.render = function (params) {
1103
  this.done(params);
1104
  break;
1105
  }
1106
-
1107
- this.view = params.type;
1108
  };
1109
 
1110
  Modal.prototype.destroy = function () {
@@ -1471,7 +1473,7 @@ var Modal = function Modal() {
1471
 
1472
  // Create stop export
1473
  var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
1474
- $(this).attr('disabled', 'disabled');
1475
  self.onStop();
1476
  });
1477
 
708
 
709
  var Modal = function Modal() {
710
  var self = this;
 
711
 
712
  // Error Modal
713
  this.error = function (params) {
757
 
758
  // Progress Modal
759
  this.progress = function (params) {
760
+ // Update progress bar meter
761
+ if (this.progress.progressBarMeter) {
762
  this.progress.progressBarMeter.width(params.percent + '%');
763
+ }
764
 
765
+ // Update progress bar percent
766
+ if (this.progress.progressBarPercent) {
767
+ return this.progress.progressBarPercent.text(params.percent + '%');
768
+ }
 
769
 
770
+ // Create the modal container
771
+ var container = $('<div></div>');
772
 
773
+ // Create section to hold title, message and action
774
+ var section = $('<section></section>');
775
 
776
+ // Create header to hold progress bar
777
+ var header = $('<h1></h1>');
778
 
779
+ // Create action section
780
+ var action = $('<div></div>');
781
 
782
+ // Create progress bar
783
+ var progressBar = $('<span class="ai1wm-progress-bar"></span>');
784
 
785
+ // Create progress bar meter
786
+ this.progress.progressBarMeter = $('<span class="ai1wm-progress-bar-meter"></span>').width(params.percent + '%');
787
 
788
+ // Create progress bar percent
789
+ this.progress.progressBarPercent = $('<span class="ai1wm-progress-bar-percent"></span>').text(params.percent + '%');
790
+
791
+ // Create stop import
792
+ var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
793
+ stopButton.attr('disabled', 'disabled');
794
+ self.onStop();
795
+ });
796
 
797
+ // Append text to stop button
798
+ stopButton.append('<i class="ai1wm-icon-notification"></i> ' + ai1wm_locale.stop_import);
799
 
800
+ // Append progress meter and progress percent
801
+ progressBar.append(this.progress.progressBarMeter).append(this.progress.progressBarPercent);
802
 
803
+ // Append stop button to action
804
+ action.append(stopButton);
805
 
806
+ // Append progress bar to section
807
+ header.append(progressBar);
808
 
809
+ // Append header to section
810
+ section.append(header);
811
 
812
+ // Append section and action to container
813
+ container.append(section).append(action);
814
 
815
+ // Render modal
816
+ self.modal.html(container).show();
817
+ self.modal.focus();
818
+ self.overlay.show();
 
819
  };
820
 
821
  // Pro Modal
886
 
887
  // Create close button
888
  var closeButton = $('<button type="button" class="ai1wm-button-gray"></button>').on('click', function () {
889
+ closeButton.attr('disabled', 'disabled');
890
  self.onStop();
891
  });
892
 
893
  // Create confirm button
894
  var confirmButton = $('<button type="button" class="ai1wm-button-green"></button>').on('click', function () {
895
+ confirmButton.attr('disabled', 'disabled');
896
  self.onConfirm();
897
  });
898
 
926
  // Blogs Modal
927
  this.blogs = function (params) {
928
  // Create the modal container
929
+ var container = $('<form></form>').on('submit', function (e) {
930
+ e.preventDefault();
931
+ continueButton.attr('disabled', 'disabled');
932
+ self.onBlogs(container.serializeArray());
933
+ });
934
 
935
  // Create section to hold title, message and action
936
  var section = $('<section></section>');
948
  var title = $('<span></span>').addClass('ai1wm-title-grey').text(params.title);
949
 
950
  // Create continue button
951
+ var continueButton = $('<button type="submit" class="ai1wm-button-green"></button>');
 
 
 
952
 
953
  // Append text to continue button
954
  continueButton.append(ai1wm_locale.continue_import);
1075
  };
1076
 
1077
  Modal.prototype.render = function (params) {
1078
+ $(document).trigger('ai1wm-import-status', params);
1079
+
1080
  // Show modal
1081
  switch (params.type) {
1082
  case 'pro':
1107
  this.done(params);
1108
  break;
1109
  }
 
 
1110
  };
1111
 
1112
  Modal.prototype.destroy = function () {
1473
 
1474
  // Create stop export
1475
  var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
1476
+ stopButton.attr('disabled', 'disabled');
1477
  self.onStop();
1478
  });
1479
 
lib/view/assets/javascript/export.min.js CHANGED
@@ -669,7 +669,7 @@ var Modal = function Modal() {
669
 
670
  // Create stop export
671
  var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
672
- $(this).attr('disabled', 'disabled');
673
  self.onStop();
674
  });
675
 
669
 
670
  // Create stop export
671
  var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
672
+ stopButton.attr('disabled', 'disabled');
673
  self.onStop();
674
  });
675
 
lib/view/assets/javascript/import.min.js CHANGED
@@ -708,7 +708,6 @@ var $ = jQuery;
708
 
709
  var Modal = function Modal() {
710
  var self = this;
711
- this.view = null;
712
 
713
  // Error Modal
714
  this.error = function (params) {
@@ -758,63 +757,65 @@ var Modal = function Modal() {
758
 
759
  // Progress Modal
760
  this.progress = function (params) {
761
- if (this.view === 'progress') {
762
- // Update progress bar meter
763
  this.progress.progressBarMeter.width(params.percent + '%');
 
764
 
765
- // Update progress bar percent
766
- this.progress.progressBarPercent.text(params.percent + '%');
767
- } else {
768
- // Create the modal container
769
- var container = $('<div></div>');
770
 
771
- // Create section to hold title, message and action
772
- var section = $('<section></section>');
773
 
774
- // Create header to hold progress bar
775
- var header = $('<h1></h1>');
776
 
777
- // Create action section
778
- var action = $('<div></div>');
 
 
 
779
 
780
- // Create progress bar
781
- var progressBar = $('<span class="ai1wm-progress-bar"></span>');
782
 
783
- // Create progress bar meter
784
- this.progress.progressBarMeter = $('<span class="ai1wm-progress-bar-meter"></span>').width(params.percent + '%');
785
 
786
- // Create progress bar percent
787
- this.progress.progressBarPercent = $('<span class="ai1wm-progress-bar-percent"></span>').text(params.percent + '%');
788
 
789
- // Create stop import
790
- var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
791
- $(this).attr('disabled', 'disabled');
792
- self.onStop();
793
- });
794
 
795
- // Append text to stop button
796
- stopButton.append('<i class="ai1wm-icon-notification"></i> ' + ai1wm_locale.stop_import);
797
 
798
- // Append progress meter and progress percent
799
- progressBar.append(this.progress.progressBarMeter).append(this.progress.progressBarPercent);
800
 
801
- // Append stop button to action
802
- action.append(stopButton);
803
 
804
- // Append progress bar to section
805
- header.append(progressBar);
806
 
807
- // Append header to section
808
- section.append(header);
809
 
810
- // Append section and action to container
811
- container.append(section).append(action);
812
 
813
- // Render modal
814
- self.modal.html(container).show();
815
- self.modal.focus();
816
- self.overlay.show();
817
- }
818
  };
819
 
820
  // Pro Modal
@@ -885,13 +886,13 @@ var Modal = function Modal() {
885
 
886
  // Create close button
887
  var closeButton = $('<button type="button" class="ai1wm-button-gray"></button>').on('click', function () {
888
- $(this).attr('disabled', 'disabled');
889
  self.onStop();
890
  });
891
 
892
  // Create confirm button
893
  var confirmButton = $('<button type="button" class="ai1wm-button-green"></button>').on('click', function () {
894
- $(this).attr('disabled', 'disabled');
895
  self.onConfirm();
896
  });
897
 
@@ -925,7 +926,11 @@ var Modal = function Modal() {
925
  // Blogs Modal
926
  this.blogs = function (params) {
927
  // Create the modal container
928
- var container = $('<form></form>');
 
 
 
 
929
 
930
  // Create section to hold title, message and action
931
  var section = $('<section></section>');
@@ -943,10 +948,7 @@ var Modal = function Modal() {
943
  var title = $('<span></span>').addClass('ai1wm-title-grey').text(params.title);
944
 
945
  // Create continue button
946
- var continueButton = $('<button type="button" class="ai1wm-button-green"></button>').on('click', function () {
947
- $(this).attr('disabled', 'disabled');
948
- self.onBlogs($(this).closest('form').serializeArray());
949
- });
950
 
951
  // Append text to continue button
952
  continueButton.append(ai1wm_locale.continue_import);
@@ -1073,6 +1075,8 @@ var Modal = function Modal() {
1073
  };
1074
 
1075
  Modal.prototype.render = function (params) {
 
 
1076
  // Show modal
1077
  switch (params.type) {
1078
  case 'pro':
@@ -1103,8 +1107,6 @@ Modal.prototype.render = function (params) {
1103
  this.done(params);
1104
  break;
1105
  }
1106
-
1107
- this.view = params.type;
1108
  };
1109
 
1110
  Modal.prototype.destroy = function () {
708
 
709
  var Modal = function Modal() {
710
  var self = this;
 
711
 
712
  // Error Modal
713
  this.error = function (params) {
757
 
758
  // Progress Modal
759
  this.progress = function (params) {
760
+ // Update progress bar meter
761
+ if (this.progress.progressBarMeter) {
762
  this.progress.progressBarMeter.width(params.percent + '%');
763
+ }
764
 
765
+ // Update progress bar percent
766
+ if (this.progress.progressBarPercent) {
767
+ return this.progress.progressBarPercent.text(params.percent + '%');
768
+ }
 
769
 
770
+ // Create the modal container
771
+ var container = $('<div></div>');
772
 
773
+ // Create section to hold title, message and action
774
+ var section = $('<section></section>');
775
 
776
+ // Create header to hold progress bar
777
+ var header = $('<h1></h1>');
778
+
779
+ // Create action section
780
+ var action = $('<div></div>');
781
 
782
+ // Create progress bar
783
+ var progressBar = $('<span class="ai1wm-progress-bar"></span>');
784
 
785
+ // Create progress bar meter
786
+ this.progress.progressBarMeter = $('<span class="ai1wm-progress-bar-meter"></span>').width(params.percent + '%');
787
 
788
+ // Create progress bar percent
789
+ this.progress.progressBarPercent = $('<span class="ai1wm-progress-bar-percent"></span>').text(params.percent + '%');
790
 
791
+ // Create stop import
792
+ var stopButton = $('<button type="button" class="ai1wm-button-red"></button>').on('click', function () {
793
+ stopButton.attr('disabled', 'disabled');
794
+ self.onStop();
795
+ });
796
 
797
+ // Append text to stop button
798
+ stopButton.append('<i class="ai1wm-icon-notification"></i> ' + ai1wm_locale.stop_import);
799
 
800
+ // Append progress meter and progress percent
801
+ progressBar.append(this.progress.progressBarMeter).append(this.progress.progressBarPercent);
802
 
803
+ // Append stop button to action
804
+ action.append(stopButton);
805
 
806
+ // Append progress bar to section
807
+ header.append(progressBar);
808
 
809
+ // Append header to section
810
+ section.append(header);
811
 
812
+ // Append section and action to container
813
+ container.append(section).append(action);
814
 
815
+ // Render modal
816
+ self.modal.html(container).show();
817
+ self.modal.focus();
818
+ self.overlay.show();
 
819
  };
820
 
821
  // Pro Modal
886
 
887
  // Create close button
888
  var closeButton = $('<button type="button" class="ai1wm-button-gray"></button>').on('click', function () {
889
+ closeButton.attr('disabled', 'disabled');
890
  self.onStop();
891
  });
892
 
893
  // Create confirm button
894
  var confirmButton = $('<button type="button" class="ai1wm-button-green"></button>').on('click', function () {
895
+ confirmButton.attr('disabled', 'disabled');
896
  self.onConfirm();
897
  });
898
 
926
  // Blogs Modal
927
  this.blogs = function (params) {
928
  // Create the modal container
929
+ var container = $('<form></form>').on('submit', function (e) {
930
+ e.preventDefault();
931
+ continueButton.attr('disabled', 'disabled');
932
+ self.onBlogs(container.serializeArray());
933
+ });
934
 
935
  // Create section to hold title, message and action
936
  var section = $('<section></section>');
948
  var title = $('<span></span>').addClass('ai1wm-title-grey').text(params.title);
949
 
950
  // Create continue button
951
+ var continueButton = $('<button type="submit" class="ai1wm-button-green"></button>');
 
 
 
952
 
953
  // Append text to continue button
954
  continueButton.append(ai1wm_locale.continue_import);
1075
  };
1076
 
1077
  Modal.prototype.render = function (params) {
1078
+ $(document).trigger('ai1wm-import-status', params);
1079
+
1080
  // Show modal
1081
  switch (params.type) {
1082
  case 'pro':
1107
  this.done(params);
1108
  break;
1109
  }
 
 
1110
  };
1111
 
1112
  Modal.prototype.destroy = function () {
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: move, transfer, copy, migrate, backup, clone, restore, db migration, wordp
4
  Requires at least: 3.3
5
  Tested up to: 5.3
6
  Requires PHP: 5.2.17
7
- Stable tag: 7.10
8
  License: GPLv2 or later
9
 
10
  Move, transfer, copy, migrate, and backup a site with 1-click. Quick, easy, and reliable.
@@ -108,6 +108,12 @@ Alternatively you can download the plugin using the download button on this page
108
  All-in-One WP Migration **asks for your consent** to collect **requester's email address** when filling plugin's contact form. [GDPR Compliant Privacy Policy](https://www.iubenda.com/privacy-policy/946881)
109
 
110
  == Changelog ==
 
 
 
 
 
 
111
  = 7.10 =
112
  **Added**
113
 
4
  Requires at least: 3.3
5
  Tested up to: 5.3
6
  Requires PHP: 5.2.17
7
+ Stable tag: 7.11
8
  License: GPLv2 or later
9
 
10
  Move, transfer, copy, migrate, and backup a site with 1-click. Quick, easy, and reliable.
108
  All-in-One WP Migration **asks for your consent** to collect **requester's email address** when filling plugin's contact form. [GDPR Compliant Privacy Policy](https://www.iubenda.com/privacy-policy/946881)
109
 
110
  == Changelog ==
111
+ = 7.11 =
112
+ **Added**
113
+
114
+ * Support for Webba Booking plugin
115
+ * Support for utf8mb4_0900_ai_ci collation in MySQL v8.0.1+
116
+
117
  = 7.10 =
118
  **Added**
119