WP Staging – DB & File Duplicator & Migration - Version 2.6.2

Version Description

  • Fix: Do not show warning "Preparing Data Step3: Failed to update rewrite_rules in wpstg0_options"
  • Fix: Change error "Table wpstgtmp_options does not exist" to warning
  • New: Add arguments for hook wpstg_cloning_complete
  • New: Setup server environment variables per process and not globally (e.g. set_time_limit)
  • New: Add support for custom uploads folder if user customized UPLOADS constant or upload_path in DB
Download this release

Release Info

Developer ReneHermi
Plugin Icon 128x128 WP Staging – DB & File Duplicator & Migration
Version 2.6.2
Comparing to
See all releases

Code changes from version 2.6.1 to 2.6.2

apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -176,7 +176,7 @@ class Data extends JobExecutable {
176
  */
177
  protected function isTable( $table ) {
178
  if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
179
- $this->log( "Preparing Data: Table {$table} does not exist.", Logger::TYPE_ERROR );
180
  return false;
181
  }
182
  return true;
@@ -411,7 +411,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
411
  //}
412
  // All good
413
  if( $insert ) {
414
- $this->log( "Preparing Data Step2: Successfull", Logger::TYPE_INFO );
415
  return true;
416
  }
417
 
@@ -450,7 +450,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
450
  return true;
451
  }
452
 
453
- $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
454
  return true;
455
  }
456
 
@@ -614,6 +614,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
614
 
615
  // Skip - Table does not exist
616
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
 
617
  return true;
618
  }
619
 
@@ -900,21 +901,24 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
900
  return true;
901
  }
902
 
903
- $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
904
-
905
- $this->log( "Preparing Data Step13: Updating upload_path in {$this->prefix}options. {$error}" );
906
 
 
 
 
 
 
907
  $updateOptions = $this->db->query(
908
  $this->db->prepare(
909
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
910
  )
911
  );
912
 
913
  if( false === $updateOptions ) {
914
- $this->log( "Preparing Data Step13: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
915
  return true;
916
  }
917
- $this->Log( "Preparing Data Step 13: Finished successfully" );
918
  return true;
919
  }
920
 
@@ -1043,34 +1047,81 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
1043
  * Remove UPLOADS constant in wp-config.php to reset default image folder
1044
  * @return bool
1045
  */
1046
- protected function step17() {
1047
- $path = $this->options->destinationDir . "wp-config.php";
1048
-
1049
- $this->log( "Preparing Data Step17: Remove UPLOADS in wp-config.php" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1050
 
 
 
 
 
 
 
 
 
1051
  if( false === ($content = file_get_contents( $path )) ) {
1052
  $this->log( "Preparing Data Step17: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
1053
  return false;
1054
  }
1055
-
1056
-
1057
- // Get UPLOADS from wp-config.php
1058
  preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
1059
-
1060
  if( !empty( $matches[0] ) ) {
1061
-
1062
  $pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
1063
-
1064
- $replace = "";
1065
-
1066
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
1067
- $this->log( "Preparing Data: Failed to change UPLOADS", Logger::TYPE_ERROR );
1068
  return false;
1069
  }
1070
  } else {
1071
- $this->log( "Preparing Data Step17: UPLOADS not defined in wp-config.php. Skipping this step." );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1072
  }
1073
-
1074
  if( false === @file_put_contents( $path, $content ) ) {
1075
  $this->log( "Preparing Data Step17: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
1076
  return false;
176
  */
177
  protected function isTable( $table ) {
178
  if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
179
+ $this->log( "Preparing Data: Table {$table} does not exist.", Logger::TYPE_INFO );
180
  return false;
181
  }
182
  return true;
411
  //}
412
  // All good
413
  if( $insert ) {
414
+ $this->log( "Preparing Data Step2: Successful", Logger::TYPE_INFO );
415
  return true;
416
  }
417
 
450
  return true;
451
  }
452
 
453
+ //$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
454
  return true;
455
  }
456
 
614
 
615
  // Skip - Table does not exist
616
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
617
+ $this->log( "Preparing Data Step8: Skipping" );
618
  return true;
619
  }
620
 
901
  return true;
902
  }
903
 
904
+ $error = !empty( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
 
 
905
 
906
+ // $updateOptions = $this->db->query(
907
+ // $this->db->prepare(
908
+ // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
909
+ // )
910
+ // );
911
  $updateOptions = $this->db->query(
912
  $this->db->prepare(
913
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", ""
914
  )
915
  );
916
 
917
  if( false === $updateOptions ) {
918
+ $this->log( "Preparing Data Step13: Did not update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_INFO );
919
  return true;
920
  }
921
+ $this->Log( "Preparing Data Step13: Finished successfully" );
922
  return true;
923
  }
924
 
1047
  * Remove UPLOADS constant in wp-config.php to reset default image folder
1048
  * @return bool
1049
  */
1050
+ // protected function step17() {
1051
+ // $path = $this->options->destinationDir . "wp-config.php";
1052
+ //
1053
+ // $this->log( "Preparing Data Step17: Remove UPLOADS in wp-config.php" );
1054
+ //
1055
+ // if( false === ($content = file_get_contents( $path )) ) {
1056
+ // $this->log( "Preparing Data Step17: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
1057
+ // return false;
1058
+ // }
1059
+ //
1060
+ //
1061
+ // // Get UPLOADS from wp-config.php
1062
+ // preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
1063
+ //
1064
+ // if( !empty( $matches[0] ) ) {
1065
+ //
1066
+ // $pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
1067
+ //
1068
+ // $replace = "";
1069
+ //
1070
+ // if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
1071
+ // $this->log( "Preparing Data: Failed to change UPLOADS", Logger::TYPE_ERROR );
1072
+ // return false;
1073
+ // }
1074
+ // } else {
1075
+ // $this->log( "Preparing Data Step17: UPLOADS not defined in wp-config.php. Skipping this step." );
1076
+ // }
1077
+ //
1078
+ // if( false === @file_put_contents( $path, $content ) ) {
1079
+ // $this->log( "Preparing Data Step17: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
1080
+ // return false;
1081
+ // }
1082
+ // $this->Log( "Preparing Data Step17: Finished successfully" );
1083
+ // return true;
1084
+ // }
1085
 
1086
+ /**
1087
+ * Add UPLOADS constant in wp-config.php or change it to correct destination.
1088
+ * This is important when a custom uploads folder is used
1089
+ * @return bool
1090
+ */
1091
+ protected function step17() {
1092
+ $path = $this->options->destinationDir . "wp-config.php";
1093
+ $this->log( "Preparing Data Step17: Update UPLOADS constant in wp-config.php" );
1094
  if( false === ($content = file_get_contents( $path )) ) {
1095
  $this->log( "Preparing Data Step17: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
1096
  return false;
1097
  }
1098
+ // Get UPLOADS from wp-config.php if there is already one
 
 
1099
  preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
1100
+ $uploadFolder = wpstg_get_rel_upload_dir();
1101
  if( !empty( $matches[0] ) ) {
 
1102
  $pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
1103
+ $replace = "define('UPLOADS', '" . $uploadFolder . "');";
 
 
1104
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
1105
+ $this->log( "Preparing Data Step17: Failed to change UPLOADS", Logger::TYPE_ERROR );
1106
  return false;
1107
  }
1108
  } else {
1109
+ $this->log( "Preparing Data Step17: UPLOADS not defined in wp-config.php. Creating new entry." );
1110
+ // Find line with ABSPATH and add UPLOADS constant above
1111
+ preg_match( "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/", $content, $matches );
1112
+ if( !empty( $matches[0] ) ) {
1113
+ $matches[0];
1114
+ $pattern = "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/";
1115
+ $replace = "define('UPLOADS', '" . $uploadFolder . "'); \n" .
1116
+ "if ( ! defined( 'ABSPATH' ) )";
1117
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
1118
+ $this->log( "Preparing Data Step 17: Failed to change UPLOADS", Logger::TYPE_ERROR );
1119
+ return false;
1120
+ }
1121
+ } else {
1122
+ $this->log( "Preparing Data Step 17: Can not add UPLOAD constant to wp-config.php. Can not find free position to add it.", Logger::TYPE_ERROR );
1123
+ }
1124
  }
 
1125
  if( false === @file_put_contents( $path, $content ) ) {
1126
  $this->log( "Preparing Data Step17: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
1127
  return false;
apps/Backend/Modules/Jobs/Delete.php CHANGED
@@ -152,12 +152,15 @@ class Delete extends Job {
152
 
153
  $this->tables = array();
154
 
 
 
155
  foreach ( $tables as $table ) {
156
  $this->tables[] = array(
157
  "name" => $table->Name,
158
  "size" => $this->formatSize( ($table->Data_length + $table->Index_length ) )
159
  );
160
  }
 
161
 
162
  $this->tables = json_decode( json_encode( $this->tables ) );
163
  }
@@ -310,10 +313,13 @@ class Delete extends Job {
310
  */
311
  public function deleteTables() {
312
  if( $this->isOverThreshold() ) {
 
313
  return;
314
  }
315
 
316
- foreach ( $this->getTablesToRemove() as $table ) {
 
 
317
  // PROTECTION: Never delete any table that beginns with wp prefix of live site
318
  if( !$this->isExternalDatabase() && $this->startsWith( $table, $this->wpdb->prefix ) ) {
319
  $this->log( "Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL );
@@ -374,6 +380,7 @@ class Delete extends Job {
374
  return;
375
  }
376
 
 
377
  $di = new \RecursiveDirectoryIterator( $this->deleteDir, \FilesystemIterator::SKIP_DOTS );
378
  $ri = new \RecursiveIteratorIterator( $di, \RecursiveIteratorIterator::CHILD_FIRST );
379
  foreach ( $ri as $file ) {
@@ -383,27 +390,43 @@ class Delete extends Job {
383
  return;
384
  }
385
  }
 
386
 
387
  // Delete left over staging site root folder
388
  @rmdir( $this->deleteDir );
389
 
390
- // Throw fatal error if the folder is still not deleted
391
- if( is_dir($this->deleteDir) ) {
392
  $clone = ( string ) $this->clone->path;
393
  $response = array(
394
  'job' => 'delete',
395
  'status' => true,
396
  'delete' => 'finished',
397
- 'message' => "Could not the staging site entirely. The folder {$clone}is still not empty. <br/><br/> If this happens again please contact us at support@wp-staging.com",
398
  'error' => true,
399
  );
400
  wp_die( json_encode( $response ) );
401
  }
402
 
403
- // Successfull finish deleting job
404
  return $this->deleteFinish();
405
  }
406
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  /**
408
  * Delete file
409
  * @param object iterator $file
152
 
153
  $this->tables = array();
154
 
155
+ // no results
156
+ if( null !== $tables ) {
157
  foreach ( $tables as $table ) {
158
  $this->tables[] = array(
159
  "name" => $table->Name,
160
  "size" => $this->formatSize( ($table->Data_length + $table->Index_length ) )
161
  );
162
  }
163
+ }
164
 
165
  $this->tables = json_decode( json_encode( $this->tables ) );
166
  }
313
  */
314
  public function deleteTables() {
315
  if( $this->isOverThreshold() ) {
316
+ $this->log( "Deleting: Is over threshold", Logger::TYPE_INFO );
317
  return;
318
  }
319
 
320
+ $tables = $this->getTablesToRemove();
321
+
322
+ foreach ( $tables as $table ) {
323
  // PROTECTION: Never delete any table that beginns with wp prefix of live site
324
  if( !$this->isExternalDatabase() && $this->startsWith( $table, $this->wpdb->prefix ) ) {
325
  $this->log( "Fatal Error: Trying to delete table {$table} of main WP installation!", Logger::TYPE_CRITICAL );
380
  return;
381
  }
382
 
383
+ if( $this->isNotEmpty( $this->deleteDir ) ) {
384
  $di = new \RecursiveDirectoryIterator( $this->deleteDir, \FilesystemIterator::SKIP_DOTS );
385
  $ri = new \RecursiveIteratorIterator( $di, \RecursiveIteratorIterator::CHILD_FIRST );
386
  foreach ( $ri as $file ) {
390
  return;
391
  }
392
  }
393
+ }
394
 
395
  // Delete left over staging site root folder
396
  @rmdir( $this->deleteDir );
397
 
398
+ // Throw fatal error if the folder has still not been deleted and there are files in it
399
+ if( $this->isNotEmpty( $this->deleteDir ) ) {
400
  $clone = ( string ) $this->clone->path;
401
  $response = array(
402
  'job' => 'delete',
403
  'status' => true,
404
  'delete' => 'finished',
405
+ 'message' => "Could not delete the entire staging site. The folder {$clone} still exists and is not empty. <br/> Try to empty this folder manually by using FTP or file manager plugin and then try to delete again the staging site here.<br/> If this happens again please contact us at support@wp-staging.com",
406
  'error' => true,
407
  );
408
  wp_die( json_encode( $response ) );
409
  }
410
 
411
+ // Successful finish deleting job
412
  return $this->deleteFinish();
413
  }
414
 
415
+ /**
416
+ * Check if directory exists and is not empty
417
+ * @param string $dir
418
+ * @return bool
419
+ */
420
+ private function isNotEmpty( $dir ) {
421
+ // Throw fatal error if the folder has still not been deleted and there are files in it
422
+ $isDirNotEmpty = false;
423
+ if( is_dir( $dir ) ) {
424
+ $iterator = new \FilesystemIterator( $dir );
425
+ $isDirNotEmpty = $iterator->valid();
426
+ }
427
+ return $isDirNotEmpty;
428
+ }
429
+
430
  /**
431
  * Delete file
432
  * @param object iterator $file
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -158,7 +158,7 @@ class Files extends JobExecutable {
158
  */
159
  private function isFinished() {
160
  return (
161
- !isset($this->options->isRunning) ||
162
  $this->options->currentStep > $this->options->totalSteps ||
163
  $this->options->copiedFiles >= $this->options->totalFiles
164
  );
@@ -248,25 +248,57 @@ class Files extends JobExecutable {
248
  *
249
  * @return string
250
  */
251
- protected function getWpContentPath( $file ) {
252
- // Get absolute custom upload dir
253
- $uploads = wp_upload_dir();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
- // Get absolute upload dir from ABSPATH
 
 
 
 
 
 
 
 
 
 
256
  $uploadsAbsPath = trailingslashit( $uploads['basedir'] );
257
 
258
- // Get absolute custom wp-content dir
259
- $wpContentDir = trailingslashit( WP_CONTENT_DIR );
260
-
261
- // Check if there is a custom upload directory and do a search $ replace
262
- $file = str_replace( $uploadsAbsPath, ABSPATH . 'wp-content/uploads/', $file, $count );
263
-
 
 
 
 
 
264
  // If there is no custom upload directory do a search & replace of the custom wp-content directory
265
  if( empty( $count ) || $count === 0 ) {
 
266
  $file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
267
  }
268
-
269
-
270
  return $file;
271
  }
272
 
@@ -294,8 +326,11 @@ class Files extends JobExecutable {
294
  // Get custom wp-content and uploads folder
295
  $file = $this->getWpContentPath( $file );
296
 
 
297
  $relativePath = str_replace( \WPStaging\WPStaging::getWPpath(), null, $file );
 
298
  $destinationPath = $this->destination . $relativePath;
 
299
  $destinationDirectory = dirname( $destinationPath );
300
 
301
  if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, wpstg_get_permissions_for_directory(), true ) ) {
@@ -434,4 +469,4 @@ class Files extends JobExecutable {
434
  return false;
435
  }
436
 
437
- }
158
  */
159
  private function isFinished() {
160
  return (
161
+ !isset( $this->options->isRunning ) ||
162
  $this->options->currentStep > $this->options->totalSteps ||
163
  $this->options->copiedFiles >= $this->options->totalFiles
164
  );
248
  *
249
  * @return string
250
  */
251
+ // protected function getWpContentPath( $file ) {
252
+ // // Get absolute custom upload dir
253
+ // $uploads = wp_upload_dir();
254
+ //
255
+ // // Get absolute upload dir from ABSPATH
256
+ // $uploadsAbsPath = trailingslashit( $uploads['basedir'] );
257
+ //
258
+ // // Get absolute custom wp-content dir
259
+ // $wpContentDir = trailingslashit( WP_CONTENT_DIR );
260
+ //
261
+ // // Check if there is a custom upload directory and do a search $ replace
262
+ // $file = str_replace( $uploadsAbsPath, ABSPATH . 'wp-content/uploads/', $file, $count );
263
+ //
264
+ // // If there is no custom upload directory do a search & replace of the custom wp-content directory
265
+ // if( empty( $count ) || $count === 0 ) {
266
+ // $file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
267
+ // }
268
+ //
269
+ //
270
+ // return $file;
271
+ // }
272
 
273
+ /**
274
+ * Get wp-content and wp-content/uploads destination dir
275
+ * Necessary if these folders were customized and changed from the default ones.
276
+ *
277
+ * @return string
278
+ */
279
+ protected function getWpContentPath( $file ) {
280
+ // Get upload directory information
281
+ $uploads = wp_upload_dir();
282
+
283
+ // Get absolute path to wordpress uploads directory e.g srv/www/htdocs/sitename/wp-content/uploads
284
  $uploadsAbsPath = trailingslashit( $uploads['basedir'] );
285
 
286
+ // Get relative path to the uploads folder, e.g assets
287
+ $uploadsRelPath = wpstg_get_rel_upload_dir();
288
+
289
+ // Get absolute path to wp-content directory e.g srv/www/htdocs/sitename/wp-content
290
+ $wpContentDir = trailingslashit( WP_CONTENT_DIR );
291
+
292
+ // Check if there is a custom uploads directory, then do a search $ replace. Do this only if custom upload path is not identical to WP_CONTENT_DIR
293
+ if( $uploadsAbsPath != $wpContentDir ) {
294
+ //$file = str_replace( $uploadsAbsPath, ABSPATH . 'wp-content/uploads/', $file, $count );
295
+ $file = str_replace( $uploadsAbsPath, ABSPATH . $uploadsRelPath, $file, $count );
296
+ }
297
  // If there is no custom upload directory do a search & replace of the custom wp-content directory
298
  if( empty( $count ) || $count === 0 ) {
299
+ //$file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
300
  $file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
301
  }
 
 
302
  return $file;
303
  }
304
 
326
  // Get custom wp-content and uploads folder
327
  $file = $this->getWpContentPath( $file );
328
 
329
+ // remove ABSPATH and get last part of the path
330
  $relativePath = str_replace( \WPStaging\WPStaging::getWPpath(), null, $file );
331
+ // add destination path
332
  $destinationPath = $this->destination . $relativePath;
333
+ // get folder
334
  $destinationDirectory = dirname( $destinationPath );
335
 
336
  if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, wpstg_get_permissions_for_directory(), true ) ) {
469
  return false;
470
  }
471
 
472
+ }
apps/Backend/Modules/Jobs/Finish.php CHANGED
@@ -46,7 +46,7 @@ class Finish extends Job {
46
  );
47
 
48
  //$this->flush();
49
- do_action('wpstg_cloning_complete');
50
 
51
 
52
  return ( object ) $return;
46
  );
47
 
48
  //$this->flush();
49
+ do_action('wpstg_cloning_complete', $this->options);
50
 
51
 
52
  return ( object ) $return;
apps/Backend/Modules/Jobs/JobExecutable.php CHANGED
@@ -1,41 +1,41 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules\Jobs;
3
 
4
  // No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
  die;
8
  }
9
 
10
-
11
  /**
12
  * Class JobExecutable
13
  * I'm sorry for such mess, we need to support PHP 5.3
14
  * @package WPStaging\Backend\Modules\Jobs
15
  */
16
- abstract class JobExecutable extends Job
17
- {
18
 
19
  /**
20
  * @var array
21
  */
22
  protected $response = array(
23
- "status" => false,
24
- "percentage" => 0,
25
- "total" => 0,
26
- "step" => 0,
27
- "last_msg" => '',
28
  );
29
 
30
  /**
31
  * JobExecutable constructor.
32
  */
33
- public function __construct()
34
- {
35
  parent::__construct();
36
 
37
  // Calculate total steps
38
  $this->calculateTotalSteps();
 
 
 
39
  }
40
 
41
  /**
@@ -44,28 +44,26 @@ abstract class JobExecutable extends Job
44
  * @param bool $incrementCurrentStep
45
  * @return array
46
  */
47
- protected function prepareResponse($status = false, $incrementCurrentStep = true)
48
- {
49
- if ($incrementCurrentStep)
50
- {
51
  $this->options->currentStep++;
52
  }
53
 
54
  $percentage = 0;
55
- if (isset($this->options->currentStep) && isset($this->options->totalSteps) && $this->options->totalSteps > 0){
56
- $percentage = round(($this->options->currentStep / $this->options->totalSteps) * 100);
57
- $percentage = (100 < $percentage) ? 100 : $percentage;
58
  }
59
 
60
  return $this->response = array(
61
- "status" => $status,
62
- "percentage" => $percentage,
63
- "total" => $this->options->totalSteps,
64
- "step" => $this->options->currentStep,
65
- "job" => $this->options->currentJob,
66
- "last_msg" => $this->logger->getLastLogMsg(),
67
- "running_time" => $this->time() - time(),
68
- "job_done" => $status
69
  );
70
  }
71
 
@@ -73,35 +71,31 @@ abstract class JobExecutable extends Job
73
  * Start Module
74
  * @return object
75
  */
76
- public function start()
77
- {
78
  // Execute steps
79
  $this->run();
80
 
81
  // Save option, progress
82
  $this->saveOptions();
83
 
84
- return (object) $this->response;
85
  }
86
-
87
- /**
88
  * Run Steps
89
  */
90
- protected function run()
91
- {
92
  // Execute steps
93
- for ($i = 0; $i < $this->options->totalSteps; $i++)
94
- {
95
  // Job is finished or over threshold limits was hit
96
- if (!$this->execute())
97
- {
98
  break;
99
  }
100
  // Return after every step to create lower batches
101
  // This also gets a smoother progress bar and to a less consumptive php cpu load
102
  // This decrease performance tremendous but also lowers memory consumption
103
- if ($this->settings->cpuLoad === 'low'){
104
- return (object) $this->response;
105
  }
106
  }
107
  }
@@ -118,4 +112,4 @@ abstract class JobExecutable extends Job
118
  * @return bool
119
  */
120
  abstract protected function execute();
121
- }
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
 
7
  die;
8
  }
9
 
 
10
  /**
11
  * Class JobExecutable
12
  * I'm sorry for such mess, we need to support PHP 5.3
13
  * @package WPStaging\Backend\Modules\Jobs
14
  */
15
+ abstract class JobExecutable extends Job {
 
16
 
17
  /**
18
  * @var array
19
  */
20
  protected $response = array(
21
+ "status" => false,
22
+ "percentage" => 0,
23
+ "total" => 0,
24
+ "step" => 0,
25
+ "last_msg" => '',
26
  );
27
 
28
  /**
29
  * JobExecutable constructor.
30
  */
31
+ public function __construct() {
 
32
  parent::__construct();
33
 
34
  // Calculate total steps
35
  $this->calculateTotalSteps();
36
+
37
+ // Set server settings (Do not set this globally. HS Ticket #9061)
38
+ wpstg_setup_environment();
39
  }
40
 
41
  /**
44
  * @param bool $incrementCurrentStep
45
  * @return array
46
  */
47
+ protected function prepareResponse( $status = false, $incrementCurrentStep = true ) {
48
+ if( $incrementCurrentStep ) {
 
 
49
  $this->options->currentStep++;
50
  }
51
 
52
  $percentage = 0;
53
+ if( isset( $this->options->currentStep ) && isset( $this->options->totalSteps ) && $this->options->totalSteps > 0 ) {
54
+ $percentage = round( ($this->options->currentStep / $this->options->totalSteps) * 100 );
55
+ $percentage = (100 < $percentage) ? 100 : $percentage;
56
  }
57
 
58
  return $this->response = array(
59
+ "status" => $status,
60
+ "percentage" => $percentage,
61
+ "total" => $this->options->totalSteps,
62
+ "step" => $this->options->currentStep,
63
+ "job" => $this->options->currentJob,
64
+ "last_msg" => $this->logger->getLastLogMsg(),
65
+ "running_time" => $this->time() - time(),
66
+ "job_done" => $status
67
  );
68
  }
69
 
71
  * Start Module
72
  * @return object
73
  */
74
+ public function start() {
 
75
  // Execute steps
76
  $this->run();
77
 
78
  // Save option, progress
79
  $this->saveOptions();
80
 
81
+ return ( object ) $this->response;
82
  }
83
+
84
+ /**
85
  * Run Steps
86
  */
87
+ protected function run() {
 
88
  // Execute steps
89
+ for ( $i = 0; $i < $this->options->totalSteps; $i++ ) {
 
90
  // Job is finished or over threshold limits was hit
91
+ if( !$this->execute() ) {
 
92
  break;
93
  }
94
  // Return after every step to create lower batches
95
  // This also gets a smoother progress bar and to a less consumptive php cpu load
96
  // This decrease performance tremendous but also lowers memory consumption
97
+ if( $this->settings->cpuLoad === 'low' ) {
98
+ return ( object ) $this->response;
99
  }
100
  }
101
  }
112
  * @return bool
113
  */
114
  abstract protected function execute();
115
+ }
apps/Backend/Modules/Jobs/Multisite/Data.php CHANGED
@@ -40,12 +40,9 @@ class Data extends JobExecutable {
40
  * Initialize
41
  */
42
  public function initialize() {
43
- $this->db = WPStaging::getInstance()->get( "wpdb" );
44
-
45
  $this->prefix = $this->options->prefix;
46
-
47
  $this->getTables();
48
-
49
  // Fix current step
50
  if( 0 == $this->options->currentStep ) {
51
  $this->options->currentStep = 0;
@@ -81,10 +78,8 @@ class Data extends JobExecutable {
81
  public function start() {
82
  // Execute steps
83
  $this->run();
84
-
85
  // Save option, progress
86
  $this->saveOptions();
87
-
88
  return ( object ) $this->response;
89
  }
90
 
@@ -98,7 +93,6 @@ class Data extends JobExecutable {
98
  if( $this->isRoot() ) {
99
  return false;
100
  }
101
-
102
  // Over limits threshold
103
  if( $this->isOverThreshold() ) {
104
  // Prepare response and save current progress
@@ -106,23 +100,19 @@ class Data extends JobExecutable {
106
  $this->saveOptions();
107
  return false;
108
  }
109
-
110
  // No more steps, finished
111
  if( $this->isFinished() ) {
112
  $this->prepareResponse( true, false );
113
  return false;
114
  }
115
-
116
  // Execute step
117
  $stepMethodName = "step" . $this->options->currentStep;
118
  if( !$this->{$stepMethodName}() ) {
119
  $this->prepareResponse( false, false );
120
  return false;
121
  }
122
-
123
  // Prepare Response
124
  $this->prepareResponse();
125
-
126
  // Not finished
127
  return true;
128
  }
@@ -133,6 +123,7 @@ class Data extends JobExecutable {
133
  */
134
  protected function isFinished() {
135
  return (
 
136
  $this->options->currentStep > $this->options->totalSteps ||
137
  !method_exists( $this, "step" . $this->options->currentStep )
138
  );
@@ -143,24 +134,20 @@ class Data extends JobExecutable {
143
  * @return boolean
144
  */
145
  protected function isRoot() {
146
-
147
  // Prefix is the same as the one of live site
148
  //$wpdb = WPStaging::getInstance()->get( "wpdb" );
149
  if( $this->db->prefix === $this->prefix ) {
150
  return true;
151
  }
152
-
153
  // CloneName is empty
154
  $name = ( array ) $this->options->cloneDirectoryName;
155
  if( empty( $name ) ) {
156
  return true;
157
  }
158
-
159
  // Live domain === Staging domain
160
  if( $this->multisiteHomeDomain . $this->options->cloneDirectoryName === $this->multisiteHomeDomain ) {
161
  return true;
162
  }
163
-
164
  return false;
165
  }
166
 
@@ -178,54 +165,138 @@ class Data extends JobExecutable {
178
  }
179
 
180
  /**
181
- * Return absolute destination path
182
- * @return string
183
- */
184
- // private function getAbsDestination() {
185
- // if( empty( $this->options->cloneDir ) ) {
186
- // return \WPStaging\WPStaging::getWPpath();
187
- // }
188
- // return trailingslashit( $this->options->cloneDir );
189
- // }
190
-
191
- /**
192
- * Copy wp-config.php if it is located outside of root one level up
193
- * @todo Needs some more testing before it will be released
194
  * @return boolean
195
  */
196
  protected function step0() {
197
- $this->log( "Preparing Data Step0: Check if wp-config.php is located in root path", Logger::TYPE_INFO );
198
-
199
- $dir = trailingslashit( dirname( ABSPATH ) );
200
-
201
- $source = $dir . 'wp-config.php';
202
-
203
  $destination = $this->options->destinationDir . 'wp-config.php';
204
-
205
-
206
- // Do not do anything
207
- if( (!is_file( $source ) && !is_link( $source )) || is_file( $destination ) ) {
208
- $this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
209
  return true;
210
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
- // Copy target of a symbolic link
 
 
 
 
 
 
 
213
  if( is_link( $source ) ) {
214
- $this->log( "Preparing Data Step0: Symbolic link found...", Logger::TYPE_INFO );
215
  if( !@copy( readlink( $source ), $destination ) ) {
216
  $errors = error_get_last();
217
- $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
218
- return true;
219
  }
220
  }
221
-
222
- // Copy file wp-config.php
223
  if( !@copy( $source, $destination ) ) {
224
  $errors = error_get_last();
225
- $this->log( "Preparing Data Step0: Failed to copy wp-config.php! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
226
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  }
228
- $this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
229
  return true;
230
  }
231
 
@@ -235,7 +306,6 @@ class Data extends JobExecutable {
235
  */
236
  protected function step1() {
237
  $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
238
-
239
  // Skip - Table does not exist
240
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
241
  return true;
@@ -245,27 +315,6 @@ class Data extends JobExecutable {
245
  $this->log( "Preparing Data Step1: Skipping" );
246
  return true;
247
  }
248
-
249
- // Installed in sub-directory
250
- // if( $this->isSubDir() ) {
251
- // $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
252
- // // Replace URLs
253
- // $result = $this->db->query(
254
- // $this->db->prepare(
255
- // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName
256
- // )
257
- // );
258
- // } else
259
- // {
260
- // $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->multisiteHomeDomain, "/" ) . '/' . $this->options->cloneDirectoryName );
261
- // // Replace URLs
262
- // $result = $this->db->query(
263
- // $this->db->prepare(
264
- // "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->multisiteHomeDomain . '/' . $this->options->cloneDirectoryName
265
- // )
266
- // );
267
- // }
268
-
269
  $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
270
  // Replace URLs
271
  $result = $this->db->query(
@@ -273,15 +322,11 @@ class Data extends JobExecutable {
273
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
274
  )
275
  );
276
-
277
-
278
-
279
  // All good
280
  if( $result ) {
281
  return true;
282
  }
283
-
284
- $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
285
  return true;
286
  }
287
 
@@ -290,11 +335,10 @@ class Data extends JobExecutable {
290
  * @return bool
291
  */
292
  protected function step2() {
293
-
294
  $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
295
-
296
  // Skip - Table does not exist
297
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
 
298
  return true;
299
  }
300
  // Skip - Table is not selected or updated
@@ -302,13 +346,11 @@ class Data extends JobExecutable {
302
  $this->log( "Preparing Data Step2: Skipping" );
303
  return true;
304
  }
305
-
306
  $result = $this->db->query(
307
  $this->db->prepare(
308
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
309
  )
310
  );
311
-
312
  // No errors but no option name such as wpstg_is_staging_site
313
  if( '' === $this->db->last_error && 0 == $result ) {
314
  $result = $this->db->query(
@@ -317,12 +359,10 @@ class Data extends JobExecutable {
317
  )
318
  );
319
  }
320
-
321
  // All good
322
  if( $result ) {
323
  return true;
324
  }
325
-
326
  $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
327
  return false;
328
  }
@@ -332,60 +372,55 @@ class Data extends JobExecutable {
332
  * @return bool
333
  */
334
  protected function step3() {
335
-
336
  $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
337
-
 
 
 
 
338
  // Skip - Table does not exist
339
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
340
  return true;
341
  }
342
-
343
  // Skip - Table is not selected or updated
344
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
345
  $this->log( "Preparing Data Step3: Skipping" );
346
  return true;
347
  }
348
-
349
  $result = $this->db->query(
350
  $this->db->prepare(
351
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
352
  )
353
  );
354
-
355
  // All good
356
  if( $result ) {
357
  return true;
358
  }
359
-
360
- $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
361
  return true;
362
  }
363
 
364
  /**
365
- * Update Table Prefix in wp_usermeta and wp_options
366
  * @return bool
367
  */
368
  protected function step4() {
369
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. {$this->db->last_error}" );
370
-
371
  // Skip - Table does not exist
372
  if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
373
  return true;
374
  }
375
-
376
  // Skip - Table is not selected or updated
377
  if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
378
  $this->log( "Preparing Data Step4: Skipping" );
379
  return true;
380
  }
381
-
382
  $update = $this->db->query(
383
  $this->db->prepare(
384
  "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
385
  )
386
  );
387
-
388
- if( !$update ) {
389
  $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
390
  $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
391
  return false;
@@ -398,25 +433,20 @@ class Data extends JobExecutable {
398
  * @return bool
399
  */
400
  protected function step5() {
401
- $path = $this->options->destinationDir . "wp-config.php";
402
-
403
  $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
404
  if( false === ($content = file_get_contents( $path )) ) {
405
  $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
406
  return false;
407
  }
408
-
409
  // Replace table prefix
410
  $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
411
-
412
  // Replace URLs
413
  $content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
414
-
415
  if( false === @file_put_contents( $path, $content ) ) {
416
  $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
417
  return false;
418
  }
419
-
420
  return true;
421
  }
422
 
@@ -428,45 +458,34 @@ class Data extends JobExecutable {
428
  * @return bool
429
  */
430
  protected function step6() {
431
-
432
  if( !$this->isSubDir() ) {
433
  $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
434
  return true;
435
  }
436
-
437
- $path = $this->options->destinationDir . "index.php";
438
-
439
  if( false === ($content = file_get_contents( $path )) ) {
440
  $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
441
  return false;
442
  }
443
-
444
-
445
  if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
446
  $this->log(
447
- "Preparing Data Step6: Failed to reset index.php for sub directory. Can not find line 'require(.*)wp-blog-header.php' in index.php", Logger::TYPE_ERROR
448
  );
449
  return false;
450
  }
451
  $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
452
-
453
  $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
454
-
455
  $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
456
- $replace.= " // Changed by WP-Staging";
457
-
458
-
459
-
460
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
461
  $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
462
  return false;
463
  }
464
-
465
  if( false === @file_put_contents( $path, $content ) ) {
466
  $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
467
  return false;
468
  }
469
- $this->Log( "Preparing Data: Finished Step 6 successfully" );
470
  return true;
471
  }
472
 
@@ -475,33 +494,22 @@ class Data extends JobExecutable {
475
  * @return bool
476
  */
477
  protected function step7() {
478
-
479
  $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
480
-
481
  // Skip - Table does not exist
482
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
483
  return true;
484
  }
485
-
486
  // Skip - Table is not selected or updated
487
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
488
  $this->log( "Preparing Data Step7: Skipping" );
489
  return true;
490
  }
491
-
492
  $result = $this->db->query(
493
  $this->db->prepare(
494
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
495
  )
496
  );
497
-
498
- // All good
499
- if( $result ) {
500
- $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
501
- return true;
502
- }
503
-
504
- $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
505
  return true;
506
  }
507
 
@@ -510,32 +518,31 @@ class Data extends JobExecutable {
510
  * @return bool
511
  */
512
  protected function step8() {
513
-
514
  $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
515
-
 
 
 
 
516
  // Skip - Table does not exist
517
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
518
  return true;
519
  }
520
-
521
  // Skip - Table is not selected or updated
522
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
523
  $this->log( "Preparing Data Step8: Skipping" );
524
  return true;
525
  }
526
-
527
  $result = $this->db->query(
528
  $this->db->prepare(
529
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
530
  )
531
  );
532
-
533
  // All good
534
  if( $result ) {
535
- $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
536
  return true;
537
  }
538
-
539
  $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
540
  return true;
541
  }
@@ -545,31 +552,25 @@ class Data extends JobExecutable {
545
  * @return bool
546
  */
547
  protected function step9() {
548
-
549
  $this->log( "Preparing Data Step9: Set staging site to noindex" );
550
-
551
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
552
  return true;
553
  }
554
-
555
  // Skip - Table is not selected or updated
556
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
557
  $this->log( "Preparing Data Step9: Skipping" );
558
  return true;
559
  }
560
-
561
  $result = $this->db->query(
562
  $this->db->prepare(
563
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
564
  )
565
  );
566
-
567
  // All good
568
  if( $result ) {
569
- $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
570
  return true;
571
  }
572
-
573
  $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
574
  return true;
575
  }
@@ -579,40 +580,31 @@ class Data extends JobExecutable {
579
  * @return bool
580
  */
581
  protected function step10() {
582
- $path = $this->options->destinationDir . "wp-config.php";
583
-
584
  $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
585
-
586
  if( false === ($content = file_get_contents( $path )) ) {
587
  $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
588
  return false;
589
  }
590
-
591
-
592
  // Get WP_HOME from wp-config.php
593
- preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/", $content, $matches );
594
-
595
  if( !empty( $matches[1] ) ) {
596
  $matches[1];
597
-
598
- $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*(.*)\s*\);/";
599
-
600
  $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
601
- $replace.= " // Changed by WP-Staging";
602
-
603
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
604
- $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
605
  return false;
606
  }
607
  } else {
608
  $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
609
  }
610
-
611
  if( false === @file_put_contents( $path, $content ) ) {
612
- $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
613
  return false;
614
  }
615
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
616
  return true;
617
  }
618
 
@@ -621,27 +613,19 @@ class Data extends JobExecutable {
621
  * @return bool
622
  */
623
  protected function step11() {
624
- $path = $this->options->destinationDir . "wp-config.php";
625
-
626
  $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
627
-
628
  if( false === ($content = file_get_contents( $path )) ) {
629
  $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
630
  return false;
631
  }
632
-
633
-
634
  // Get WP_SITEURL from wp-config.php
635
- preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/", $content, $matches );
636
-
637
  if( !empty( $matches[1] ) ) {
638
  $matches[1];
639
-
640
- $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*(.*)\s*\);/";
641
-
642
  $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
643
- $replace.= " // Changed by WP-Staging";
644
-
645
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
646
  $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
647
  return false;
@@ -649,13 +633,11 @@ class Data extends JobExecutable {
649
  } else {
650
  $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
651
  }
652
-
653
-
654
  if( false === @file_put_contents( $path, $content ) ) {
655
  $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
656
  return false;
657
  }
658
- $this->Log( "Preparing Data: Finished Step 11 successfully" );
659
  return true;
660
  }
661
 
@@ -664,27 +646,19 @@ class Data extends JobExecutable {
664
  * @return bool
665
  */
666
  protected function step12() {
667
- $path = $this->options->destinationDir . "wp-config.php";
668
-
669
  $this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
670
-
671
  if( false === ($content = file_get_contents( $path )) ) {
672
  $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
673
  return false;
674
  }
675
-
676
-
677
  // Get WP_SITEURL from wp-config.php
678
- preg_match( "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
679
-
680
  if( !empty( $matches[1] ) ) {
681
  $matches[1];
682
-
683
- $pattern = "/define\s*\(\s*'WP_ALLOW_MULTISITE'\s*,\s*(.*)\s*\);/";
684
-
685
  $replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
686
- $replace.= " // Changed by WP-Staging";
687
-
688
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
689
  $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
690
  return false;
@@ -692,8 +666,6 @@ class Data extends JobExecutable {
692
  } else {
693
  $this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
694
  }
695
-
696
-
697
  if( false === @file_put_contents( $path, $content ) ) {
698
  $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
699
  return false;
@@ -707,27 +679,19 @@ class Data extends JobExecutable {
707
  * @return bool
708
  */
709
  protected function step13() {
710
- $path = $this->options->destinationDir . "wp-config.php";
711
-
712
  $this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
713
-
714
  if( false === ($content = file_get_contents( $path )) ) {
715
  $this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
716
  return false;
717
  }
718
-
719
-
720
  // Get WP_SITEURL from wp-config.php
721
- preg_match( "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/", $content, $matches );
722
-
723
  if( !empty( $matches[1] ) ) {
724
  $matches[1];
725
-
726
- $pattern = "/define\s*\(\s*'MULTISITE'\s*,\s*(.*)\s*\);/";
727
-
728
  $replace = "define('MULTISITE',false); // " . $matches[1];
729
- $replace.= " // Changed by WP-Staging";
730
-
731
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
732
  $this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
733
  return false;
@@ -735,13 +699,11 @@ class Data extends JobExecutable {
735
  } else {
736
  $this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
737
  }
738
-
739
-
740
  if( false === @file_put_contents( $path, $content ) ) {
741
  $this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
742
  return false;
743
  }
744
- $this->Log( "Preparing Data: Finished Step 13 successfully" );
745
  return true;
746
  }
747
 
@@ -750,56 +712,42 @@ class Data extends JobExecutable {
750
  * Merge both arrays and copy them to the staging site into active_plugins
751
  */
752
  protected function step14() {
753
-
754
-
755
- $this->log( "Data Crunching Step 14: Updating active_plugins" );
756
-
757
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
758
- $this->log( 'Data Crunching Step 14: Fatal Error ' . $this->prefix . 'options does not exist' );
759
- $this->returnException( 'Data Crunching Step 14: Fatal Error ' . $this->prefix . 'options does not exist' );
760
  return false;
761
  }
762
-
763
  // Skip - Table is not selected or updated
764
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
765
  $this->log( "Preparing Data Step14: Skipping" );
766
  return true;
767
  }
768
-
769
  // Get active_plugins value from sub site options table
770
  $active_plugins = $this->db->get_var( "SELECT option_value FROM {$this->db->prefix}options WHERE option_name = 'active_plugins' " );
771
-
772
  if( !$active_plugins ) {
773
- $this->log( "Data Crunching Step 14: Option active_plugins are empty " );
774
  $active_plugins = array();
775
  }
776
  // Get active_sitewide_plugins value from main multisite wp_sitemeta table
777
  $active_sitewide_plugins = $this->db->get_var( "SELECT meta_value FROM {$this->db->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
778
-
779
  if( !$active_sitewide_plugins ) {
780
- $this->log( "Data Crunching Step 14: Options {$this->db->base_prefix}active_sitewide_plugins is empty " );
781
  $active_sitewide_plugins = array();
782
  }
783
-
784
  $active_sitewide_plugins = unserialize( $active_sitewide_plugins );
785
  $active_plugins = unserialize( $active_plugins );
786
-
787
- $all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
788
-
789
  sort( $all_plugins );
790
-
791
-
792
  // Update active_plugins
793
- $update = $this->db->query(
794
  "UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
795
  );
796
-
797
  if( false === $update ) {
798
- $this->log( "Data Crunching Step 14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
799
  return false;
800
  }
801
-
802
- $this->log( "Data Crunching Step 14: Successful!" );
803
  return true;
804
  }
805
 
@@ -809,48 +757,37 @@ class Data extends JobExecutable {
809
  */
810
  protected function step15() {
811
  $this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
812
-
813
  // Skip - Table does not exist
814
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
815
  return true;
816
  }
817
-
818
  // Skip - Table is not selected or updated
819
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
820
  $this->log( "Preparing Data Step4: Skipping" );
821
  return true;
822
  }
823
-
824
-
825
  $this->log( "Updating db option_names in {$this->prefix}options. " );
826
-
827
  // Filter the rows below. Do not update them!
828
  $filters = array(
829
  'wp_mail_smtp',
830
  'wp_mail_smtp_version',
831
  'wp_mail_smtp_debug',
832
  );
833
-
834
  $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
835
-
836
- $where = "";
837
  foreach ( $filters as $filter ) {
838
  $where .= " AND option_name <> '" . $filter . "'";
839
  }
840
-
841
  $updateOptions = $this->db->query(
842
  $this->db->prepare(
843
  "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
844
  )
845
  );
846
-
847
- if( !$updateOptions ) {
848
  $this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_WARNING );
849
  //$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
850
  return true;
851
  }
852
-
853
-
854
  return true;
855
  }
856
 
@@ -860,36 +797,28 @@ class Data extends JobExecutable {
860
  */
861
  protected function step16() {
862
  $this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
863
-
864
  // Skip - Table does not exist
865
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
866
  return true;
867
  }
868
-
869
  $newUploadPath = $this->getNewUploadPath();
870
-
871
  if( false === $newUploadPath ) {
872
  $this->log( "Preparing Data Step16: Skipping" );
873
  return true;
874
  }
875
-
876
  // Skip - Table is not selected or updated
877
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
878
  $this->log( "Preparing Data Step16: Skipping" );
879
  return true;
880
  }
881
-
882
- $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
883
-
884
  $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
885
-
886
  $updateOptions = $this->db->query(
887
  $this->db->prepare(
888
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
889
  )
890
  );
891
-
892
- if( !$updateOptions ) {
893
  $this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
894
  return true;
895
  }
@@ -902,27 +831,19 @@ class Data extends JobExecutable {
902
  * @return bool
903
  */
904
  protected function step17() {
905
- $path = $this->options->destinationDir . "wp-config.php";
906
-
907
  $this->log( "Preparing Data Step17: Set WP_CACHE in wp-config.php to false" );
908
-
909
  if( false === ($content = file_get_contents( $path )) ) {
910
  $this->log( "Preparing Data Step17: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
911
  return false;
912
  }
913
-
914
-
915
  // Get WP_CACHE from wp-config.php
916
- preg_match( "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/", $content, $matches );
917
-
918
  if( !empty( $matches[1] ) ) {
919
  $matches[1];
920
-
921
- $pattern = "/define\s*\(\s*'WP_CACHE'\s*,\s*(.*)\s*\);/";
922
-
923
  $replace = "define('WP_CACHE',false); // " . $matches[1];
924
- $replace.= " // Changed by WP-Staging";
925
-
926
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
927
  $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
928
  return false;
@@ -930,7 +851,6 @@ class Data extends JobExecutable {
930
  } else {
931
  $this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
932
  }
933
-
934
  if( false === @file_put_contents( $path, $content ) ) {
935
  $this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
936
  return false;
@@ -939,21 +859,213 @@ class Data extends JobExecutable {
939
  return true;
940
  }
941
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
942
  /**
943
  * Get Upload Path to staging site
944
  * @return boolean|string
945
  */
946
  protected function getNewUploadPath() {
947
  $uploadPath = get_option( 'upload_path' );
948
-
949
  if( !$uploadPath ) {
950
  return false;
951
  }
952
-
953
- $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
954
-
955
  $newUploadPath = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR . $customSlug;
956
-
957
  return $newUploadPath;
958
  }
959
 
@@ -962,16 +1074,18 @@ class Data extends JobExecutable {
962
  * @return string
963
  */
964
  protected function getStagingSiteUrl() {
965
-
966
  if( !empty( $this->options->cloneHostname ) ) {
967
  return $this->options->cloneHostname;
968
  }
969
-
970
  if( $this->isSubDir() ) {
971
- return trailingslashit( $this->multisiteHomeDomain ) . trailingslashit($this->getSubDir()) . $this->options->cloneDirectoryName;
972
- }
973
-
974
- return trailingslashit( $this->multisiteHomeDomain ) . $this->options->cloneDirectoryName;
 
 
 
 
975
  }
976
 
977
  /**
@@ -983,7 +1097,6 @@ class Data extends JobExecutable {
983
  // This is happening much more often than you would expect
984
  $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
985
  $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
986
-
987
  if( $home !== $siteurl ) {
988
  return true;
989
  }
@@ -997,11 +1110,9 @@ class Data extends JobExecutable {
997
  protected function getSubDir() {
998
  $home = get_option( 'home' );
999
  $siteurl = get_option( 'siteurl' );
1000
-
1001
  if( empty( $home ) || empty( $siteurl ) ) {
1002
  return '';
1003
  }
1004
-
1005
  $dir = str_replace( $home, '', $siteurl );
1006
  return str_replace( '/', '', $dir );
1007
  }
40
  * Initialize
41
  */
42
  public function initialize() {
43
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
 
44
  $this->prefix = $this->options->prefix;
 
45
  $this->getTables();
 
46
  // Fix current step
47
  if( 0 == $this->options->currentStep ) {
48
  $this->options->currentStep = 0;
78
  public function start() {
79
  // Execute steps
80
  $this->run();
 
81
  // Save option, progress
82
  $this->saveOptions();
 
83
  return ( object ) $this->response;
84
  }
85
 
93
  if( $this->isRoot() ) {
94
  return false;
95
  }
 
96
  // Over limits threshold
97
  if( $this->isOverThreshold() ) {
98
  // Prepare response and save current progress
100
  $this->saveOptions();
101
  return false;
102
  }
 
103
  // No more steps, finished
104
  if( $this->isFinished() ) {
105
  $this->prepareResponse( true, false );
106
  return false;
107
  }
 
108
  // Execute step
109
  $stepMethodName = "step" . $this->options->currentStep;
110
  if( !$this->{$stepMethodName}() ) {
111
  $this->prepareResponse( false, false );
112
  return false;
113
  }
 
114
  // Prepare Response
115
  $this->prepareResponse();
 
116
  // Not finished
117
  return true;
118
  }
123
  */
124
  protected function isFinished() {
125
  return (
126
+ !isset( $this->options->isRunning ) ||
127
  $this->options->currentStep > $this->options->totalSteps ||
128
  !method_exists( $this, "step" . $this->options->currentStep )
129
  );
134
  * @return boolean
135
  */
136
  protected function isRoot() {
 
137
  // Prefix is the same as the one of live site
138
  //$wpdb = WPStaging::getInstance()->get( "wpdb" );
139
  if( $this->db->prefix === $this->prefix ) {
140
  return true;
141
  }
 
142
  // CloneName is empty
143
  $name = ( array ) $this->options->cloneDirectoryName;
144
  if( empty( $name ) ) {
145
  return true;
146
  }
 
147
  // Live domain === Staging domain
148
  if( $this->multisiteHomeDomain . $this->options->cloneDirectoryName === $this->multisiteHomeDomain ) {
149
  return true;
150
  }
 
151
  return false;
152
  }
153
 
165
  }
166
 
167
  /**
168
+ * Copy wp-config.php from the staging site if it is located outside of root one level up or
169
+ * copy default wp-config.php if production site uses bedrock or any other boilerplate solution that stores wp default config data elsewhere.
 
 
 
 
 
 
 
 
 
 
 
170
  * @return boolean
171
  */
172
  protected function step0() {
173
+ $this->log( "Preparing Data Step0: Copy wp-config.php file", Logger::TYPE_INFO );
174
+ $dir = trailingslashit( dirname( ABSPATH ) );
175
+ $source = $dir . 'wp-config.php';
 
 
 
176
  $destination = $this->options->destinationDir . 'wp-config.php';
177
+ // Check if there is already a valid wp-config.php in root of staging site
178
+ if( $this->isValidWpConfig( $destination ) ) {
179
+ $this->log( "Preparing Data Step0: Found wp-config.php file in folder {$destination}", Logger::TYPE_INFO );
 
 
180
  return true;
181
  }
182
+ // Check if there is a valid wp-config.php outside root of wp production site
183
+ if( $this->isValidWpConfig( $source ) ) {
184
+ // Copy it to staging site
185
+ if( $this->copy( $source, $destination ) ) {
186
+ $this->log( "Preparing Data Step0: Copy wp-config.php file from source {$source} to {$destination}", Logger::TYPE_INFO );
187
+ return true;
188
+ }
189
+ }
190
+ // No valid wp-config.php found so let's copy wp stagings default wp-config.php to staging site
191
+ $source = WPSTG_PLUGIN_DIR . "apps/Backend/helpers/wp-config.php";
192
+ $this->log( "Preparing Data Step0: Copy default wp-config.php file from source {$source} to {$destination}", Logger::TYPE_INFO );
193
+ if( $this->copy( $source, $destination ) ) {
194
+ // add missing db credentials to wp-config.php
195
+ if( !$this->alterWpConfig( $destination ) ) {
196
+ $this->log( "Preparing Data Step0: Can not alter db credentials in wp-config.php", Logger::TYPE_INFO );
197
+ return false;
198
+ }
199
+ }
200
+ $this->log( "Preparing Data Step0: Successful", Logger::TYPE_INFO );
201
+ return true;
202
+ }
203
 
204
+ /**
205
+ * Copy files with symlink support
206
+ * @param type $source
207
+ * @param type $destination
208
+ * @return boolean
209
+ */
210
+ protected function copy( $source, $destination ) {
211
+ // Copy symbolic link
212
  if( is_link( $source ) ) {
213
+ $this->log( "Preparing Data: Symbolic link found...", Logger::TYPE_INFO );
214
  if( !@copy( readlink( $source ), $destination ) ) {
215
  $errors = error_get_last();
216
+ $this->log( "Preparing Data: Failed to copy {$source} Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
217
+ return false;
218
  }
219
  }
220
+ // Copy file
 
221
  if( !@copy( $source, $destination ) ) {
222
  $errors = error_get_last();
223
+ $this->log( "Preparing Data Step0: Failed to copy {$source}! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
224
+ return false;
225
+ }
226
+ return true;
227
+ }
228
+
229
+ /**
230
+ * Make sure wp-config.php contains correct db credentials
231
+ * @param type $source
232
+ * @return boolean
233
+ */
234
+ protected function alterWpConfig( $source ) {
235
+ $this->log( "Preparing Data: Alter wp-config.php", Logger::TYPE_ERROR );
236
+ $content = file_get_contents( $source );
237
+ if( false === ($content = file_get_contents( $source )) ) {
238
+ return false;
239
+ }
240
+ $search = "// ** MySQL settings ** //";
241
+ $replace = "// ** MySQL settings ** //\r\n
242
+ define( 'DB_NAME', '" . DB_NAME . "' );\r\n
243
+ /** MySQL database username */\r\n
244
+ define( 'DB_USER', '" . DB_USER . "' );\r\n
245
+ /** MySQL database password */\r\n
246
+ define( 'DB_PASSWORD', '" . DB_PASSWORD . "' );\r\n
247
+ /** MySQL hostname */\r\n
248
+ define( 'DB_HOST', '" . DB_HOST . "' );\r\n
249
+ /** Database Charset to use in creating database tables. */\r\n
250
+ define( 'DB_CHARSET', '" . DB_CHARSET . "' );\r\n
251
+ /** The Database Collate type. Don't change this if in doubt. */\r\n
252
+ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
253
+ $content = str_replace( $search, $replace, $content );
254
+ if( false === @file_put_contents( $source, $content ) ) {
255
+ $this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
256
+ return false;
257
+ }
258
+ return true;
259
+ }
260
+
261
+ /**
262
+ * Check if wp-config.php contains important constants
263
+ * @param type $source
264
+ * @return boolean
265
+ */
266
+ protected function isValidWpConfig( $source ) {
267
+ if( !is_file( $source ) && !is_link( $source ) ) {
268
+ $this->log( "Preparing Data Step0: Can not find {$source}", Logger::TYPE_INFO );
269
+ return false;
270
+ }
271
+ $content = file_get_contents( $source );
272
+ if( false === ($content = file_get_contents( $source )) ) {
273
+ $this->log( "Preparing Data Step0: Can not read {$source}", Logger::TYPE_INFO );
274
+ return false;
275
+ }
276
+ // Get DB_NAME from wp-config.php
277
+ preg_match( "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
278
+ if( empty( $matches[1] ) ) {
279
+ $this->log( "Preparing Data Step0: Can not find DB_NAME in wp-config.php", Logger::TYPE_INFO );
280
+ return false;
281
+ }
282
+ // Get DB_USER from wp-config.php
283
+ preg_match( "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
284
+ if( empty( $matches[1] ) ) {
285
+ $this->log( "Preparing Data Step0: Can not find DB_USER in wp-config.php", Logger::TYPE_INFO );
286
+ return false;
287
+ }
288
+ // Get DB_PASSWORD from wp-config.php
289
+ preg_match( "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
290
+ if( empty( $matches[1] ) ) {
291
+ $this->log( "Preparing Data Step0: Can not find DB_PASSWORD in wp-config.php", Logger::TYPE_INFO );
292
+ return false;
293
+ }
294
+ // Get DB_HOST from wp-config.php
295
+ preg_match( "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
296
+ if( empty( $matches[1] ) ) {
297
+ $this->log( "Preparing Data Step0: Can not find DB_HOST in wp-config.php", Logger::TYPE_INFO );
298
+ return false;
299
  }
 
300
  return true;
301
  }
302
 
306
  */
307
  protected function step1() {
308
  $this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
 
309
  // Skip - Table does not exist
310
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
311
  return true;
315
  $this->log( "Preparing Data Step1: Skipping" );
316
  return true;
317
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
319
  // Replace URLs
320
  $result = $this->db->query(
322
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
323
  )
324
  );
 
 
 
325
  // All good
326
  if( $result ) {
327
  return true;
328
  }
329
+ $this->log( "Preparing Data Step1: Skip updating siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_WARNING );
 
330
  return true;
331
  }
332
 
335
  * @return bool
336
  */
337
  protected function step2() {
 
338
  $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
 
339
  // Skip - Table does not exist
340
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
341
+ $this->log( "Preparing Data Step2: Skipping" );
342
  return true;
343
  }
344
  // Skip - Table is not selected or updated
346
  $this->log( "Preparing Data Step2: Skipping" );
347
  return true;
348
  }
 
349
  $result = $this->db->query(
350
  $this->db->prepare(
351
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
352
  )
353
  );
 
354
  // No errors but no option name such as wpstg_is_staging_site
355
  if( '' === $this->db->last_error && 0 == $result ) {
356
  $result = $this->db->query(
359
  )
360
  );
361
  }
 
362
  // All good
363
  if( $result ) {
364
  return true;
365
  }
 
366
  $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
367
  return false;
368
  }
372
  * @return bool
373
  */
374
  protected function step3() {
 
375
  $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
376
+ // Keep Permalinks
377
+ if( isset( $this->settings->keepPermalinks ) && $this->settings->keepPermalinks === "1" ) {
378
+ $this->log( "Preparing Data Step3: Skipping" );
379
+ return true;
380
+ }
381
  // Skip - Table does not exist
382
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
383
  return true;
384
  }
 
385
  // Skip - Table is not selected or updated
386
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
387
  $this->log( "Preparing Data Step3: Skipping" );
388
  return true;
389
  }
 
390
  $result = $this->db->query(
391
  $this->db->prepare(
392
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
393
  )
394
  );
 
395
  // All good
396
  if( $result ) {
397
  return true;
398
  }
399
+ //$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
 
400
  return true;
401
  }
402
 
403
  /**
404
+ * Update Table Prefix in wp_usermeta
405
  * @return bool
406
  */
407
  protected function step4() {
408
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
 
409
  // Skip - Table does not exist
410
  if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
411
  return true;
412
  }
 
413
  // Skip - Table is not selected or updated
414
  if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
415
  $this->log( "Preparing Data Step4: Skipping" );
416
  return true;
417
  }
 
418
  $update = $this->db->query(
419
  $this->db->prepare(
420
  "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
421
  )
422
  );
423
+ if( false === $update ) {
 
424
  $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
425
  $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
426
  return false;
433
  * @return bool
434
  */
435
  protected function step5() {
436
+ $path = $this->options->destinationDir . "wp-config.php";
 
437
  $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
438
  if( false === ($content = file_get_contents( $path )) ) {
439
  $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
440
  return false;
441
  }
 
442
  // Replace table prefix
443
  $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
 
444
  // Replace URLs
445
  $content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
 
446
  if( false === @file_put_contents( $path, $content ) ) {
447
  $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
448
  return false;
449
  }
 
450
  return true;
451
  }
452
 
458
  * @return bool
459
  */
460
  protected function step6() {
 
461
  if( !$this->isSubDir() ) {
462
  $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
463
  return true;
464
  }
465
+ $path = $this->options->destinationDir . "index.php";
 
 
466
  if( false === ($content = file_get_contents( $path )) ) {
467
  $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
468
  return false;
469
  }
 
 
470
  if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
471
  $this->log(
472
+ "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
473
  );
474
  return false;
475
  }
476
  $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
 
477
  $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
 
478
  $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
479
+ $replace .= " // Changed by WP-Staging";
 
 
 
480
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
481
  $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
482
  return false;
483
  }
 
484
  if( false === @file_put_contents( $path, $content ) ) {
485
  $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
486
  return false;
487
  }
488
+ $this->Log( "Preparing Data Step6: Finished successfully" );
489
  return true;
490
  }
491
 
494
  * @return bool
495
  */
496
  protected function step7() {
 
497
  $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
 
498
  // Skip - Table does not exist
499
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
500
  return true;
501
  }
 
502
  // Skip - Table is not selected or updated
503
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
504
  $this->log( "Preparing Data Step7: Skipping" );
505
  return true;
506
  }
 
507
  $result = $this->db->query(
508
  $this->db->prepare(
509
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
510
  )
511
  );
512
+ $this->Log( "Preparing Data Step7: Finished successfully" );
 
 
 
 
 
 
 
513
  return true;
514
  }
515
 
518
  * @return bool
519
  */
520
  protected function step8() {
 
521
  $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
522
+ // Keep Permalinks
523
+ if( isset( $this->settings->keepPermalinks ) && $this->settings->keepPermalinks === "1" ) {
524
+ $this->log( "Preparing Data Step8: Skipping" );
525
+ return true;
526
+ }
527
  // Skip - Table does not exist
528
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
529
  return true;
530
  }
 
531
  // Skip - Table is not selected or updated
532
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
533
  $this->log( "Preparing Data Step8: Skipping" );
534
  return true;
535
  }
 
536
  $result = $this->db->query(
537
  $this->db->prepare(
538
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
539
  )
540
  );
 
541
  // All good
542
  if( $result ) {
543
+ $this->Log( "Preparing Data Step8: Finished successfully" );
544
  return true;
545
  }
 
546
  $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
547
  return true;
548
  }
552
  * @return bool
553
  */
554
  protected function step9() {
 
555
  $this->log( "Preparing Data Step9: Set staging site to noindex" );
 
556
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
557
  return true;
558
  }
 
559
  // Skip - Table is not selected or updated
560
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
561
  $this->log( "Preparing Data Step9: Skipping" );
562
  return true;
563
  }
 
564
  $result = $this->db->query(
565
  $this->db->prepare(
566
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
567
  )
568
  );
 
569
  // All good
570
  if( $result ) {
571
+ $this->Log( "Preparing Data Step9: Finished successfully" );
572
  return true;
573
  }
 
574
  $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
575
  return true;
576
  }
580
  * @return bool
581
  */
582
  protected function step10() {
583
+ $path = $this->options->destinationDir . "wp-config.php";
 
584
  $this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
 
585
  if( false === ($content = file_get_contents( $path )) ) {
586
  $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
587
  return false;
588
  }
 
 
589
  // Get WP_HOME from wp-config.php
590
+ preg_match( "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
 
591
  if( !empty( $matches[1] ) ) {
592
  $matches[1];
593
+ $pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/";
 
 
594
  $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
595
+ $replace .= " // Changed by WP-Staging";
 
596
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
597
+ $this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
598
  return false;
599
  }
600
  } else {
601
  $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
602
  }
 
603
  if( false === @file_put_contents( $path, $content ) ) {
604
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
605
  return false;
606
  }
607
+ $this->Log( "Preparing Data Step 10: Finished successfully" );
608
  return true;
609
  }
610
 
613
  * @return bool
614
  */
615
  protected function step11() {
616
+ $path = $this->options->destinationDir . "wp-config.php";
 
617
  $this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
 
618
  if( false === ($content = file_get_contents( $path )) ) {
619
  $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
620
  return false;
621
  }
 
 
622
  // Get WP_SITEURL from wp-config.php
623
+ preg_match( "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
 
624
  if( !empty( $matches[1] ) ) {
625
  $matches[1];
626
+ $pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/";
 
 
627
  $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
628
+ $replace .= " // Changed by WP-Staging";
 
629
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
630
  $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
631
  return false;
633
  } else {
634
  $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
635
  }
 
 
636
  if( false === @file_put_contents( $path, $content ) ) {
637
  $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
638
  return false;
639
  }
640
+ $this->Log( "Preparing Data Step 11: Finished successfully" );
641
  return true;
642
  }
643
 
646
  * @return bool
647
  */
648
  protected function step12() {
649
+ $path = $this->options->destinationDir . "wp-config.php";
 
650
  $this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
 
651
  if( false === ($content = file_get_contents( $path )) ) {
652
  $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
653
  return false;
654
  }
 
 
655
  // Get WP_SITEURL from wp-config.php
656
+ preg_match( "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
 
657
  if( !empty( $matches[1] ) ) {
658
  $matches[1];
659
+ $pattern = "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);/";
 
 
660
  $replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
661
+ $replace .= " // Changed by WP-Staging";
 
662
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
663
  $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
664
  return false;
666
  } else {
667
  $this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
668
  }
 
 
669
  if( false === @file_put_contents( $path, $content ) ) {
670
  $this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
671
  return false;
679
  * @return bool
680
  */
681
  protected function step13() {
682
+ $path = $this->options->destinationDir . "wp-config.php";
 
683
  $this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
 
684
  if( false === ($content = file_get_contents( $path )) ) {
685
  $this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
686
  return false;
687
  }
 
 
688
  // Get WP_SITEURL from wp-config.php
689
+ preg_match( "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
 
690
  if( !empty( $matches[1] ) ) {
691
  $matches[1];
692
+ $pattern = "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);/";
 
 
693
  $replace = "define('MULTISITE',false); // " . $matches[1];
694
+ $replace .= " // Changed by WP-Staging";
 
695
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
696
  $this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
697
  return false;
699
  } else {
700
  $this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
701
  }
 
 
702
  if( false === @file_put_contents( $path, $content ) ) {
703
  $this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
704
  return false;
705
  }
706
+ $this->Log( "Preparing Data Step13: Finished successfully" );
707
  return true;
708
  }
709
 
712
  * Merge both arrays and copy them to the staging site into active_plugins
713
  */
714
  protected function step14() {
715
+ $this->log( "Data Crunching Step14: Updating active_plugins" );
 
 
 
716
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
717
+ $this->log( 'Data Crunching Step14: Fatal Error ' . $this->prefix . 'options does not exist' );
718
+ $this->returnException( 'Data Crunching Step14: Fatal Error ' . $this->prefix . 'options does not exist' );
719
  return false;
720
  }
 
721
  // Skip - Table is not selected or updated
722
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
723
  $this->log( "Preparing Data Step14: Skipping" );
724
  return true;
725
  }
 
726
  // Get active_plugins value from sub site options table
727
  $active_plugins = $this->db->get_var( "SELECT option_value FROM {$this->db->prefix}options WHERE option_name = 'active_plugins' " );
 
728
  if( !$active_plugins ) {
729
+ $this->log( "Data Crunching Step14: Option active_plugins are empty " );
730
  $active_plugins = array();
731
  }
732
  // Get active_sitewide_plugins value from main multisite wp_sitemeta table
733
  $active_sitewide_plugins = $this->db->get_var( "SELECT meta_value FROM {$this->db->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
 
734
  if( !$active_sitewide_plugins ) {
735
+ $this->log( "Data Crunching Step14: Options {$this->db->base_prefix}active_sitewide_plugins is empty " );
736
  $active_sitewide_plugins = array();
737
  }
 
738
  $active_sitewide_plugins = unserialize( $active_sitewide_plugins );
739
  $active_plugins = unserialize( $active_plugins );
740
+ $all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
 
 
741
  sort( $all_plugins );
 
 
742
  // Update active_plugins
743
+ $update = $this->db->query(
744
  "UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
745
  );
 
746
  if( false === $update ) {
747
+ $this->log( "Data Crunching Step14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
748
  return false;
749
  }
750
+ $this->log( "Data Crunching Step14: Successful!" );
 
751
  return true;
752
  }
753
 
757
  */
758
  protected function step15() {
759
  $this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
 
760
  // Skip - Table does not exist
761
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
762
  return true;
763
  }
 
764
  // Skip - Table is not selected or updated
765
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
766
  $this->log( "Preparing Data Step4: Skipping" );
767
  return true;
768
  }
 
 
769
  $this->log( "Updating db option_names in {$this->prefix}options. " );
 
770
  // Filter the rows below. Do not update them!
771
  $filters = array(
772
  'wp_mail_smtp',
773
  'wp_mail_smtp_version',
774
  'wp_mail_smtp_debug',
775
  );
 
776
  $filters = apply_filters( 'wpstg_data_excl_rows', $filters );
777
+ $where = "";
 
778
  foreach ( $filters as $filter ) {
779
  $where .= " AND option_name <> '" . $filter . "'";
780
  }
 
781
  $updateOptions = $this->db->query(
782
  $this->db->prepare(
783
  "UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
784
  )
785
  );
786
+ if( false === $updateOptions ) {
 
787
  $this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_WARNING );
788
  //$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
789
  return true;
790
  }
 
 
791
  return true;
792
  }
793
 
797
  */
798
  protected function step16() {
799
  $this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
 
800
  // Skip - Table does not exist
801
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
802
  return true;
803
  }
 
804
  $newUploadPath = $this->getNewUploadPath();
 
805
  if( false === $newUploadPath ) {
806
  $this->log( "Preparing Data Step16: Skipping" );
807
  return true;
808
  }
 
809
  // Skip - Table is not selected or updated
810
  if( !in_array( $this->prefix . 'options', $this->tables ) ) {
811
  $this->log( "Preparing Data Step16: Skipping" );
812
  return true;
813
  }
814
+ $error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
 
 
815
  $this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
 
816
  $updateOptions = $this->db->query(
817
  $this->db->prepare(
818
  "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
819
  )
820
  );
821
+ if( false === $updateOptions ) {
 
822
  $this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
823
  return true;
824
  }
831
  * @return bool
832
  */
833
  protected function step17() {
834
+ $path = $this->options->destinationDir . "wp-config.php";
 
835
  $this->log( "Preparing Data Step17: Set WP_CACHE in wp-config.php to false" );
 
836
  if( false === ($content = file_get_contents( $path )) ) {
837
  $this->log( "Preparing Data Step17: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
838
  return false;
839
  }
 
 
840
  // Get WP_CACHE from wp-config.php
841
+ preg_match( "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
 
842
  if( !empty( $matches[1] ) ) {
843
  $matches[1];
844
+ $pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/";
 
 
845
  $replace = "define('WP_CACHE',false); // " . $matches[1];
846
+ $replace .= " // Changed by WP-Staging";
 
847
  if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
848
  $this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
849
  return false;
851
  } else {
852
  $this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
853
  }
 
854
  if( false === @file_put_contents( $path, $content ) ) {
855
  $this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
856
  return false;
859
  return true;
860
  }
861
 
862
+ /**
863
+ * Add UPLOADS constant in wp-config.php or change it to correct destination (multisite type /sites/2/)
864
+ * @return bool
865
+ */
866
+ protected function step18() {
867
+ $path = $this->options->destinationDir . "wp-config.php";
868
+ $this->log( "Preparing Data Step18: Update UPLOADS constant in wp-config.php" );
869
+ if( false === ($content = file_get_contents( $path )) ) {
870
+ $this->log( "Preparing Data Step18: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
871
+ return false;
872
+ }
873
+ // Get UPLOADS from wp-config.php if there is already one
874
+ preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
875
+ $uploadFolder = $this->getMultisiteUploadFolder();
876
+ if( !empty( $matches[0] ) ) {
877
+ $pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
878
+ $replace = "define('UPLOADS', '" . $uploadFolder . "');";
879
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
880
+ $this->log( "Preparing Data Step 18: Failed to change UPLOADS", Logger::TYPE_ERROR );
881
+ return false;
882
+ }
883
+ } else {
884
+ $this->log( "Preparing Data Step18: UPLOADS not defined in wp-config.php. Creating new entry." );
885
+ // Find ABSPATH and add UPLOAD constant above
886
+ preg_match( "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/", $content, $matches );
887
+ if( !empty( $matches[0] ) ) {
888
+ $matches[0];
889
+ $pattern = "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/";
890
+ $replace = "define('UPLOADS', '" . $uploadFolder . "'); \n" .
891
+ "if ( ! defined( 'ABSPATH' ) )";
892
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
893
+ $this->log( "Preparing Data Step 18: Failed to change UPLOADS", Logger::TYPE_ERROR );
894
+ return false;
895
+ }
896
+ } else {
897
+ $this->log( "Preparing Data Step 18: Can not add UPLOAD constant to wp-config.php. Can not find free position to add it.", Logger::TYPE_ERROR );
898
+ }
899
+ }
900
+ if( false === @file_put_contents( $path, $content ) ) {
901
+ $this->log( "Preparing Data Step18: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
902
+ return false;
903
+ }
904
+ $this->Log( "Preparing Data Step18: Finished successfully" );
905
+ return true;
906
+ }
907
+
908
+ /**
909
+ * Update database credentials in wp-config.php
910
+ * @return bool
911
+ */
912
+ // protected function step19() {
913
+ // $path = $this->options->destinationDir . "wp-config.php";
914
+ //
915
+ // $this->log("Preparing Data Step19: Change database credentials in wp-config.php");
916
+ //
917
+ // if (false === ($content = file_get_contents($path))) {
918
+ // $this->log("Preparing Data Step19: Failed to update database credentials in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR);
919
+ // return false;
920
+ // }
921
+ //
922
+ //
923
+ // // Get DB_NAME from wp-config.php
924
+ // preg_match("/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*(.*)\s*\);/", $content, $matches);
925
+ //
926
+ // if (!empty($matches[1])) {
927
+ // $matches[1];
928
+ //
929
+ // $pattern = "/define\s*\(\s*'DB_NAME'\s*,\s*(.*)\s*\);/";
930
+ //
931
+ // $replace = "define('DB_NAME','{$this->options->databaseDatabase}'); // " . $matches[1];
932
+ // $replace .= " // Changed by WP-Staging";
933
+ //
934
+ // if (null === ($content = preg_replace(array($pattern), $replace, $content))) {
935
+ // $this->log("Preparing Data: Failed to change DB_NAME", Logger::TYPE_ERROR);
936
+ // return false;
937
+ // }
938
+ // } else {
939
+ // $this->log("Preparing Data Step19: DB_NAME not defined in wp-config.php. Skipping this step.");
940
+ // }
941
+ // // Get DB_USER from wp-config.php
942
+ // preg_match("/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/", $content, $matches);
943
+ //
944
+ // if (!empty($matches[1])) {
945
+ // $matches[1];
946
+ //
947
+ // $pattern = "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/";
948
+ //
949
+ // $replace = "define('DB_USER','{$this->options->databaseUser}'); // " . $matches[1];
950
+ // $replace .= " // Changed by WP-Staging";
951
+ //
952
+ // if (null === ($content = preg_replace(array($pattern), $replace, $content))) {
953
+ // $this->log("Preparing Data: Failed to change DB_USER", Logger::TYPE_ERROR);
954
+ // return false;
955
+ // }
956
+ // } else {
957
+ // $this->log("Preparing Data Step19: DB_USER not defined in wp-config.php. Skipping this step.");
958
+ // }
959
+ // // Get DB_PASSWORD from wp-config.php
960
+ // preg_match("/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/", $content, $matches);
961
+ //
962
+ // if (!empty($matches[1])) {
963
+ // $matches[1];
964
+ //
965
+ // $pattern = "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/";
966
+ //
967
+ // $replace = "define('DB_PASSWORD','{$this->options->databasePassword}'); // " . $matches[1];
968
+ // $replace .= " // Changed by WP-Staging";
969
+ //
970
+ // if (null === ($content = preg_replace(array($pattern), $replace, $content))) {
971
+ // $this->log("Preparing Data: Failed to change DB_PASSWORD", Logger::TYPE_ERROR);
972
+ // return false;
973
+ // }
974
+ // } else {
975
+ // $this->log("Preparing Data Step19: DB_PASSWORD not defined in wp-config.php. Skipping this step.");
976
+ // }
977
+ // // Get DB_HOST from wp-config.php
978
+ // preg_match("/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/", $content, $matches);
979
+ //
980
+ // if (!empty($matches[1])) {
981
+ // $matches[1];
982
+ //
983
+ // $pattern = "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/";
984
+ //
985
+ // $replace = "define('DB_HOST','{$this->options->databaseServer}'); // " . $matches[1];
986
+ // $replace .= " // Changed by WP-Staging";
987
+ //
988
+ // if (null === ($content = preg_replace(array($pattern), $replace, $content))) {
989
+ // $this->log("Preparing Data: Failed to change DB_HOST", Logger::TYPE_ERROR);
990
+ // return false;
991
+ // }
992
+ // } else {
993
+ // $this->log("Preparing Data Step19: DB_HOST not defined in wp-config.php. Skipping this step.");
994
+ // }
995
+ //
996
+ //
997
+ // if (false === @file_put_contents($path, $content)) {
998
+ // $this->log("Preparing Data Step19: Failed to update database credentials in wp-config.php. Can't save contents", Logger::TYPE_ERROR);
999
+ // return false;
1000
+ // }
1001
+ // $this->Log("Preparing Data Step 19: Finished successfully");
1002
+ // return true;
1003
+ // }
1004
+ /**
1005
+ * Remove UPLOADS constant in wp-config.php to reset default image folder
1006
+ * @return bool
1007
+ */
1008
+ // protected function step20() {
1009
+ // $path = $this->options->destinationDir . "wp-config.php";
1010
+ //
1011
+ // $this->log( "Preparing Data Step20: Remove UPLOADS in wp-config.php" );
1012
+ //
1013
+ // if( false === ($content = file_get_contents( $path )) ) {
1014
+ // $this->log( "Preparing Data Step20: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
1015
+ // return false;
1016
+ // }
1017
+ //
1018
+ //
1019
+ // // Get UPLOADS from wp-config.php
1020
+ // preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
1021
+ //
1022
+ // if( !empty( $matches[0] ) ) {
1023
+ //
1024
+ // $pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
1025
+ //
1026
+ // $replace = "";
1027
+ //
1028
+ // if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
1029
+ // $this->log( "Preparing Data: Failed to change UPLOADS", Logger::TYPE_ERROR );
1030
+ // return false;
1031
+ // }
1032
+ // } else {
1033
+ // $this->log( "Preparing Data Step19: UPLOADS not defined in wp-config.php. Skipping this step." );
1034
+ // }
1035
+ //
1036
+ // if( false === @file_put_contents( $path, $content ) ) {
1037
+ // $this->log( "Preparing Data Step20: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
1038
+ // return false;
1039
+ // }
1040
+ // $this->Log( "Preparing Data Step 20: Finished successfully" );
1041
+ // return true;
1042
+ // }
1043
+ /**
1044
+ * Get relative path to the uploads media folder of multisite e.g.
1045
+ * wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
1046
+ * @return boolean
1047
+ */
1048
+ protected function getMultisiteUploadFolder() {
1049
+ $strings = new Strings();
1050
+ // Get absolute path to uploads folder
1051
+ $uploads = wp_upload_dir();
1052
+ $basedir = $strings->sanitizeDirectorySeparator( $uploads['basedir'] );
1053
+ // Get relative upload path
1054
+ $relDir = str_replace( ABSPATH, null, $basedir );
1055
+ return $relDir;
1056
+ }
1057
+
1058
  /**
1059
  * Get Upload Path to staging site
1060
  * @return boolean|string
1061
  */
1062
  protected function getNewUploadPath() {
1063
  $uploadPath = get_option( 'upload_path' );
 
1064
  if( !$uploadPath ) {
1065
  return false;
1066
  }
1067
+ $customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
 
 
1068
  $newUploadPath = \WPStaging\WPStaging::getWPpath() . $this->options->cloneDirectoryName . DIRECTORY_SEPARATOR . $customSlug;
 
1069
  return $newUploadPath;
1070
  }
1071
 
1074
  * @return string
1075
  */
1076
  protected function getStagingSiteUrl() {
 
1077
  if( !empty( $this->options->cloneHostname ) ) {
1078
  return $this->options->cloneHostname;
1079
  }
 
1080
  if( $this->isSubDir() ) {
1081
+ return trailingslashit( $this->multisiteHomeDomain ) . trailingslashit( $this->getSubDir() ) . $this->options->cloneDirectoryName;
1082
+ }
1083
+ // Get the path to the main multisite without appending and trailingslash e.g. wordpress
1084
+ $multisitePath = defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : '/';
1085
+ $url = rtrim( $this->multisiteHomeDomain, '/\\' ) . $multisitePath . $this->options->cloneDirectoryName;
1086
+ //$multisitePath = defined( 'PATH_CURRENT_SITE') ? str_replace('/', '', PATH_CURRENT_SITE) : '';
1087
+ //$url = trailingslashit( $this->multisiteHomeDomain ) . $multisitePath . '/' . $this->options->cloneDirectoryName;
1088
+ return $url;
1089
  }
1090
 
1091
  /**
1097
  // This is happening much more often than you would expect
1098
  $siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
1099
  $home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
 
1100
  if( $home !== $siteurl ) {
1101
  return true;
1102
  }
1110
  protected function getSubDir() {
1111
  $home = get_option( 'home' );
1112
  $siteurl = get_option( 'siteurl' );
 
1113
  if( empty( $home ) || empty( $siteurl ) ) {
1114
  return '';
1115
  }
 
1116
  $dir = str_replace( $home, '', $siteurl );
1117
  return str_replace( '/', '', $dir );
1118
  }
apps/Backend/views/clone/ajax/process-lock.php CHANGED
@@ -7,7 +7,7 @@
7
  </button>
8
 
9
  <button type="button" id="wpstg-restart-cloning" class="wpstg-link-btn button-primary wpstg-button">
10
- <?php echo __("Start over and stop other process", "wp-staging")?>
11
  </button>
12
 
13
 
7
  </button>
8
 
9
  <button type="button" id="wpstg-restart-cloning" class="wpstg-link-btn button-primary wpstg-button">
10
+ <?php echo __("Stop other process", "wp-staging")?>
11
  </button>
12
 
13
 
apps/Backend/views/welcome/welcome.php CHANGED
@@ -1,7 +1,7 @@
1
  <div class="" id="wpstg-welcome">
2
  <div style="border: 2px solid white;padding: 20px;margin-bottom:20px;">
3
  <h2 class="wpstg-h2">
4
- <span class="wpstg-heading-pro"><?php _e( 'WP Staging Pro', 'wp-staging' ); ?></span><?php _e( ' - Migrate and copy over staging site to live site', 'wp-staging' ); ?>
5
  </h2>
6
  <li><strong>Cloning</strong> - <?php _e( 'Create a clone of your website with a simple click', 'wp-staging' ); ?></li>
7
  <li><strong>Push Changes</strong> - <?php _e( 'Copy plugin and theme files from staging to live site', 'wp-staging' ); ?></li>
1
  <div class="" id="wpstg-welcome">
2
  <div style="border: 2px solid white;padding: 20px;margin-bottom:20px;">
3
  <h2 class="wpstg-h2">
4
+ <span class="wpstg-heading-pro"><?php _e( 'WP Staging Pro', 'wp-staging' ); ?></span><?php _e( ' - Copy Themes & Plugins from Staging to Live Site', 'wp-staging' ); ?>
5
  </h2>
6
  <li><strong>Cloning</strong> - <?php _e( 'Create a clone of your website with a simple click', 'wp-staging' ); ?></li>
7
  <li><strong>Push Changes</strong> - <?php _e( 'Copy plugin and theme files from staging to live site', 'wp-staging' ); ?></li>
apps/Core/Utils/functions.php CHANGED
@@ -7,11 +7,11 @@
7
  */
8
  function wpstg_get_permissions_for_directory() {
9
  $octal = 0755;
10
- if (defined('FS_CHMOD_DIR')) {
11
  $octal = FS_CHMOD_DIR;
12
  }
13
 
14
- return apply_filters('wpstg_folder_permission', $octal);
15
  }
16
 
17
  /**
@@ -170,39 +170,87 @@ function wpstg_is_stagingsite() {
170
  return true;
171
  }
172
 
173
- if( file_exists( ABSPATH . '.wp-staging')){
174
  return true;
175
  }
176
 
177
  return false;
178
  }
179
 
180
- /**
181
- * @param string $memory
182
- * @return int
183
- */
184
- function wpstg_get_memory_in_bytes( $memory ) {
185
- // Handle unlimited ones
186
- if( 1 > ( int ) $memory ) {
187
- //return (int) $memory;
188
- // 128 MB default value
189
- return ( int ) 134217728;
190
- }
191
-
192
- $bytes = ( int ) $memory; // grab only the number
193
- $size = trim( str_replace( $bytes, null, strtolower( $memory ) ) ); // strip away number and lower-case it
194
- // Actual calculation
195
- switch ( $size ) {
196
- case 'k':
197
  $bytes *= 1024;
198
  break;
199
- case 'm':
200
  $bytes *= (1024 * 1024);
201
  break;
202
- case 'g':
203
  $bytes *= (1024 * 1024 * 1024);
204
  break;
205
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
- return $bytes;
208
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  */
8
  function wpstg_get_permissions_for_directory() {
9
  $octal = 0755;
10
+ if( defined( 'FS_CHMOD_DIR' ) ) {
11
  $octal = FS_CHMOD_DIR;
12
  }
13
 
14
+ return apply_filters( 'wpstg_folder_permission', $octal );
15
  }
16
 
17
  /**
170
  return true;
171
  }
172
 
173
+ if( file_exists( ABSPATH . '.wp-staging' ) ) {
174
  return true;
175
  }
176
 
177
  return false;
178
  }
179
 
180
+ /**
181
+ * @param string $memory
182
+ * @return int
183
+ */
184
+ function wpstg_get_memory_in_bytes( $memory ) {
185
+ // Handle unlimited ones
186
+ if( 1 > ( int ) $memory ) {
187
+ //return (int) $memory;
188
+ // 128 MB default value
189
+ return ( int ) 134217728;
190
+ }
191
+
192
+ $bytes = ( int ) $memory; // grab only the number
193
+ $size = trim( str_replace( $bytes, null, strtolower( $memory ) ) ); // strip away number and lower-case it
194
+ // Actual calculation
195
+ switch ( $size ) {
196
+ case 'k':
197
  $bytes *= 1024;
198
  break;
199
+ case 'm':
200
  $bytes *= (1024 * 1024);
201
  break;
202
+ case 'g':
203
  $bytes *= (1024 * 1024 * 1024);
204
  break;
205
+ }
206
+
207
+ return $bytes;
208
+ }
209
+
210
+ /**
211
+ * Invalidate constraints
212
+ * @param type $query
213
+ * @return type
214
+ */
215
+ function wpstg_unique_constraint( $query ) {
216
+ // Change name to random in all constraints, if there, to prevent trouble with existing
217
+ $query = preg_replace_callback( "/CONSTRAINT\s`(\w+)`/", function() {
218
+ return "CONSTRAINT `" . uniqid() . "`";
219
+ }, $query );
220
+
221
+ return $query;
222
+ }
223
 
224
+ /**
225
+ * Get relative path to the uploads folder, can be a custom folder e.g assets or default folder wp-content/uploads
226
+ * @return string
227
+ */
228
+ function wpstg_get_rel_upload_dir() {
229
+ // Get upload directory information. Default is ABSPATH . 'wp-content/uploads'
230
+ // Can be customized by populating the db option upload_path or the constant UPLOADS
231
+ // If both are defined WordPress will uses the value of the UPLOADS constant
232
+ $uploads = wp_upload_dir();
233
+
234
+ // Get absolute path to wordpress uploads directory e.g srv/www/htdocs/sitename/wp-content/uploads
235
+ $uploadsAbsPath = trailingslashit( $uploads['basedir'] );
236
+
237
+ // Get relative path to the uploads folder, e.g assets
238
+ //$relPath = rtrim(str_replace( ABSPATH, null, $uploadsAbsPath ), "/\\");
239
+ $relPath = str_replace( ABSPATH, null, $uploadsAbsPath );
240
+
241
+ return $relPath;
242
+ }
243
+
244
+ /**
245
+ * Get relative path to the uploads folder, can be a custom folder e.g assets or default folder wp-content/uploads
246
+ * @return string
247
+ */
248
+ function wpstg_get_abs_upload_dir() {
249
+ // Get upload directory information.
250
+ $uploads = wp_upload_dir();
251
+
252
+ // Get absolute path to wordpress uploads directory e.g srv/www/htdocs/sitename/wp-content/uploads
253
+ $uploadsAbsPath = trailingslashit( $uploads['basedir'] );
254
+
255
+ return $uploadsAbsPath;
256
+ }
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.6.1";
33
 
34
  /**
35
  * Plugin name
@@ -83,7 +83,7 @@ final class WPStaging {
83
  // Licensing stuff be loaded in wpstg core to make cron hook available from frontpage
84
  $this->initLicensing();
85
 
86
- wpstg_setup_environment();
87
  }
88
 
89
  /**
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.6.2";
33
 
34
  /**
35
  * Plugin name
83
  // Licensing stuff be loaded in wpstg core to make cron hook available from frontpage
84
  $this->initLicensing();
85
 
86
+ //wpstg_setup_environment();
87
  }
88
 
89
  /**
readme.txt CHANGED
@@ -9,7 +9,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
  Tested up to: 5.2
12
- Stable tag: 2.6.1
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
@@ -151,6 +151,14 @@ https://wp-staging.com
151
 
152
  == Changelog ==
153
 
 
 
 
 
 
 
 
 
154
  = 2.6.1 =
155
  * New: Improve styling of login form. Thanks to Andy Kennan (Screaming Frog)
156
  * New: Add 'password lost' button to login form
@@ -160,6 +168,7 @@ https://wp-staging.com
160
  * Fix: Can not update staging site db table if there are constraints in it
161
 
162
 
 
163
  = 2.6.0 =
164
  * New: Compatible up to WordPress 5.2.2
165
  * New: Performance improvement for directory iterator using less server ressources
9
  Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
10
  Requires at least: 3.6+
11
  Tested up to: 5.2
12
+ Stable tag: 2.6.2
13
  Requires PHP: 5.3
14
 
15
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
151
 
152
  == Changelog ==
153
 
154
+ = 2.6.2 =
155
+ * Fix: Do not show warning "Preparing Data Step3: Failed to update rewrite_rules in wpstg0_options"
156
+ * Fix: Change error "Table wpstgtmp_options does not exist" to warning
157
+ * New: Add arguments for hook wpstg_cloning_complete
158
+ * New: Setup server environment variables per process and not globally (e.g. set_time_limit)
159
+ * New: Add support for custom uploads folder if user customized UPLOADS constant or upload_path in DB
160
+
161
+
162
  = 2.6.1 =
163
  * New: Improve styling of login form. Thanks to Andy Kennan (Screaming Frog)
164
  * New: Add 'password lost' button to login form
168
  * Fix: Can not update staging site db table if there are constraints in it
169
 
170
 
171
+
172
  = 2.6.0 =
173
  * New: Compatible up to WordPress 5.2.2
174
  * New: Performance improvement for directory iterator using less server ressources
wp-staging.php CHANGED
@@ -7,7 +7,7 @@
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
- * Version: 2.6.1
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
@@ -51,7 +51,7 @@ if( !defined( 'WPSTG_PLUGIN_URL' ) ) {
51
 
52
  // Version
53
  if( !defined( 'WPSTG_VERSION' ) ) {
54
- define( 'WPSTG_VERSION', '2.6.1' );
55
  }
56
 
57
  // Must use version of the optimizer
7
  * Author: WP-Staging
8
  * Author URI: https://wp-staging.com
9
  * Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.6.2
11
  * Text Domain: wp-staging
12
  * Domain Path: /languages/
13
 
51
 
52
  // Version
53
  if( !defined( 'WPSTG_VERSION' ) ) {
54
+ define( 'WPSTG_VERSION', '2.6.2' );
55
  }
56
 
57
  // Must use version of the optimizer