Version Description
- New Support for WordPress 5.2.4
- New: Allow wildcards for excluding files
- New: Add hook "wpstg_clone_action_staging" to execute code on staging site after cloning
- Tweak: Improved support for custom uploads folder if user customized UPLOADS constant or upload_path in DB
- Fix: Better compatibility with Windows IIS server
- Fix: External links are broken after cloning if ABSPATH is equal to /www/
- Fix: use an alternative method for file_put_contents as it is not supported on all systems due to file permission issues
- Fix: Redundant and duplicated update comments in wp-config.php in staging site
Download this release
Release Info
Developer | ReneHermi |
Plugin | WP Staging – DB & File Duplicator & Migration |
Version | 2.6.3 |
Comparing to | |
See all releases |
Code changes from version 2.6.2 to 2.6.3
- apps/Backend/Modules/Jobs/Data.php +139 -159
- apps/Backend/Modules/Jobs/Directories.php +3 -2
- apps/Backend/Modules/Jobs/Files.php +67 -49
- apps/Backend/Modules/Jobs/Multisite/Data.php +381 -175
- apps/Backend/Modules/Jobs/Multisite/DataExternal.php +1430 -1135
- apps/Backend/Modules/Jobs/Multisite/Database.php +366 -363
- apps/Backend/Modules/Jobs/Multisite/DatabaseExternal.php +446 -411
- apps/Backend/Modules/Jobs/Multisite/Directories.php +609 -632
- apps/Backend/Modules/Jobs/Multisite/Files.php +481 -431
- apps/Backend/Modules/Jobs/Multisite/Finish.php +141 -139
- apps/Backend/Modules/Jobs/Multisite/SearchReplace.php +839 -816
- apps/Backend/Modules/Jobs/Multisite/SearchReplaceExternal.php +833 -840
- apps/Backend/Modules/Jobs/Scan.php +73 -10
- apps/Backend/Modules/Jobs/SearchReplace.php +10 -5
- apps/Core/Utils/Cache.php +184 -183
- apps/Core/Utils/Logger.php +3 -1
- apps/Core/Utils/functions.php +202 -0
- apps/Core/WPStaging.php +2 -2
- readme.txt +59 -48
- wp-staging.php +2 -2
apps/Backend/Modules/Jobs/Data.php
CHANGED
@@ -75,7 +75,7 @@ class Data extends JobExecutable {
|
|
75 |
* @return void
|
76 |
*/
|
77 |
protected function calculateTotalSteps() {
|
78 |
-
$this->options->totalSteps =
|
79 |
}
|
80 |
|
81 |
/**
|
@@ -219,7 +219,7 @@ class Data extends JobExecutable {
|
|
219 |
}
|
220 |
}
|
221 |
|
222 |
-
$this->log( "Preparing Data Step0:
|
223 |
return true;
|
224 |
}
|
225 |
|
@@ -279,7 +279,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
279 |
|
280 |
$content = str_replace( $search, $replace, $content );
|
281 |
|
282 |
-
if( false === @
|
283 |
$this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
|
284 |
return false;
|
285 |
}
|
@@ -389,11 +389,6 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
389 |
return true;
|
390 |
}
|
391 |
|
392 |
-
// $result = $this->db->query(
|
393 |
-
// $this->db->prepare(
|
394 |
-
// "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
|
395 |
-
// )
|
396 |
-
// );
|
397 |
$delete = $this->db->query(
|
398 |
$this->db->prepare(
|
399 |
"DELETE FROM `{$this->prefix}options` WHERE `option_name` = %s;", 'wpstg_is_staging_site'
|
@@ -505,12 +500,19 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
505 |
}
|
506 |
|
507 |
// Replace table prefix
|
508 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
509 |
|
510 |
// Replace URLs
|
511 |
$content = str_replace( $this->homeUrl, $this->getStagingSiteUrl(), $content );
|
512 |
|
513 |
-
if( false === @
|
514 |
$this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
|
515 |
return false;
|
516 |
}
|
@@ -548,11 +550,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
548 |
}
|
549 |
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
550 |
|
551 |
-
$pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*)
|
552 |
-
|
553 |
-
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
|
554 |
-
$replace.= " // Changed by WP-Staging";
|
555 |
|
|
|
556 |
|
557 |
|
558 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
@@ -560,7 +560,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
560 |
return false;
|
561 |
}
|
562 |
|
563 |
-
if( false === @
|
564 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
565 |
return false;
|
566 |
}
|
@@ -695,10 +695,10 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
695 |
if( !empty( $matches[1] ) ) {
|
696 |
$matches[1];
|
697 |
|
698 |
-
$pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\)
|
699 |
|
700 |
-
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
701 |
-
|
702 |
|
703 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
704 |
$this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
|
@@ -708,7 +708,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
708 |
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
709 |
}
|
710 |
|
711 |
-
if( false === @
|
712 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
713 |
return false;
|
714 |
}
|
@@ -737,10 +737,10 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
737 |
if( !empty( $matches[1] ) ) {
|
738 |
$matches[1];
|
739 |
|
740 |
-
$pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\)
|
741 |
|
742 |
-
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
743 |
-
|
744 |
|
745 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
746 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL to " . $this->getStagingSiteUrl(), Logger::TYPE_ERROR );
|
@@ -751,7 +751,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
751 |
}
|
752 |
|
753 |
|
754 |
-
if( false === @
|
755 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL to " . $this->getStagingSiteUrl() . " Can't save contents", Logger::TYPE_ERROR );
|
756 |
return false;
|
757 |
}
|
@@ -759,72 +759,6 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
759 |
return true;
|
760 |
}
|
761 |
|
762 |
-
/**
|
763 |
-
* Set true WP_DEBUG & WP_DEBUG_DISPLAY in wp-config.php
|
764 |
-
* @return bool
|
765 |
-
*/
|
766 |
-
// protected function step12() {
|
767 |
-
// $path = $this->getAbsDestination() . $this->options->cloneDirectoryName . "/wp-config.php";
|
768 |
-
//
|
769 |
-
// $this->log( "Preparing Data Step12: Set WP_DEBUG to true in wp-config.php." );
|
770 |
-
//
|
771 |
-
// if( false === ($content = file_get_contents( $path )) ) {
|
772 |
-
// $this->log( "Preparing Data Step12: Failed to update WP_DEBUG in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
773 |
-
// return false;
|
774 |
-
// }
|
775 |
-
//
|
776 |
-
//
|
777 |
-
// // Get WP_DEBUG from wp-config.php
|
778 |
-
// preg_match( "/define\s*\(\s*'WP_DEBUG'\s*,\s*(.*)\s*\);/", $content, $matches );
|
779 |
-
//
|
780 |
-
// // Found it!
|
781 |
-
// if( !empty( $matches[1] ) ) {
|
782 |
-
// $matches[1];
|
783 |
-
//
|
784 |
-
// $pattern = "/define\s*\(\s*'WP_DEBUG'\s*,\s*(.*)\s*\);/";
|
785 |
-
//
|
786 |
-
// $replace = "define('WP_DEBUG',true); // " . $matches[1];
|
787 |
-
// $replace.= " // Changed by WP-Staging";
|
788 |
-
//
|
789 |
-
// if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
790 |
-
// $this->log( "Preparing Data Step12: Failed to update WP_DEBUG", Logger::TYPE_ERROR );
|
791 |
-
// return false;
|
792 |
-
// }
|
793 |
-
// } else {
|
794 |
-
// $this->log( "Preparing Data Step12: WP_DEBUG not defined in wp-config.php. Skip it." );
|
795 |
-
// }
|
796 |
-
//
|
797 |
-
//
|
798 |
-
//
|
799 |
-
// // Get WP_DEBUG_DISPLAY
|
800 |
-
// preg_match( "/define\s*\(\s*'WP_DEBUG_DISPLAY'\s*,\s*(.*)\s*\);/", $content, $matches );
|
801 |
-
//
|
802 |
-
// // Found it!
|
803 |
-
// if( !empty( $matches[1] ) ) {
|
804 |
-
// $matches[1];
|
805 |
-
//
|
806 |
-
// $pattern = "/define\s*\(\s*'WP_DEBUG_DISPLAY'\s*,\s*(.*)\s*\);/";
|
807 |
-
//
|
808 |
-
// $replace = "define('WP_DEBUG_DISPLAY',true); // " . $matches[1];
|
809 |
-
// $replace.= " // Changed by WP-Staging";
|
810 |
-
//
|
811 |
-
// if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
812 |
-
// $this->log( "Preparing Data Step12: Failed to update WP_DEBUG", Logger::TYPE_ERROR );
|
813 |
-
// return false;
|
814 |
-
// }
|
815 |
-
// } else {
|
816 |
-
// $this->log( "Preparing Data Step12: WP_DEBUG not defined in wp-config.php. Skip it." );
|
817 |
-
// }
|
818 |
-
//
|
819 |
-
//
|
820 |
-
// if( false === @file_put_contents( $path, $content ) ) {
|
821 |
-
// $this->log( "Preparing Data Step12: Failed to update WP_DEBUG. Can't save content in wp-config.php", Logger::TYPE_ERROR );
|
822 |
-
// return false;
|
823 |
-
// }
|
824 |
-
// $this->Log( "Preparing Data: Finished Step 12 successfully" );
|
825 |
-
// return true;
|
826 |
-
// }
|
827 |
-
|
828 |
/**
|
829 |
* Update Table Prefix in wp_options
|
830 |
* @return bool
|
@@ -903,11 +837,14 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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'", ""
|
@@ -942,10 +879,10 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
942 |
|
943 |
if( !empty( $matches[1] ) ) {
|
944 |
|
945 |
-
$pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\)
|
946 |
|
947 |
-
$replace = "define('WP_CACHE',false); // " . $matches[1];
|
948 |
-
|
949 |
|
950 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
951 |
$this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
|
@@ -955,7 +892,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
955 |
$this->log( "Preparing Data Step14: WP_CACHE not defined in wp-config.php. Skipping this step." );
|
956 |
}
|
957 |
|
958 |
-
if( false === @
|
959 |
$this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
960 |
return false;
|
961 |
}
|
@@ -995,7 +932,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
995 |
$this->log( "Preparing Data Step15: WP_CONTENT_DIR not defined in wp-config.php. Skipping this step." );
|
996 |
}
|
997 |
|
998 |
-
if( false === @
|
999 |
$this->log( "Preparing Data Step15: Failed to update WP_CONTENT_DIR. Can't save contents", Logger::TYPE_ERROR );
|
1000 |
return false;
|
1001 |
}
|
@@ -1035,7 +972,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
1035 |
$this->log( "Preparing Data Step16: WP_CONTENT_URL not defined in wp-config.php. Skipping this step." );
|
1036 |
}
|
1037 |
|
1038 |
-
if( false === @
|
1039 |
$this->log( "Preparing Data Step16: Failed to update WP_CONTENT_URL. Can't save contents", Logger::TYPE_ERROR );
|
1040 |
return false;
|
1041 |
}
|
@@ -1043,46 +980,6 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
1043 |
return true;
|
1044 |
}
|
1045 |
|
1046 |
-
/**
|
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
|
@@ -1100,6 +997,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 );
|
@@ -1122,7 +1020,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 === @
|
1126 |
$this->log( "Preparing Data Step17: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
|
1127 |
return false;
|
1128 |
}
|
@@ -1131,49 +1029,131 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
1131 |
}
|
1132 |
|
1133 |
/**
|
1134 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1135 |
* @return bool
|
1136 |
*/
|
1137 |
-
// protected function
|
1138 |
-
//
|
1139 |
-
// $this->log( "Preparing Data Step18: Clean up list of staging sites" );
|
1140 |
//
|
|
|
1141 |
// if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
1142 |
// return true;
|
1143 |
// }
|
1144 |
//
|
1145 |
// // Skip - Table is not selected or updated
|
1146 |
// if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1147 |
-
// $this->log( "Preparing Data
|
1148 |
// return true;
|
1149 |
// }
|
1150 |
//
|
1151 |
-
// $
|
1152 |
-
//
|
1153 |
-
//
|
1154 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1155 |
// );
|
1156 |
//
|
1157 |
-
// //
|
1158 |
-
//
|
1159 |
-
//
|
1160 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1161 |
// }
|
1162 |
//
|
1163 |
-
// $this->
|
|
|
|
|
|
|
|
|
1164 |
// return true;
|
1165 |
// }
|
1166 |
|
1167 |
/**
|
1168 |
-
*
|
|
|
1169 |
*/
|
1170 |
-
|
1171 |
-
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
|
|
|
|
|
|
1177 |
|
1178 |
/**
|
1179 |
* Change upload path of staging site if it has been customized on production site
|
@@ -1200,7 +1180,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
1200 |
return false;
|
1201 |
}
|
1202 |
|
1203 |
-
$customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
|
1204 |
|
1205 |
$newUploadPath = $this->options->destinationDir . $customSlug;
|
1206 |
|
75 |
* @return void
|
76 |
*/
|
77 |
protected function calculateTotalSteps() {
|
78 |
+
$this->options->totalSteps = 19;
|
79 |
}
|
80 |
|
81 |
/**
|
219 |
}
|
220 |
}
|
221 |
|
222 |
+
$this->log( "Preparing Data Step0: Successful", Logger::TYPE_INFO );
|
223 |
return true;
|
224 |
}
|
225 |
|
279 |
|
280 |
$content = str_replace( $search, $replace, $content );
|
281 |
|
282 |
+
if( false === @wpstg_put_contents( $source, $content ) ) {
|
283 |
$this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
|
284 |
return false;
|
285 |
}
|
389 |
return true;
|
390 |
}
|
391 |
|
|
|
|
|
|
|
|
|
|
|
392 |
$delete = $this->db->query(
|
393 |
$this->db->prepare(
|
394 |
"DELETE FROM `{$this->prefix}options` WHERE `option_name` = %s;", 'wpstg_is_staging_site'
|
500 |
}
|
501 |
|
502 |
// Replace table prefix
|
503 |
+
$pattern = '/\$table_prefix\s*=\s*(.*)/';
|
504 |
+
$replacement = '$table_prefix = \'' . $this->prefix . '\'; // Changed by WP Staging';
|
505 |
+
$content = preg_replace( $pattern, $replacement, $content );
|
506 |
+
|
507 |
+
if( null === $content ) {
|
508 |
+
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
509 |
+
return false;
|
510 |
+
}
|
511 |
|
512 |
// Replace URLs
|
513 |
$content = str_replace( $this->homeUrl, $this->getStagingSiteUrl(), $content );
|
514 |
|
515 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
516 |
$this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
|
517 |
return false;
|
518 |
}
|
550 |
}
|
551 |
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
552 |
|
553 |
+
$pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);.*/";
|
|
|
|
|
|
|
554 |
|
555 |
+
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0] . " Changed by WP-Staging";
|
556 |
|
557 |
|
558 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
560 |
return false;
|
561 |
}
|
562 |
|
563 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
564 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
565 |
return false;
|
566 |
}
|
695 |
if( !empty( $matches[1] ) ) {
|
696 |
$matches[1];
|
697 |
|
698 |
+
$pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);.*/";
|
699 |
|
700 |
+
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1] . " Changed by WP-Staging";
|
701 |
+
//$replace.= " // Changed by WP-Staging";
|
702 |
|
703 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
704 |
$this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
|
708 |
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
709 |
}
|
710 |
|
711 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
712 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
713 |
return false;
|
714 |
}
|
737 |
if( !empty( $matches[1] ) ) {
|
738 |
$matches[1];
|
739 |
|
740 |
+
$pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);.*/";
|
741 |
|
742 |
+
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1] . " Changed by WP-Staging";
|
743 |
+
//$replace.= " // Changed by WP-Staging";
|
744 |
|
745 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
746 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL to " . $this->getStagingSiteUrl(), Logger::TYPE_ERROR );
|
751 |
}
|
752 |
|
753 |
|
754 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
755 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL to " . $this->getStagingSiteUrl() . " Can't save contents", Logger::TYPE_ERROR );
|
756 |
return false;
|
757 |
}
|
759 |
return true;
|
760 |
}
|
761 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
762 |
/**
|
763 |
* Update Table Prefix in wp_options
|
764 |
* @return bool
|
837 |
|
838 |
$error = !empty( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
|
839 |
|
840 |
+
$this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
|
841 |
+
|
842 |
// $updateOptions = $this->db->query(
|
843 |
// $this->db->prepare(
|
844 |
// "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
|
845 |
// )
|
846 |
// );
|
847 |
+
// remove upload_path value and use UPLOADS constant instead. (upload_path is deprecated and should not be used any longer)
|
848 |
$updateOptions = $this->db->query(
|
849 |
$this->db->prepare(
|
850 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", ""
|
879 |
|
880 |
if( !empty( $matches[1] ) ) {
|
881 |
|
882 |
+
$pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);.*/";
|
883 |
|
884 |
+
$replace = "define('WP_CACHE',false); // " . $matches[1] . " Changed by WP-Staging";
|
885 |
+
//$replace.= " // Changed by WP-Staging";
|
886 |
|
887 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
888 |
$this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
|
892 |
$this->log( "Preparing Data Step14: WP_CACHE not defined in wp-config.php. Skipping this step." );
|
893 |
}
|
894 |
|
895 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
896 |
$this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
897 |
return false;
|
898 |
}
|
932 |
$this->log( "Preparing Data Step15: WP_CONTENT_DIR not defined in wp-config.php. Skipping this step." );
|
933 |
}
|
934 |
|
935 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
936 |
$this->log( "Preparing Data Step15: Failed to update WP_CONTENT_DIR. Can't save contents", Logger::TYPE_ERROR );
|
937 |
return false;
|
938 |
}
|
972 |
$this->log( "Preparing Data Step16: WP_CONTENT_URL not defined in wp-config.php. Skipping this step." );
|
973 |
}
|
974 |
|
975 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
976 |
$this->log( "Preparing Data Step16: Failed to update WP_CONTENT_URL. Can't save contents", Logger::TYPE_ERROR );
|
977 |
return false;
|
978 |
}
|
980 |
return true;
|
981 |
}
|
982 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
983 |
/**
|
984 |
* Add UPLOADS constant in wp-config.php or change it to correct destination.
|
985 |
* This is important when a custom uploads folder is used
|
997 |
$uploadFolder = wpstg_get_rel_upload_dir();
|
998 |
if( !empty( $matches[0] ) ) {
|
999 |
$pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
|
1000 |
+
|
1001 |
$replace = "define('UPLOADS', '" . $uploadFolder . "');";
|
1002 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1003 |
$this->log( "Preparing Data Step17: Failed to change UPLOADS", Logger::TYPE_ERROR );
|
1020 |
$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 );
|
1021 |
}
|
1022 |
}
|
1023 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
1024 |
$this->log( "Preparing Data Step17: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
|
1025 |
return false;
|
1026 |
}
|
1029 |
}
|
1030 |
|
1031 |
/**
|
1032 |
+
* Save hostname of parent production site in option_name wpstg_connection
|
1033 |
+
* @return boolean
|
1034 |
+
*/
|
1035 |
+
protected function step18() {
|
1036 |
+
|
1037 |
+
$table = $this->prefix . 'options';
|
1038 |
+
|
1039 |
+
$siteurl = get_site_url();
|
1040 |
+
|
1041 |
+
$connection = json_encode( array('prodHostname' => $siteurl) );
|
1042 |
+
|
1043 |
+
$data = array(
|
1044 |
+
'option_name' => 'wpstg_connection',
|
1045 |
+
'option_value' => $connection
|
1046 |
+
);
|
1047 |
+
|
1048 |
+
$format = array('%s', '%s');
|
1049 |
+
|
1050 |
+
$result = $this->db->replace( $table, $data, $format );
|
1051 |
+
|
1052 |
+
if( false === $result ) {
|
1053 |
+
$this->Log( "Preparing Data Step18: Could not save {$siteurl} in {$table}", Logger::TYPE_ERROR );
|
1054 |
+
}
|
1055 |
+
return true;
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
/**
|
1059 |
+
* Add option_name wpstg_execute and set it to true
|
1060 |
+
* This option is used to determine if the staging website has not been loaded initiall for executing certain custom actions from \WPStaging\initActions()
|
1061 |
+
* @return boolean
|
1062 |
+
*/
|
1063 |
+
protected function step19() {
|
1064 |
+
|
1065 |
+
$table = $this->prefix . 'options';
|
1066 |
+
|
1067 |
+
// Skip - Table does not exist
|
1068 |
+
if( false === $this->isTable( $table ) ) {
|
1069 |
+
return true;
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
|
1073 |
+
$result = $this->db->query(
|
1074 |
+
$this->db->prepare(
|
1075 |
+
"INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_execute',%s) ON DUPLICATE KEY UPDATE option_value = %s", "true", "true"
|
1076 |
+
)
|
1077 |
+
);
|
1078 |
+
|
1079 |
+
if( false === $result ) {
|
1080 |
+
$this->Log( "Preparing Data Step19: Could not save wpstg_execute in {$table}", Logger::TYPE_ERROR );
|
1081 |
+
}
|
1082 |
+
return true;
|
1083 |
+
}
|
1084 |
+
|
1085 |
+
/**
|
1086 |
+
* Preserve data and prevents data in wp_options from beeing cloned to staging site
|
1087 |
* @return bool
|
1088 |
*/
|
1089 |
+
// protected function step20() {
|
1090 |
+
// $this->log( "Preparing Data Step20: Preserve Data in " . $this->prefix . "options" );
|
|
|
1091 |
//
|
1092 |
+
// // Skip - Table does not exist
|
1093 |
// if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
1094 |
// return true;
|
1095 |
// }
|
1096 |
//
|
1097 |
// // Skip - Table is not selected or updated
|
1098 |
// if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1099 |
+
// $this->log( "Preparing Data Step20: Skipped" );
|
1100 |
// return true;
|
1101 |
// }
|
1102 |
//
|
1103 |
+
// $sql = '';
|
1104 |
+
//
|
1105 |
+
// $preserved_option_names = array('wpstg_existing_clones_beta');
|
1106 |
+
//
|
1107 |
+
// $preserved_option_names = apply_filters( 'wpstg_preserved_options_cloning', $preserved_option_names );
|
1108 |
+
// $preserved_options_escaped = esc_sql( $preserved_option_names );
|
1109 |
+
//
|
1110 |
+
// $preserved_options_data = array();
|
1111 |
+
//
|
1112 |
+
// // Get preserved data in wp_options tables
|
1113 |
+
// $table = $this->db->prefix . 'options';
|
1114 |
+
// $preserved_options_data[$this->prefix . 'options'] = $this->db->get_results(
|
1115 |
+
// sprintf(
|
1116 |
+
// "SELECT * FROM `{$table}` WHERE `option_name` IN ('%s')", implode( "','", $preserved_options_escaped )
|
1117 |
+
// ), ARRAY_A
|
1118 |
// );
|
1119 |
//
|
1120 |
+
// // Create preserved data queries for options tables
|
1121 |
+
// foreach ( $preserved_options_data as $key => $value ) {
|
1122 |
+
// if( false === empty( $value ) ) {
|
1123 |
+
// foreach ( $value as $option ) {
|
1124 |
+
// $sql .= $this->db->prepare(
|
1125 |
+
// "DELETE FROM `{$key}` WHERE `option_name` = %s;\n", $option['option_name']
|
1126 |
+
// );
|
1127 |
+
//
|
1128 |
+
// $sql .= $this->db->prepare(
|
1129 |
+
// "INSERT INTO `{$key}` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s );\n", $option['option_name'], $option['option_value'], $option['autoload']
|
1130 |
+
// );
|
1131 |
+
// }
|
1132 |
+
// }
|
1133 |
// }
|
1134 |
//
|
1135 |
+
// $this->debugLog( "Preparing Data Step20: Preserve values " . json_encode( $preserved_options_data ), Logger::TYPE_INFO );
|
1136 |
+
//
|
1137 |
+
// $this->executeSql( $sql );
|
1138 |
+
//
|
1139 |
+
// $this->log( "Preparing Data Step20: Successful!" );
|
1140 |
// return true;
|
1141 |
// }
|
1142 |
|
1143 |
/**
|
1144 |
+
* Execute a batch of sql queries
|
1145 |
+
* @param string $sqlbatch
|
1146 |
*/
|
1147 |
+
private function executeSql( $sqlbatch ) {
|
1148 |
+
$queries = array_filter( explode( ";\n", $sqlbatch ) );
|
1149 |
+
|
1150 |
+
foreach ( $queries as $query ) {
|
1151 |
+
if( false === $this->db->query( $query ) ) {
|
1152 |
+
$this->log( "Data Crunching Warning: Can not execute query {$query}", Logger::TYPE_WARNING );
|
1153 |
+
}
|
1154 |
+
}
|
1155 |
+
return true;
|
1156 |
+
}
|
1157 |
|
1158 |
/**
|
1159 |
* Change upload path of staging site if it has been customized on production site
|
1180 |
return false;
|
1181 |
}
|
1182 |
|
1183 |
+
$customSlug = str_replace( wpstg_replace_windows_directory_separator( \WPStaging\WPStaging::getWPpath() ), '', wpstg_replace_windows_directory_separator( $uploadPath ) );
|
1184 |
|
1185 |
$newUploadPath = $this->options->destinationDir . $customSlug;
|
1186 |
|
apps/Backend/Modules/Jobs/Directories.php
CHANGED
@@ -164,7 +164,8 @@ class Directories extends JobExecutable {
|
|
164 |
foreach ( $iterator as $item ) {
|
165 |
if( $item->isFile() ) {
|
166 |
$wpContentDir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
|
167 |
-
|
|
|
168 |
$this->options->totalFiles++;
|
169 |
|
170 |
// Too much cpu time
|
@@ -315,7 +316,7 @@ class Directories extends JobExecutable {
|
|
315 |
foreach ( $iterator as $item ) {
|
316 |
if( $item->isFile() ) {
|
317 |
//if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
|
318 |
-
if( $this->write( $files, str_replace( \WPStaging\WPStaging::getWPpath(), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
|
319 |
$this->options->totalFiles++;
|
320 |
// Too much cpu time
|
321 |
//$this->options->totalFileSize += $iterator->getSize();
|
164 |
foreach ( $iterator as $item ) {
|
165 |
if( $item->isFile() ) {
|
166 |
$wpContentDir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
|
167 |
+
$file = $wpContentDir . '/' . $iterator->getSubPathName() . PHP_EOL;
|
168 |
+
if( $this->write( $files, $file ) ) {
|
169 |
$this->options->totalFiles++;
|
170 |
|
171 |
// Too much cpu time
|
316 |
foreach ( $iterator as $item ) {
|
317 |
if( $item->isFile() ) {
|
318 |
//if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
|
319 |
+
if( $this->write( $files, str_replace( wpstg_replace_windows_directory_separator(\WPStaging\WPStaging::getWPpath()), '', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
|
320 |
$this->options->totalFiles++;
|
321 |
// Too much cpu time
|
322 |
//$this->options->totalFileSize += $iterator->getSize();
|
apps/Backend/Modules/Jobs/Files.php
CHANGED
@@ -135,9 +135,7 @@ class Files extends JobExecutable {
|
|
135 |
|
136 |
$file = $this->file->fgets();
|
137 |
|
138 |
-
|
139 |
-
// $test = $file;
|
140 |
-
// }
|
141 |
$this->copyFile( $file );
|
142 |
}
|
143 |
|
@@ -172,6 +170,8 @@ class Files extends JobExecutable {
|
|
172 |
|
173 |
$file = trim( \WPStaging\WPStaging::getWPpath() . $file );
|
174 |
|
|
|
|
|
175 |
$directory = dirname( $file );
|
176 |
|
177 |
// Directory is excluded
|
@@ -224,9 +224,6 @@ class Files extends JobExecutable {
|
|
224 |
return $this->copyBig( $file, $destination, $this->settings->batchSize );
|
225 |
}
|
226 |
|
227 |
-
$this->debugLog( "Try to copy: {$file} to {$destination}", Logger::TYPE_INFO );
|
228 |
-
|
229 |
-
|
230 |
// Attempt to copy
|
231 |
if( !@copy( $file, $destination ) ) {
|
232 |
$errors = error_get_last();
|
@@ -242,34 +239,6 @@ class Files extends JobExecutable {
|
|
242 |
return true;
|
243 |
}
|
244 |
|
245 |
-
/**
|
246 |
-
* Get wp-content and wp-content/uploads destination dir
|
247 |
-
* Necessary if these folders were customized and changed from the default ones.
|
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.
|
@@ -278,7 +247,7 @@ class Files extends JobExecutable {
|
|
278 |
*/
|
279 |
protected function getWpContentPath( $file ) {
|
280 |
// Get upload directory information
|
281 |
-
$uploads
|
282 |
|
283 |
// Get absolute path to wordpress uploads directory e.g srv/www/htdocs/sitename/wp-content/uploads
|
284 |
$uploadsAbsPath = trailingslashit( $uploads['basedir'] );
|
@@ -287,7 +256,7 @@ class Files extends JobExecutable {
|
|
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
|
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 ) {
|
@@ -299,6 +268,8 @@ class Files extends JobExecutable {
|
|
299 |
//$file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
|
300 |
$file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
|
301 |
}
|
|
|
|
|
302 |
return $file;
|
303 |
}
|
304 |
|
@@ -322,15 +293,11 @@ class Files extends JobExecutable {
|
|
322 |
* @return bool|string
|
323 |
*/
|
324 |
private function getDestination( $file ) {
|
325 |
-
|
326 |
-
|
327 |
-
$
|
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 ) ) {
|
@@ -382,11 +349,31 @@ class Files extends JobExecutable {
|
|
382 |
* @return boolean
|
383 |
*/
|
384 |
private function isFileExcluded( $file ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
385 |
// If file name exists
|
386 |
-
if( in_array(
|
387 |
return true;
|
388 |
}
|
389 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
390 |
// Do not copy wp-config.php if the clone gets updated. This is for security purposes,
|
391 |
// because if the updating process fails, the staging site would not be accessable any longer
|
392 |
if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
|
@@ -397,6 +384,41 @@ class Files extends JobExecutable {
|
|
397 |
return false;
|
398 |
}
|
399 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
/**
|
401 |
* Check if certain file is excluded from copying process
|
402 |
*
|
@@ -422,10 +444,6 @@ class Files extends JobExecutable {
|
|
422 |
* @return string
|
423 |
*/
|
424 |
private function sanitizeDirectorySeparator( $path ) {
|
425 |
-
//$string = str_replace( '\\', '/', $path );
|
426 |
-
//$string = str_replace( "/", "\\", $path );
|
427 |
-
//return str_replace( '\\\\', '\\', $string );
|
428 |
-
//return preg_replace( '/[\\\\]+/', '\\\\\\\\', $string );
|
429 |
return preg_replace( '/[\\\\]+/', '/', $path );
|
430 |
}
|
431 |
|
135 |
|
136 |
$file = $this->file->fgets();
|
137 |
|
138 |
+
|
|
|
|
|
139 |
$this->copyFile( $file );
|
140 |
}
|
141 |
|
170 |
|
171 |
$file = trim( \WPStaging\WPStaging::getWPpath() . $file );
|
172 |
|
173 |
+
$file = wpstg_replace_windows_directory_separator($file);
|
174 |
+
|
175 |
$directory = dirname( $file );
|
176 |
|
177 |
// Directory is excluded
|
224 |
return $this->copyBig( $file, $destination, $this->settings->batchSize );
|
225 |
}
|
226 |
|
|
|
|
|
|
|
227 |
// Attempt to copy
|
228 |
if( !@copy( $file, $destination ) ) {
|
229 |
$errors = error_get_last();
|
239 |
return true;
|
240 |
}
|
241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
/**
|
243 |
* Get wp-content and wp-content/uploads destination dir
|
244 |
* Necessary if these folders were customized and changed from the default ones.
|
247 |
*/
|
248 |
protected function getWpContentPath( $file ) {
|
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'] );
|
256 |
$uploadsRelPath = wpstg_get_rel_upload_dir();
|
257 |
|
258 |
// Get absolute path to wp-content directory e.g srv/www/htdocs/sitename/wp-content
|
259 |
+
$wpContentDir = trailingslashit( WP_CONTENT_DIR );
|
260 |
|
261 |
// 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
|
262 |
if( $uploadsAbsPath != $wpContentDir ) {
|
268 |
//$file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
|
269 |
$file = str_replace( $wpContentDir, ABSPATH . 'wp-content/', $file );
|
270 |
}
|
271 |
+
|
272 |
+
|
273 |
return $file;
|
274 |
}
|
275 |
|
293 |
* @return bool|string
|
294 |
*/
|
295 |
private function getDestination( $file ) {
|
296 |
+
//$file = $this->getMultisiteUploadFolder( $file );
|
297 |
+
$file = wpstg_replace_windows_directory_separator($file);
|
298 |
+
$rootPath = wpstg_replace_windows_directory_separator(\WPStaging\WPStaging::getWPpath());
|
299 |
+
$relativePath = str_replace( $rootPath, null, $file );
|
|
|
|
|
|
|
300 |
$destinationPath = $this->destination . $relativePath;
|
|
|
301 |
$destinationDirectory = dirname( $destinationPath );
|
302 |
|
303 |
if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, wpstg_get_permissions_for_directory(), true ) ) {
|
349 |
* @return boolean
|
350 |
*/
|
351 |
private function isFileExcluded( $file ) {
|
352 |
+
|
353 |
+
$excludedFiles = ( array ) $this->options->excludedFiles;
|
354 |
+
|
355 |
+
$basenameFile = basename( $file );
|
356 |
+
|
357 |
+
|
358 |
+
// Remove .htaccess and web.config from array excludedFiles if staging site is copied to a subdomain
|
359 |
+
//if( $this->isCustomDirectory() ) {
|
360 |
+
if( false === $this->isIdenticalHostname() ) {
|
361 |
+
$excludedFiles = \array_diff( $excludedFiles, array("web.config", ".htaccess") );
|
362 |
+
}
|
363 |
+
|
364 |
+
|
365 |
// If file name exists
|
366 |
+
if( in_array( $basenameFile, $excludedFiles ) ) {
|
367 |
return true;
|
368 |
}
|
369 |
|
370 |
+
// Check for wildcards
|
371 |
+
foreach ($excludedFiles as $pattern){
|
372 |
+
if(fnmatch($pattern, $basenameFile)){
|
373 |
+
return true;
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
// Do not copy wp-config.php if the clone gets updated. This is for security purposes,
|
378 |
// because if the updating process fails, the staging site would not be accessable any longer
|
379 |
if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
|
384 |
return false;
|
385 |
}
|
386 |
|
387 |
+
/**
|
388 |
+
* Check if custom target directory is used
|
389 |
+
* @return boolean
|
390 |
+
*/
|
391 |
+
// private function isCustomDirectory() {
|
392 |
+
//
|
393 |
+
// if( empty( $this->options->cloneDir ) ) {
|
394 |
+
// return false;
|
395 |
+
// }
|
396 |
+
// return true;
|
397 |
+
// }
|
398 |
+
|
399 |
+
/**
|
400 |
+
* Check if production and staging hostname are identical
|
401 |
+
* If they are not identical we assume website is cloned to a subdomain and not into a subfolder
|
402 |
+
* @return boolean
|
403 |
+
*/
|
404 |
+
private function isIdenticalHostname() {
|
405 |
+
// hostname of production site without scheme
|
406 |
+
$siteurl = get_site_url();
|
407 |
+
$url = parse_url( $siteurl );
|
408 |
+
$productionHostname = $url['host'];
|
409 |
+
|
410 |
+
// hostname of staging site without scheme
|
411 |
+
$cloneUrl = empty($this->options->cloneHostname) ? $url : parse_url( $this->options->cloneHostname );
|
412 |
+
$targetHostname = $cloneUrl['host'];
|
413 |
+
|
414 |
+
// Check if target hostname beginns with the production hostname
|
415 |
+
// Only compare the hostname without path
|
416 |
+
if( wpstg_starts_with( $productionHostname, $targetHostname ) ) {
|
417 |
+
return true;
|
418 |
+
}
|
419 |
+
return false;
|
420 |
+
}
|
421 |
+
|
422 |
/**
|
423 |
* Check if certain file is excluded from copying process
|
424 |
*
|
444 |
* @return string
|
445 |
*/
|
446 |
private function sanitizeDirectorySeparator( $path ) {
|
|
|
|
|
|
|
|
|
447 |
return preg_replace( '/[\\\\]+/', '/', $path );
|
448 |
}
|
449 |
|
apps/Backend/Modules/Jobs/Multisite/Data.php
CHANGED
@@ -40,9 +40,12 @@ class Data extends JobExecutable {
|
|
40 |
* Initialize
|
41 |
*/
|
42 |
public function initialize() {
|
43 |
-
$this->db
|
|
|
44 |
$this->prefix = $this->options->prefix;
|
|
|
45 |
$this->getTables();
|
|
|
46 |
// Fix current step
|
47 |
if( 0 == $this->options->currentStep ) {
|
48 |
$this->options->currentStep = 0;
|
@@ -68,7 +71,7 @@ class Data extends JobExecutable {
|
|
68 |
* @return void
|
69 |
*/
|
70 |
protected function calculateTotalSteps() {
|
71 |
-
$this->options->totalSteps =
|
72 |
}
|
73 |
|
74 |
/**
|
@@ -78,8 +81,10 @@ class Data extends JobExecutable {
|
|
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,6 +98,7 @@ class Data extends JobExecutable {
|
|
93 |
if( $this->isRoot() ) {
|
94 |
return false;
|
95 |
}
|
|
|
96 |
// Over limits threshold
|
97 |
if( $this->isOverThreshold() ) {
|
98 |
// Prepare response and save current progress
|
@@ -100,19 +106,23 @@ class Data extends JobExecutable {
|
|
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 |
}
|
@@ -134,20 +144,24 @@ class Data extends JobExecutable {
|
|
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 |
|
@@ -171,14 +185,19 @@ class Data extends JobExecutable {
|
|
171 |
*/
|
172 |
protected function step0() {
|
173 |
$this->log( "Preparing Data Step0: Copy wp-config.php file", Logger::TYPE_INFO );
|
174 |
-
|
175 |
-
$
|
|
|
|
|
|
|
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
|
@@ -187,9 +206,12 @@ class Data extends JobExecutable {
|
|
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 ) ) {
|
@@ -197,6 +219,7 @@ class Data extends JobExecutable {
|
|
197 |
return false;
|
198 |
}
|
199 |
}
|
|
|
200 |
$this->log( "Preparing Data Step0: Successful", Logger::TYPE_INFO );
|
201 |
return true;
|
202 |
}
|
@@ -217,12 +240,14 @@ class Data extends JobExecutable {
|
|
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 |
|
@@ -232,12 +257,16 @@ class Data extends JobExecutable {
|
|
232 |
* @return boolean
|
233 |
*/
|
234 |
protected function alterWpConfig( $source ) {
|
235 |
-
$this->log( "Preparing Data: Alter wp-config.php", Logger::
|
|
|
236 |
$content = file_get_contents( $source );
|
|
|
237 |
if( false === ($content = file_get_contents( $source )) ) {
|
238 |
return false;
|
239 |
}
|
240 |
-
|
|
|
|
|
241 |
$replace = "// ** MySQL settings ** //\r\n
|
242 |
define( 'DB_NAME', '" . DB_NAME . "' );\r\n
|
243 |
/** MySQL database username */\r\n
|
@@ -250,11 +279,14 @@ define( 'DB_HOST', '" . DB_HOST . "' );\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 |
-
|
|
|
255 |
$this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
|
256 |
return false;
|
257 |
}
|
|
|
258 |
return true;
|
259 |
}
|
260 |
|
@@ -264,35 +296,46 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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;
|
@@ -306,6 +349,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,6 +359,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,10 +367,14 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
}
|
@@ -335,7 +384,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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" );
|
@@ -346,11 +397,13 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,10 +412,12 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,30 +427,37 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
}
|
@@ -406,20 +468,38 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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}" );
|
@@ -433,20 +513,32 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
433 |
* @return bool
|
434 |
*/
|
435 |
protected function step5() {
|
436 |
-
$path
|
|
|
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 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
444 |
// Replace URLs
|
445 |
$content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
|
446 |
-
|
|
|
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,15 +550,20 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
-
|
|
|
|
|
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
|
@@ -474,14 +571,20 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
474 |
return false;
|
475 |
}
|
476 |
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
477 |
-
|
|
|
|
|
478 |
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
|
479 |
-
|
|
|
|
|
|
|
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 |
-
|
|
|
485 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
486 |
return false;
|
487 |
}
|
@@ -494,21 +597,26 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
}
|
@@ -518,31 +626,38 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,25 +667,31 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,19 +701,27 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
580 |
* @return bool
|
581 |
*/
|
582 |
protected function step10() {
|
583 |
-
$path
|
|
|
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 |
-
|
|
|
|
|
594 |
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
595 |
-
|
|
|
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;
|
@@ -600,7 +729,8 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
600 |
} else {
|
601 |
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
602 |
}
|
603 |
-
|
|
|
604 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
605 |
return false;
|
606 |
}
|
@@ -613,19 +743,27 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
613 |
* @return bool
|
614 |
*/
|
615 |
protected function step11() {
|
616 |
-
$path
|
|
|
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 |
-
|
|
|
|
|
627 |
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
628 |
-
|
|
|
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,7 +771,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
633 |
} else {
|
634 |
$this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
|
635 |
}
|
636 |
-
|
|
|
|
|
637 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
638 |
return false;
|
639 |
}
|
@@ -646,19 +786,27 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
646 |
* @return bool
|
647 |
*/
|
648 |
protected function step12() {
|
649 |
-
$path
|
|
|
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 |
-
|
|
|
|
|
660 |
$replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
|
661 |
-
|
|
|
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,7 +814,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
666 |
} else {
|
667 |
$this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
|
668 |
}
|
669 |
-
|
|
|
|
|
670 |
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
|
671 |
return false;
|
672 |
}
|
@@ -679,19 +829,27 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
679 |
* @return bool
|
680 |
*/
|
681 |
protected function step13() {
|
682 |
-
$path
|
|
|
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 |
-
|
|
|
|
|
693 |
$replace = "define('MULTISITE',false); // " . $matches[1];
|
694 |
-
|
|
|
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,7 +857,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
699 |
} else {
|
700 |
$this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
|
701 |
}
|
702 |
-
|
|
|
|
|
703 |
$this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
|
704 |
return false;
|
705 |
}
|
@@ -712,41 +872,55 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
-
|
|
|
|
|
741 |
sort( $all_plugins );
|
|
|
|
|
742 |
// Update active_plugins
|
743 |
-
$update
|
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 |
}
|
@@ -757,37 +931,48 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
-
|
|
|
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,27 +982,35 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
-
|
|
|
|
|
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;
|
@@ -831,19 +1024,27 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
831 |
* @return bool
|
832 |
*/
|
833 |
protected function step17() {
|
834 |
-
$path
|
|
|
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 |
-
|
|
|
|
|
845 |
$replace = "define('WP_CACHE',false); // " . $matches[1];
|
846 |
-
|
|
|
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,7 +1052,8 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
851 |
} else {
|
852 |
$this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
|
853 |
}
|
854 |
-
|
|
|
855 |
$this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
856 |
return false;
|
857 |
}
|
@@ -875,7 +1077,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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;
|
@@ -889,6 +1093,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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;
|
@@ -897,7 +1102,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 === @
|
901 |
$this->log( "Preparing Data Step18: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
|
902 |
return false;
|
903 |
}
|
@@ -906,140 +1111,132 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
906 |
}
|
907 |
|
908 |
/**
|
909 |
-
*
|
910 |
-
* @return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
911 |
*/
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
//
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
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 |
-
*
|
1006 |
* @return bool
|
1007 |
*/
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
//
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
//
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
//
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
//
|
1040 |
-
|
1041 |
-
|
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
|
@@ -1049,9 +1246,9 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
1049 |
$strings = new Strings();
|
1050 |
// Get absolute path to uploads folder
|
1051 |
$uploads = wp_upload_dir();
|
1052 |
-
$basedir = $
|
1053 |
// Get relative upload path
|
1054 |
-
$relDir = str_replace( ABSPATH, null, $basedir );
|
1055 |
return $relDir;
|
1056 |
}
|
1057 |
|
@@ -1061,11 +1258,15 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
1061 |
*/
|
1062 |
protected function getNewUploadPath() {
|
1063 |
$uploadPath = get_option( 'upload_path' );
|
|
|
1064 |
if( !$uploadPath ) {
|
1065 |
return false;
|
1066 |
}
|
1067 |
-
|
1068 |
-
$
|
|
|
|
|
|
|
1069 |
return $newUploadPath;
|
1070 |
}
|
1071 |
|
@@ -1074,17 +1275,19 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
-
|
1087 |
-
//$url = trailingslashit( $this->multisiteHomeDomain ) . $multisitePath . '/' . $this->options->cloneDirectoryName;
|
1088 |
return $url;
|
1089 |
}
|
1090 |
|
@@ -1097,6 +1300,7 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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,9 +1314,11 @@ define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
|
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 |
}
|
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;
|
71 |
* @return void
|
72 |
*/
|
73 |
protected function calculateTotalSteps() {
|
74 |
+
$this->options->totalSteps = 21;
|
75 |
}
|
76 |
|
77 |
/**
|
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 |
if( $this->isRoot() ) {
|
99 |
return false;
|
100 |
}
|
101 |
+
|
102 |
// Over limits threshold
|
103 |
if( $this->isOverThreshold() ) {
|
104 |
// Prepare response and save current progress
|
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 |
}
|
144 |
* @return boolean
|
145 |
*/
|
146 |
protected function isRoot() {
|
147 |
+
|
148 |
// Prefix is the same as the one of live site
|
149 |
//$wpdb = WPStaging::getInstance()->get( "wpdb" );
|
150 |
if( $this->db->prefix === $this->prefix ) {
|
151 |
return true;
|
152 |
}
|
153 |
+
|
154 |
// CloneName is empty
|
155 |
$name = ( array ) $this->options->cloneDirectoryName;
|
156 |
if( empty( $name ) ) {
|
157 |
return true;
|
158 |
}
|
159 |
+
|
160 |
// Live domain === Staging domain
|
161 |
if( $this->multisiteHomeDomain . $this->options->cloneDirectoryName === $this->multisiteHomeDomain ) {
|
162 |
return true;
|
163 |
}
|
164 |
+
|
165 |
return false;
|
166 |
}
|
167 |
|
185 |
*/
|
186 |
protected function step0() {
|
187 |
$this->log( "Preparing Data Step0: Copy wp-config.php file", Logger::TYPE_INFO );
|
188 |
+
|
189 |
+
$dir = trailingslashit( dirname( ABSPATH ) );
|
190 |
+
|
191 |
+
$source = $dir . 'wp-config.php';
|
192 |
+
|
193 |
$destination = $this->options->destinationDir . 'wp-config.php';
|
194 |
+
|
195 |
// Check if there is already a valid wp-config.php in root of staging site
|
196 |
if( $this->isValidWpConfig( $destination ) ) {
|
197 |
$this->log( "Preparing Data Step0: Found wp-config.php file in folder {$destination}", Logger::TYPE_INFO );
|
198 |
return true;
|
199 |
}
|
200 |
+
|
201 |
// Check if there is a valid wp-config.php outside root of wp production site
|
202 |
if( $this->isValidWpConfig( $source ) ) {
|
203 |
// Copy it to staging site
|
206 |
return true;
|
207 |
}
|
208 |
}
|
209 |
+
|
210 |
// No valid wp-config.php found so let's copy wp stagings default wp-config.php to staging site
|
211 |
$source = WPSTG_PLUGIN_DIR . "apps/Backend/helpers/wp-config.php";
|
212 |
+
|
213 |
$this->log( "Preparing Data Step0: Copy default wp-config.php file from source {$source} to {$destination}", Logger::TYPE_INFO );
|
214 |
+
|
215 |
if( $this->copy( $source, $destination ) ) {
|
216 |
// add missing db credentials to wp-config.php
|
217 |
if( !$this->alterWpConfig( $destination ) ) {
|
219 |
return false;
|
220 |
}
|
221 |
}
|
222 |
+
|
223 |
$this->log( "Preparing Data Step0: Successful", Logger::TYPE_INFO );
|
224 |
return true;
|
225 |
}
|
240 |
return false;
|
241 |
}
|
242 |
}
|
243 |
+
|
244 |
// Copy file
|
245 |
if( !@copy( $source, $destination ) ) {
|
246 |
$errors = error_get_last();
|
247 |
$this->log( "Preparing Data Step0: Failed to copy {$source}! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
|
248 |
return false;
|
249 |
}
|
250 |
+
|
251 |
return true;
|
252 |
}
|
253 |
|
257 |
* @return boolean
|
258 |
*/
|
259 |
protected function alterWpConfig( $source ) {
|
260 |
+
$this->log( "Preparing Data: Alter wp-config.php", Logger::TYPE_INFO );
|
261 |
+
|
262 |
$content = file_get_contents( $source );
|
263 |
+
|
264 |
if( false === ($content = file_get_contents( $source )) ) {
|
265 |
return false;
|
266 |
}
|
267 |
+
|
268 |
+
$search = "// ** MySQL settings ** //";
|
269 |
+
|
270 |
$replace = "// ** MySQL settings ** //\r\n
|
271 |
define( 'DB_NAME', '" . DB_NAME . "' );\r\n
|
272 |
/** MySQL database username */\r\n
|
279 |
define( 'DB_CHARSET', '" . DB_CHARSET . "' );\r\n
|
280 |
/** The Database Collate type. Don't change this if in doubt. */\r\n
|
281 |
define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
282 |
+
|
283 |
$content = str_replace( $search, $replace, $content );
|
284 |
+
|
285 |
+
if( false === @wpstg_put_contents( $source, $content ) ) {
|
286 |
$this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
|
287 |
return false;
|
288 |
}
|
289 |
+
|
290 |
return true;
|
291 |
}
|
292 |
|
296 |
* @return boolean
|
297 |
*/
|
298 |
protected function isValidWpConfig( $source ) {
|
299 |
+
|
300 |
if( !is_file( $source ) && !is_link( $source ) ) {
|
301 |
$this->log( "Preparing Data Step0: Can not find {$source}", Logger::TYPE_INFO );
|
302 |
return false;
|
303 |
}
|
304 |
+
|
305 |
$content = file_get_contents( $source );
|
306 |
+
|
307 |
if( false === ($content = file_get_contents( $source )) ) {
|
308 |
$this->log( "Preparing Data Step0: Can not read {$source}", Logger::TYPE_INFO );
|
309 |
return false;
|
310 |
}
|
311 |
+
|
312 |
// Get DB_NAME from wp-config.php
|
313 |
preg_match( "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
314 |
+
|
315 |
if( empty( $matches[1] ) ) {
|
316 |
$this->log( "Preparing Data Step0: Can not find DB_NAME in wp-config.php", Logger::TYPE_INFO );
|
317 |
return false;
|
318 |
}
|
319 |
+
|
320 |
// Get DB_USER from wp-config.php
|
321 |
preg_match( "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
322 |
+
|
323 |
if( empty( $matches[1] ) ) {
|
324 |
$this->log( "Preparing Data Step0: Can not find DB_USER in wp-config.php", Logger::TYPE_INFO );
|
325 |
return false;
|
326 |
}
|
327 |
+
|
328 |
// Get DB_PASSWORD from wp-config.php
|
329 |
preg_match( "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
330 |
+
|
331 |
if( empty( $matches[1] ) ) {
|
332 |
$this->log( "Preparing Data Step0: Can not find DB_PASSWORD in wp-config.php", Logger::TYPE_INFO );
|
333 |
return false;
|
334 |
}
|
335 |
+
|
336 |
// Get DB_HOST from wp-config.php
|
337 |
preg_match( "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
338 |
+
|
339 |
if( empty( $matches[1] ) ) {
|
340 |
$this->log( "Preparing Data Step0: Can not find DB_HOST in wp-config.php", Logger::TYPE_INFO );
|
341 |
return false;
|
349 |
*/
|
350 |
protected function step1() {
|
351 |
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
|
352 |
+
|
353 |
// Skip - Table does not exist
|
354 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
355 |
return true;
|
359 |
$this->log( "Preparing Data Step1: Skipping" );
|
360 |
return true;
|
361 |
}
|
362 |
+
|
363 |
$this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
|
364 |
// Replace URLs
|
365 |
$result = $this->db->query(
|
367 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
|
368 |
)
|
369 |
);
|
370 |
+
|
371 |
+
|
372 |
+
|
373 |
// All good
|
374 |
if( $result ) {
|
375 |
return true;
|
376 |
}
|
377 |
+
|
378 |
$this->log( "Preparing Data Step1: Skip updating siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_WARNING );
|
379 |
return true;
|
380 |
}
|
384 |
* @return bool
|
385 |
*/
|
386 |
protected function step2() {
|
387 |
+
|
388 |
$this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
|
389 |
+
|
390 |
// Skip - Table does not exist
|
391 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
392 |
$this->log( "Preparing Data Step2: Skipping" );
|
397 |
$this->log( "Preparing Data Step2: Skipping" );
|
398 |
return true;
|
399 |
}
|
400 |
+
|
401 |
$result = $this->db->query(
|
402 |
$this->db->prepare(
|
403 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
|
404 |
)
|
405 |
);
|
406 |
+
|
407 |
// No errors but no option name such as wpstg_is_staging_site
|
408 |
if( '' === $this->db->last_error && 0 == $result ) {
|
409 |
$result = $this->db->query(
|
412 |
)
|
413 |
);
|
414 |
}
|
415 |
+
|
416 |
// All good
|
417 |
if( $result ) {
|
418 |
return true;
|
419 |
}
|
420 |
+
|
421 |
$this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
422 |
return false;
|
423 |
}
|
427 |
* @return bool
|
428 |
*/
|
429 |
protected function step3() {
|
430 |
+
|
431 |
$this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
|
432 |
+
|
433 |
// Keep Permalinks
|
434 |
if( isset( $this->settings->keepPermalinks ) && $this->settings->keepPermalinks === "1" ) {
|
435 |
$this->log( "Preparing Data Step3: Skipping" );
|
436 |
return true;
|
437 |
}
|
438 |
+
|
439 |
// Skip - Table does not exist
|
440 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
441 |
return true;
|
442 |
}
|
443 |
+
|
444 |
// Skip - Table is not selected or updated
|
445 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
446 |
$this->log( "Preparing Data Step3: Skipping" );
|
447 |
return true;
|
448 |
}
|
449 |
+
|
450 |
$result = $this->db->query(
|
451 |
$this->db->prepare(
|
452 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
|
453 |
)
|
454 |
);
|
455 |
+
|
456 |
// All good
|
457 |
if( $result ) {
|
458 |
return true;
|
459 |
}
|
460 |
+
|
461 |
//$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
462 |
return true;
|
463 |
}
|
468 |
*/
|
469 |
protected function step4() {
|
470 |
$this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
|
471 |
+
|
472 |
// Skip - Table does not exist
|
473 |
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
|
474 |
return true;
|
475 |
}
|
476 |
+
|
477 |
// Skip - Table is not selected or updated
|
478 |
if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
|
479 |
$this->log( "Preparing Data Step4: Skipping" );
|
480 |
return true;
|
481 |
}
|
482 |
+
|
483 |
+
// Change the base table prefix of the main network site
|
484 |
+
$this->debugLog( "Preparing Data Step4: UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, {$this->db->base_prefix}, {$this->prefix}) WHERE meta_key LIKE {$this->db->base_prefix}_%" );
|
485 |
+
$update = $this->db->query(
|
486 |
+
$this->db->prepare(
|
487 |
+
"UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->base_prefix, $this->prefix, $this->db->base_prefix . "_%"
|
488 |
+
)
|
489 |
+
);
|
490 |
+
|
491 |
+
if( false === $update ) {
|
492 |
+
$this->log( "Preparing Data Step4a: Skip updating {$this->prefix}usermeta meta_key database base_prefix; {$this->db->last_error}", Logger::TYPE_INFO );
|
493 |
+
}
|
494 |
+
|
495 |
+
// Change the table prefix of the child network site
|
496 |
+
$this->debugLog( "Preparing Data Step4: UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, {$this->db->prefix}, {$this->prefix}) WHERE meta_key LIKE {$this->db->prefix}_%" );
|
497 |
$update = $this->db->query(
|
498 |
$this->db->prepare(
|
499 |
"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 . "_%"
|
500 |
)
|
501 |
);
|
502 |
+
|
503 |
if( false === $update ) {
|
504 |
$this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
|
505 |
$this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
|
513 |
* @return bool
|
514 |
*/
|
515 |
protected function step5() {
|
516 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
517 |
+
|
518 |
$this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
|
519 |
if( false === ($content = file_get_contents( $path )) ) {
|
520 |
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
521 |
return false;
|
522 |
}
|
523 |
+
|
524 |
// Replace table prefix
|
525 |
+
$pattern = '/\$table_prefix\s*=\s*(.*).*/';
|
526 |
+
$replacement = '$table_prefix = \'' . $this->prefix . '\'; // Changed by WP Staging';
|
527 |
+
$content = preg_replace( $pattern, $replacement, $content );
|
528 |
+
|
529 |
+
if( null === $content ) {
|
530 |
+
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
531 |
+
return false;
|
532 |
+
}
|
533 |
+
|
534 |
// Replace URLs
|
535 |
$content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
|
536 |
+
|
537 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
538 |
$this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
|
539 |
return false;
|
540 |
}
|
541 |
+
|
542 |
return true;
|
543 |
}
|
544 |
|
550 |
* @return bool
|
551 |
*/
|
552 |
protected function step6() {
|
553 |
+
|
554 |
if( !$this->isSubDir() ) {
|
555 |
$this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
|
556 |
return true;
|
557 |
}
|
558 |
+
|
559 |
+
$path = $this->options->destinationDir . "index.php";
|
560 |
+
|
561 |
if( false === ($content = file_get_contents( $path )) ) {
|
562 |
$this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
|
563 |
return false;
|
564 |
}
|
565 |
+
|
566 |
+
|
567 |
if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
|
568 |
$this->log(
|
569 |
"Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
|
571 |
return false;
|
572 |
}
|
573 |
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
574 |
+
|
575 |
+
$pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);.*/";
|
576 |
+
|
577 |
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
|
578 |
+
//$replace .= " // Changed by WP-Staging";
|
579 |
+
|
580 |
+
|
581 |
+
|
582 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
583 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
|
584 |
return false;
|
585 |
}
|
586 |
+
|
587 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
588 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
589 |
return false;
|
590 |
}
|
597 |
* @return bool
|
598 |
*/
|
599 |
protected function step7() {
|
600 |
+
|
601 |
$this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
|
602 |
+
|
603 |
// Skip - Table does not exist
|
604 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
605 |
return true;
|
606 |
}
|
607 |
+
|
608 |
// Skip - Table is not selected or updated
|
609 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
610 |
$this->log( "Preparing Data Step7: Skipping" );
|
611 |
return true;
|
612 |
}
|
613 |
+
|
614 |
$result = $this->db->query(
|
615 |
$this->db->prepare(
|
616 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
|
617 |
)
|
618 |
);
|
619 |
+
|
620 |
$this->Log( "Preparing Data Step7: Finished successfully" );
|
621 |
return true;
|
622 |
}
|
626 |
* @return bool
|
627 |
*/
|
628 |
protected function step8() {
|
629 |
+
|
630 |
$this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
|
631 |
+
|
632 |
// Keep Permalinks
|
633 |
if( isset( $this->settings->keepPermalinks ) && $this->settings->keepPermalinks === "1" ) {
|
634 |
$this->log( "Preparing Data Step8: Skipping" );
|
635 |
return true;
|
636 |
}
|
637 |
+
|
638 |
// Skip - Table does not exist
|
639 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
640 |
return true;
|
641 |
}
|
642 |
+
|
643 |
// Skip - Table is not selected or updated
|
644 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
645 |
$this->log( "Preparing Data Step8: Skipping" );
|
646 |
return true;
|
647 |
}
|
648 |
+
|
649 |
$result = $this->db->query(
|
650 |
$this->db->prepare(
|
651 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
|
652 |
)
|
653 |
);
|
654 |
+
|
655 |
// All good
|
656 |
if( $result ) {
|
657 |
$this->Log( "Preparing Data Step8: Finished successfully" );
|
658 |
return true;
|
659 |
}
|
660 |
+
|
661 |
$this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
662 |
return true;
|
663 |
}
|
667 |
* @return bool
|
668 |
*/
|
669 |
protected function step9() {
|
670 |
+
|
671 |
$this->log( "Preparing Data Step9: Set staging site to noindex" );
|
672 |
+
|
673 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
674 |
return true;
|
675 |
}
|
676 |
+
|
677 |
// Skip - Table is not selected or updated
|
678 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
679 |
$this->log( "Preparing Data Step9: Skipping" );
|
680 |
return true;
|
681 |
}
|
682 |
+
|
683 |
$result = $this->db->query(
|
684 |
$this->db->prepare(
|
685 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
|
686 |
)
|
687 |
);
|
688 |
+
|
689 |
// All good
|
690 |
if( $result ) {
|
691 |
$this->Log( "Preparing Data Step9: Finished successfully" );
|
692 |
return true;
|
693 |
}
|
694 |
+
|
695 |
$this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
|
696 |
return true;
|
697 |
}
|
701 |
* @return bool
|
702 |
*/
|
703 |
protected function step10() {
|
704 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
705 |
+
|
706 |
$this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
|
707 |
+
|
708 |
if( false === ($content = file_get_contents( $path )) ) {
|
709 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
710 |
return false;
|
711 |
}
|
712 |
+
|
713 |
+
|
714 |
// Get WP_HOME from wp-config.php
|
715 |
preg_match( "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
716 |
+
|
717 |
if( !empty( $matches[1] ) ) {
|
718 |
$matches[1];
|
719 |
+
|
720 |
+
$pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);.*/";
|
721 |
+
|
722 |
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
723 |
+
//$replace .= " // Changed by WP-Staging";
|
724 |
+
|
725 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
726 |
$this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
|
727 |
return false;
|
729 |
} else {
|
730 |
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
731 |
}
|
732 |
+
|
733 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
734 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
735 |
return false;
|
736 |
}
|
743 |
* @return bool
|
744 |
*/
|
745 |
protected function step11() {
|
746 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
747 |
+
|
748 |
$this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
|
749 |
+
|
750 |
if( false === ($content = file_get_contents( $path )) ) {
|
751 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
752 |
return false;
|
753 |
}
|
754 |
+
|
755 |
+
|
756 |
// Get WP_SITEURL from wp-config.php
|
757 |
preg_match( "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
758 |
+
|
759 |
if( !empty( $matches[1] ) ) {
|
760 |
$matches[1];
|
761 |
+
|
762 |
+
$pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);.*/";
|
763 |
+
|
764 |
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
765 |
+
//$replace .= " // Changed by WP-Staging";
|
766 |
+
|
767 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
768 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
|
769 |
return false;
|
771 |
} else {
|
772 |
$this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
|
773 |
}
|
774 |
+
|
775 |
+
|
776 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
777 |
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
778 |
return false;
|
779 |
}
|
786 |
* @return bool
|
787 |
*/
|
788 |
protected function step12() {
|
789 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
790 |
+
|
791 |
$this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
|
792 |
+
|
793 |
if( false === ($content = file_get_contents( $path )) ) {
|
794 |
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
795 |
return false;
|
796 |
}
|
797 |
+
|
798 |
+
|
799 |
// Get WP_SITEURL from wp-config.php
|
800 |
preg_match( "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
801 |
+
|
802 |
if( !empty( $matches[1] ) ) {
|
803 |
$matches[1];
|
804 |
+
|
805 |
+
$pattern = "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
|
806 |
+
|
807 |
$replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
|
808 |
+
//$replace .= " // Changed by WP-Staging";
|
809 |
+
|
810 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
811 |
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
|
812 |
return false;
|
814 |
} else {
|
815 |
$this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
|
816 |
}
|
817 |
+
|
818 |
+
|
819 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
820 |
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
|
821 |
return false;
|
822 |
}
|
829 |
* @return bool
|
830 |
*/
|
831 |
protected function step13() {
|
832 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
833 |
+
|
834 |
$this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
|
835 |
+
|
836 |
if( false === ($content = file_get_contents( $path )) ) {
|
837 |
$this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
838 |
return false;
|
839 |
}
|
840 |
+
|
841 |
+
|
842 |
// Get WP_SITEURL from wp-config.php
|
843 |
preg_match( "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
844 |
+
|
845 |
if( !empty( $matches[1] ) ) {
|
846 |
$matches[1];
|
847 |
+
|
848 |
+
$pattern = "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
|
849 |
+
|
850 |
$replace = "define('MULTISITE',false); // " . $matches[1];
|
851 |
+
//$replace .= " // Changed by WP-Staging";
|
852 |
+
|
853 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
854 |
$this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
|
855 |
return false;
|
857 |
} else {
|
858 |
$this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
|
859 |
}
|
860 |
+
|
861 |
+
|
862 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
863 |
$this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
|
864 |
return false;
|
865 |
}
|
872 |
* Merge both arrays and copy them to the staging site into active_plugins
|
873 |
*/
|
874 |
protected function step14() {
|
875 |
+
|
876 |
+
|
877 |
$this->log( "Data Crunching Step14: Updating active_plugins" );
|
878 |
+
|
879 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
880 |
$this->log( 'Data Crunching Step14: Fatal Error ' . $this->prefix . 'options does not exist' );
|
881 |
$this->returnException( 'Data Crunching Step14: Fatal Error ' . $this->prefix . 'options does not exist' );
|
882 |
return false;
|
883 |
}
|
884 |
+
|
885 |
// Skip - Table is not selected or updated
|
886 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
887 |
$this->log( "Preparing Data Step14: Skipping" );
|
888 |
return true;
|
889 |
}
|
890 |
+
|
891 |
// Get active_plugins value from sub site options table
|
892 |
$active_plugins = $this->db->get_var( "SELECT option_value FROM {$this->db->prefix}options WHERE option_name = 'active_plugins' " );
|
893 |
+
|
894 |
if( !$active_plugins ) {
|
895 |
$this->log( "Data Crunching Step14: Option active_plugins are empty " );
|
896 |
$active_plugins = array();
|
897 |
}
|
898 |
// Get active_sitewide_plugins value from main multisite wp_sitemeta table
|
899 |
$active_sitewide_plugins = $this->db->get_var( "SELECT meta_value FROM {$this->db->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
|
900 |
+
|
901 |
if( !$active_sitewide_plugins ) {
|
902 |
$this->log( "Data Crunching Step14: Options {$this->db->base_prefix}active_sitewide_plugins is empty " );
|
903 |
$active_sitewide_plugins = array();
|
904 |
}
|
905 |
+
|
906 |
$active_sitewide_plugins = unserialize( $active_sitewide_plugins );
|
907 |
$active_plugins = unserialize( $active_plugins );
|
908 |
+
|
909 |
+
$all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
|
910 |
+
|
911 |
sort( $all_plugins );
|
912 |
+
|
913 |
+
|
914 |
// Update active_plugins
|
915 |
+
$update = $this->db->query(
|
916 |
"UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
|
917 |
);
|
918 |
+
|
919 |
if( false === $update ) {
|
920 |
$this->log( "Data Crunching Step14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
|
921 |
return false;
|
922 |
}
|
923 |
+
|
924 |
$this->log( "Data Crunching Step14: Successful!" );
|
925 |
return true;
|
926 |
}
|
931 |
*/
|
932 |
protected function step15() {
|
933 |
$this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
|
934 |
+
|
935 |
// Skip - Table does not exist
|
936 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
937 |
return true;
|
938 |
}
|
939 |
+
|
940 |
// Skip - Table is not selected or updated
|
941 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
942 |
$this->log( "Preparing Data Step4: Skipping" );
|
943 |
return true;
|
944 |
}
|
945 |
+
|
946 |
+
|
947 |
$this->log( "Updating db option_names in {$this->prefix}options. " );
|
948 |
+
|
949 |
// Filter the rows below. Do not update them!
|
950 |
$filters = array(
|
951 |
'wp_mail_smtp',
|
952 |
'wp_mail_smtp_version',
|
953 |
'wp_mail_smtp_debug',
|
954 |
);
|
955 |
+
|
956 |
$filters = apply_filters( 'wpstg_data_excl_rows', $filters );
|
957 |
+
|
958 |
+
$where = "";
|
959 |
foreach ( $filters as $filter ) {
|
960 |
$where .= " AND option_name <> '" . $filter . "'";
|
961 |
}
|
962 |
+
|
963 |
$updateOptions = $this->db->query(
|
964 |
$this->db->prepare(
|
965 |
"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 . "_%"
|
966 |
)
|
967 |
);
|
968 |
+
|
969 |
if( false === $updateOptions ) {
|
970 |
$this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_WARNING );
|
971 |
//$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
972 |
return true;
|
973 |
}
|
974 |
+
|
975 |
+
|
976 |
return true;
|
977 |
}
|
978 |
|
982 |
*/
|
983 |
protected function step16() {
|
984 |
$this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
|
985 |
+
|
986 |
// Skip - Table does not exist
|
987 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
988 |
return true;
|
989 |
}
|
990 |
+
|
991 |
$newUploadPath = $this->getNewUploadPath();
|
992 |
+
|
993 |
if( false === $newUploadPath ) {
|
994 |
$this->log( "Preparing Data Step16: Skipping" );
|
995 |
return true;
|
996 |
}
|
997 |
+
|
998 |
// Skip - Table is not selected or updated
|
999 |
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1000 |
$this->log( "Preparing Data Step16: Skipping" );
|
1001 |
return true;
|
1002 |
}
|
1003 |
+
|
1004 |
+
$error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
|
1005 |
+
|
1006 |
$this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
|
1007 |
+
|
1008 |
$updateOptions = $this->db->query(
|
1009 |
$this->db->prepare(
|
1010 |
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
|
1011 |
)
|
1012 |
);
|
1013 |
+
|
1014 |
if( false === $updateOptions ) {
|
1015 |
$this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
|
1016 |
return true;
|
1024 |
* @return bool
|
1025 |
*/
|
1026 |
protected function step17() {
|
1027 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
1028 |
+
|
1029 |
$this->log( "Preparing Data Step17: Set WP_CACHE in wp-config.php to false" );
|
1030 |
+
|
1031 |
if( false === ($content = file_get_contents( $path )) ) {
|
1032 |
$this->log( "Preparing Data Step17: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
1033 |
return false;
|
1034 |
}
|
1035 |
+
|
1036 |
+
|
1037 |
// Get WP_CACHE from wp-config.php
|
1038 |
preg_match( "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1039 |
+
|
1040 |
if( !empty( $matches[1] ) ) {
|
1041 |
$matches[1];
|
1042 |
+
|
1043 |
+
$pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);.*/";
|
1044 |
+
|
1045 |
$replace = "define('WP_CACHE',false); // " . $matches[1];
|
1046 |
+
//$replace .= " // Changed by WP-Staging";
|
1047 |
+
|
1048 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1049 |
$this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
|
1050 |
return false;
|
1052 |
} else {
|
1053 |
$this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
|
1054 |
}
|
1055 |
+
|
1056 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
1057 |
$this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
1058 |
return false;
|
1059 |
}
|
1077 |
$uploadFolder = $this->getMultisiteUploadFolder();
|
1078 |
if( !empty( $matches[0] ) ) {
|
1079 |
$pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
|
1080 |
+
|
1081 |
$replace = "define('UPLOADS', '" . $uploadFolder . "');";
|
1082 |
+
$this->log( "Preparing Data Step18: Change UPLOADS constant in wp-config.php to {$uploadFolder}." );
|
1083 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1084 |
$this->log( "Preparing Data Step 18: Failed to change UPLOADS", Logger::TYPE_ERROR );
|
1085 |
return false;
|
1093 |
$pattern = "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/";
|
1094 |
$replace = "define('UPLOADS', '" . $uploadFolder . "'); \n" .
|
1095 |
"if ( ! defined( 'ABSPATH' ) )";
|
1096 |
+
$this->log( "Preparing Data Step18: Change UPLOADS constant in wp-config.php to {$uploadFolder}." );
|
1097 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1098 |
$this->log( "Preparing Data Step 18: Failed to change UPLOADS", Logger::TYPE_ERROR );
|
1099 |
return false;
|
1102 |
$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 );
|
1103 |
}
|
1104 |
}
|
1105 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
1106 |
$this->log( "Preparing Data Step18: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
|
1107 |
return false;
|
1108 |
}
|
1111 |
}
|
1112 |
|
1113 |
/**
|
1114 |
+
* Save hostname of parent production site in option_name wpstg_connection
|
1115 |
+
* @return boolean
|
1116 |
+
*/
|
1117 |
+
protected function step19() {
|
1118 |
+
|
1119 |
+
$table = $this->prefix . 'options';
|
1120 |
+
|
1121 |
+
$siteurl = get_site_url();
|
1122 |
+
|
1123 |
+
$connection = json_encode( array('prodHostname' => $siteurl) );
|
1124 |
+
|
1125 |
+
$data = array(
|
1126 |
+
'option_name' => 'wpstg_connection',
|
1127 |
+
'option_value' => $connection
|
1128 |
+
);
|
1129 |
+
|
1130 |
+
$format = array('%s', '%s');
|
1131 |
+
|
1132 |
+
$result = $this->db->replace( $table, $data, $format );
|
1133 |
+
|
1134 |
+
if( false === $result ) {
|
1135 |
+
$this->Log( "Preparing Data Step19: Could not save {$siteurl} in {$table}", Logger::TYPE_ERROR );
|
1136 |
+
}
|
1137 |
+
return true;
|
1138 |
+
}
|
1139 |
+
|
1140 |
+
/**
|
1141 |
+
* Add option_name wpstg_execute and set it to true
|
1142 |
+
* This option is used to determine if the staging website has not been loaded initiall for executing certain custom actions from \WPStaging\initActions()
|
1143 |
+
* @return boolean
|
1144 |
*/
|
1145 |
+
protected function step20() {
|
1146 |
+
|
1147 |
+
$table = $this->prefix . 'options';
|
1148 |
+
|
1149 |
+
// Skip - Table does not exist
|
1150 |
+
if( false === $this->isTable( $table ) ) {
|
1151 |
+
return true;
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
|
1155 |
+
$result = $this->db->query(
|
1156 |
+
$this->db->prepare(
|
1157 |
+
"INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_execute',%s) ON DUPLICATE KEY UPDATE option_value = %s", "true", "true"
|
1158 |
+
)
|
1159 |
+
);
|
1160 |
+
|
1161 |
+
if( false === $result ) {
|
1162 |
+
$this->Log( "Preparing Data Step20: Could not save wpstg_execute in {$table}", Logger::TYPE_ERROR );
|
1163 |
+
}
|
1164 |
+
return true;
|
1165 |
+
}
|
1166 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1167 |
/**
|
1168 |
+
* Preserve data and prevents data in wp_options from beeing cloned to staging site
|
1169 |
* @return bool
|
1170 |
*/
|
1171 |
+
protected function step21() {
|
1172 |
+
$this->log( "Preparing Data Step21: Preserve Data in " . $this->prefix . "options" );
|
1173 |
+
|
1174 |
+
// Skip - Table does not exist
|
1175 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
1176 |
+
return true;
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
// Skip - Table is not selected or updated
|
1180 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1181 |
+
$this->log( "Preparing Data Step21: Skipped" );
|
1182 |
+
return true;
|
1183 |
+
}
|
1184 |
+
|
1185 |
+
$sql = '';
|
1186 |
+
|
1187 |
+
$preserved_option_names = array('wpstg_existing_clones_beta');
|
1188 |
+
|
1189 |
+
$preserved_option_names = apply_filters( 'wpstg_preserved_options_cloning', $preserved_option_names );
|
1190 |
+
$preserved_options_escaped = esc_sql( $preserved_option_names );
|
1191 |
+
|
1192 |
+
$preserved_options_data = array();
|
1193 |
+
|
1194 |
+
// Get preserved data in wp_options tables
|
1195 |
+
$table = $this->db->prefix . 'options';
|
1196 |
+
$preserved_options_data[$this->prefix . 'options'] = $this->db->get_results(
|
1197 |
+
sprintf(
|
1198 |
+
"SELECT * FROM `{$table}` WHERE `option_name` IN ('%s')", implode( "','", $preserved_options_escaped )
|
1199 |
+
), ARRAY_A
|
1200 |
+
);
|
1201 |
+
|
1202 |
+
// Create preserved data queries for options tables
|
1203 |
+
foreach ( $preserved_options_data as $key => $value ) {
|
1204 |
+
if( false === empty( $value ) ) {
|
1205 |
+
foreach ( $value as $option ) {
|
1206 |
+
$sql .= $this->db->prepare(
|
1207 |
+
"DELETE FROM `{$key}` WHERE `option_name` = %s;\n", $option['option_name']
|
1208 |
+
);
|
1209 |
+
|
1210 |
+
$sql .= $this->db->prepare(
|
1211 |
+
"INSERT INTO `{$key}` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s );\n", $option['option_name'], $option['option_value'], $option['autoload']
|
1212 |
+
);
|
1213 |
+
}
|
1214 |
+
}
|
1215 |
+
}
|
1216 |
+
|
1217 |
+
$this->debugLog( "Preparing Data Step21: Preserve values " . json_encode( $preserved_options_data ), Logger::TYPE_INFO );
|
1218 |
+
|
1219 |
+
$this->executeSql( $sql );
|
1220 |
+
|
1221 |
+
$this->log( "Preparing Data Step21: Successful!" );
|
1222 |
+
return true;
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
/**
|
1226 |
+
* Execute a batch of sql queries
|
1227 |
+
* @param string $sqlbatch
|
1228 |
+
*/
|
1229 |
+
private function executeSql( $sqlbatch ) {
|
1230 |
+
$queries = array_filter( explode( ";\n", $sqlbatch ) );
|
1231 |
+
|
1232 |
+
foreach ( $queries as $query ) {
|
1233 |
+
if( false === $this->db->query( $query ) ) {
|
1234 |
+
$this->log( "Data Crunching Warning: Can not execute query {$query}", Logger::TYPE_WARNING );
|
1235 |
+
}
|
1236 |
+
}
|
1237 |
+
return true;
|
1238 |
+
}
|
1239 |
+
|
1240 |
/**
|
1241 |
* Get relative path to the uploads media folder of multisite e.g.
|
1242 |
* wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
|
1246 |
$strings = new Strings();
|
1247 |
// Get absolute path to uploads folder
|
1248 |
$uploads = wp_upload_dir();
|
1249 |
+
$basedir = $uploads['basedir'];
|
1250 |
// Get relative upload path
|
1251 |
+
$relDir = str_replace( wpstg_replace_windows_directory_separator( ABSPATH ), null, wpstg_replace_windows_directory_separator( $basedir ) );
|
1252 |
return $relDir;
|
1253 |
}
|
1254 |
|
1258 |
*/
|
1259 |
protected function getNewUploadPath() {
|
1260 |
$uploadPath = get_option( 'upload_path' );
|
1261 |
+
|
1262 |
if( !$uploadPath ) {
|
1263 |
return false;
|
1264 |
}
|
1265 |
+
|
1266 |
+
$customSlug = str_replace( wpstg_replace_windows_directory_separator( \WPStaging\WPStaging::getWPpath() ), '', wpstg_replace_windows_directory_separator( $uploadPath ) );
|
1267 |
+
|
1268 |
+
$newUploadPath = wpstg_replace_windows_directory_separator( \WPStaging\WPStaging::getWPpath() ) . $this->options->cloneDirectoryName . '/' . $customSlug;
|
1269 |
+
|
1270 |
return $newUploadPath;
|
1271 |
}
|
1272 |
|
1275 |
* @return string
|
1276 |
*/
|
1277 |
protected function getStagingSiteUrl() {
|
1278 |
+
|
1279 |
if( !empty( $this->options->cloneHostname ) ) {
|
1280 |
return $this->options->cloneHostname;
|
1281 |
}
|
1282 |
+
|
1283 |
if( $this->isSubDir() ) {
|
1284 |
return trailingslashit( $this->multisiteHomeDomain ) . trailingslashit( $this->getSubDir() ) . $this->options->cloneDirectoryName;
|
1285 |
}
|
1286 |
+
|
1287 |
// Get the path to the main multisite without appending and trailingslash e.g. wordpress
|
1288 |
$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : '/';
|
1289 |
$url = rtrim( $this->multisiteHomeDomain, '/\\' ) . $multisitePath . $this->options->cloneDirectoryName;
|
1290 |
+
|
|
|
1291 |
return $url;
|
1292 |
}
|
1293 |
|
1300 |
// This is happening much more often than you would expect
|
1301 |
$siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
|
1302 |
$home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
|
1303 |
+
|
1304 |
if( $home !== $siteurl ) {
|
1305 |
return true;
|
1306 |
}
|
1314 |
protected function getSubDir() {
|
1315 |
$home = get_option( 'home' );
|
1316 |
$siteurl = get_option( 'siteurl' );
|
1317 |
+
|
1318 |
if( empty( $home ) || empty( $siteurl ) ) {
|
1319 |
return '';
|
1320 |
}
|
1321 |
+
|
1322 |
$dir = str_replace( $home, '', $siteurl );
|
1323 |
return str_replace( '/', '', $dir );
|
1324 |
}
|
apps/Backend/Modules/Jobs/Multisite/DataExternal.php
CHANGED
@@ -1,1135 +1,1430 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\Utils\Logger;
|
11 |
-
use WPStaging\WPStaging;
|
12 |
-
use WPStaging\Utils\Helper;
|
13 |
-
use WPStaging\Utils\Multisite;
|
14 |
-
use WPStaging\Utils\Strings;
|
15 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Class Data
|
19 |
-
* @package WPStaging\Backend\Modules\Jobs
|
20 |
-
*/
|
21 |
-
class DataExternal extends JobExecutable {
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @var \wpdb
|
25 |
-
*/
|
26 |
-
private $db;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var string
|
30 |
-
*/
|
31 |
-
private $prefix;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Tables e.g wpstg3_options
|
35 |
-
* @var array
|
36 |
-
*/
|
37 |
-
private $tables;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Initialize
|
41 |
-
*/
|
42 |
-
public function initialize() {
|
43 |
-
$this->db = $this->getStagingDB();
|
44 |
-
$this->productionDb = WPStaging::getInstance()->get( "wpdb" );
|
45 |
-
$this->prefix = $this->options->prefix;
|
46 |
-
$this->db->prefix = $this->options->databasePrefix;
|
47 |
-
|
48 |
-
$this->getTables();
|
49 |
-
|
50 |
-
// Fix current step
|
51 |
-
if( 0 == $this->options->currentStep ) {
|
52 |
-
$this->options->currentStep = 0;
|
53 |
-
}
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Get database object to interact with
|
58 |
-
*/
|
59 |
-
private function getStagingDB() {
|
60 |
-
return new \wpdb( $this->options->databaseUser, $this->options->databasePassword, $this->options->databaseDatabase, $this->options->databaseServer );
|
61 |
-
}
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Get a list of tables to copy
|
65 |
-
*/
|
66 |
-
private function getTables() {
|
67 |
-
$strings = new Strings();
|
68 |
-
$this->tables = array();
|
69 |
-
foreach ( $this->options->tables as $table ) {
|
70 |
-
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->productionDb->prefix, null, $table );
|
71 |
-
}
|
72 |
-
// Add extra global tables from main multisite (wpstgx_users and wpstgx_usermeta)
|
73 |
-
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->productionDb->prefix, null, 'users' );
|
74 |
-
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->productionDb->prefix, null, 'usermeta' );
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
79 |
-
* @return void
|
80 |
-
*/
|
81 |
-
protected function calculateTotalSteps() {
|
82 |
-
$this->options->totalSteps =
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Start Module
|
87 |
-
* @return object
|
88 |
-
*/
|
89 |
-
public function start() {
|
90 |
-
// Execute steps
|
91 |
-
$this->run();
|
92 |
-
|
93 |
-
// Save option, progress
|
94 |
-
$this->saveOptions();
|
95 |
-
|
96 |
-
return ( object ) $this->response;
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Execute the Current Step
|
101 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
102 |
-
* @return bool
|
103 |
-
*/
|
104 |
-
protected function execute() {
|
105 |
-
// Fatal error. Let this happen never and break here immediately
|
106 |
-
if( $this->isRoot() ) {
|
107 |
-
return false;
|
108 |
-
}
|
109 |
-
|
110 |
-
// Over limits threshold
|
111 |
-
if( $this->isOverThreshold() ) {
|
112 |
-
// Prepare response and save current progress
|
113 |
-
$this->prepareResponse( false, false );
|
114 |
-
$this->saveOptions();
|
115 |
-
return false;
|
116 |
-
}
|
117 |
-
|
118 |
-
// No more steps, finished
|
119 |
-
if( $this->isFinished() ) {
|
120 |
-
$this->prepareResponse( true, false );
|
121 |
-
return false;
|
122 |
-
}
|
123 |
-
|
124 |
-
// Execute step
|
125 |
-
$stepMethodName = "step" . $this->options->currentStep;
|
126 |
-
if( !$this->{$stepMethodName}() ) {
|
127 |
-
$this->prepareResponse( false, false );
|
128 |
-
return false;
|
129 |
-
}
|
130 |
-
|
131 |
-
// Prepare Response
|
132 |
-
$this->prepareResponse();
|
133 |
-
|
134 |
-
// Not finished
|
135 |
-
return true;
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Checks Whether There is Any Job to Execute or Not
|
140 |
-
* @return bool
|
141 |
-
*/
|
142 |
-
protected function isFinished() {
|
143 |
-
return (
|
144 |
-
$this->options->
|
145 |
-
|
146 |
-
)
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
*
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
//
|
157 |
-
//
|
158 |
-
//
|
159 |
-
//
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
*
|
177 |
-
* @
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
*
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
}
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
//
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
//
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
if(
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
$
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
return
|
466 |
-
}
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
$
|
478 |
-
|
479 |
-
|
480 |
-
$
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
$this->
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
$this->
|
513 |
-
return
|
514 |
-
}
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
)
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
*
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
$this->log( "Preparing Data
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
return
|
613 |
-
}
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
$
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
}
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
$this->log( "Data
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
if(
|
844 |
-
$this->log( "Preparing Data
|
845 |
-
return
|
846 |
-
}
|
847 |
-
|
848 |
-
|
849 |
-
//
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
return true;
|
896 |
-
}
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
//
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
$
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
}
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
$this->
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
$
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
}
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
1114 |
-
return
|
1115 |
-
}
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
*
|
1122 |
-
|
1123 |
-
|
1124 |
-
|
1125 |
-
$
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\Utils\Logger;
|
11 |
+
use WPStaging\WPStaging;
|
12 |
+
use WPStaging\Utils\Helper;
|
13 |
+
use WPStaging\Utils\Multisite;
|
14 |
+
use WPStaging\Utils\Strings;
|
15 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Class Data
|
19 |
+
* @package WPStaging\Backend\Modules\Jobs
|
20 |
+
*/
|
21 |
+
class DataExternal extends JobExecutable {
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var \wpdb
|
25 |
+
*/
|
26 |
+
private $db;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
private $prefix;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Tables e.g wpstg3_options
|
35 |
+
* @var array
|
36 |
+
*/
|
37 |
+
private $tables;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Initialize
|
41 |
+
*/
|
42 |
+
public function initialize() {
|
43 |
+
$this->db = $this->getStagingDB();
|
44 |
+
$this->productionDb = WPStaging::getInstance()->get( "wpdb" );
|
45 |
+
$this->prefix = $this->options->prefix;
|
46 |
+
$this->db->prefix = $this->options->databasePrefix;
|
47 |
+
|
48 |
+
$this->getTables();
|
49 |
+
|
50 |
+
// Fix current step
|
51 |
+
if( 0 == $this->options->currentStep ) {
|
52 |
+
$this->options->currentStep = 0;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get database object to interact with
|
58 |
+
*/
|
59 |
+
private function getStagingDB() {
|
60 |
+
return new \wpdb( $this->options->databaseUser, str_replace( "\\\\", "\\", $this->options->databasePassword ), $this->options->databaseDatabase, $this->options->databaseServer );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Get a list of tables to copy
|
65 |
+
*/
|
66 |
+
private function getTables() {
|
67 |
+
$strings = new Strings();
|
68 |
+
$this->tables = array();
|
69 |
+
foreach ( $this->options->tables as $table ) {
|
70 |
+
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->productionDb->prefix, null, $table );
|
71 |
+
}
|
72 |
+
// Add extra global tables from main multisite (wpstgx_users and wpstgx_usermeta)
|
73 |
+
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->productionDb->prefix, null, 'users' );
|
74 |
+
$this->tables[] = $this->options->prefix . $strings->str_replace_first( $this->productionDb->prefix, null, 'usermeta' );
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
79 |
+
* @return void
|
80 |
+
*/
|
81 |
+
protected function calculateTotalSteps() {
|
82 |
+
$this->options->totalSteps = 22;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Start Module
|
87 |
+
* @return object
|
88 |
+
*/
|
89 |
+
public function start() {
|
90 |
+
// Execute steps
|
91 |
+
$this->run();
|
92 |
+
|
93 |
+
// Save option, progress
|
94 |
+
$this->saveOptions();
|
95 |
+
|
96 |
+
return ( object ) $this->response;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Execute the Current Step
|
101 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
102 |
+
* @return bool
|
103 |
+
*/
|
104 |
+
protected function execute() {
|
105 |
+
// Fatal error. Let this happen never and break here immediately
|
106 |
+
if( $this->isRoot() ) {
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
|
110 |
+
// Over limits threshold
|
111 |
+
if( $this->isOverThreshold() ) {
|
112 |
+
// Prepare response and save current progress
|
113 |
+
$this->prepareResponse( false, false );
|
114 |
+
$this->saveOptions();
|
115 |
+
return false;
|
116 |
+
}
|
117 |
+
|
118 |
+
// No more steps, finished
|
119 |
+
if( $this->isFinished() ) {
|
120 |
+
$this->prepareResponse( true, false );
|
121 |
+
return false;
|
122 |
+
}
|
123 |
+
|
124 |
+
// Execute step
|
125 |
+
$stepMethodName = "step" . $this->options->currentStep;
|
126 |
+
if( !$this->{$stepMethodName}() ) {
|
127 |
+
$this->prepareResponse( false, false );
|
128 |
+
return false;
|
129 |
+
}
|
130 |
+
|
131 |
+
// Prepare Response
|
132 |
+
$this->prepareResponse();
|
133 |
+
|
134 |
+
// Not finished
|
135 |
+
return true;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Checks Whether There is Any Job to Execute or Not
|
140 |
+
* @return bool
|
141 |
+
*/
|
142 |
+
protected function isFinished() {
|
143 |
+
return (
|
144 |
+
!isset( $this->options->isRunning ) ||
|
145 |
+
$this->options->currentStep > $this->options->totalSteps ||
|
146 |
+
!method_exists( $this, "step" . $this->options->currentStep )
|
147 |
+
);
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Check if current operation is done on the root folder or on the live DB
|
152 |
+
* @return boolean
|
153 |
+
*/
|
154 |
+
protected function isRoot() {
|
155 |
+
|
156 |
+
// Prefix is the same as the one of live site
|
157 |
+
// $wpdb = WPStaging::getInstance()->get( "wpdb" );
|
158 |
+
// if( $wpdb->prefix === $this->prefix ) {
|
159 |
+
// return true;
|
160 |
+
// }
|
161 |
+
// CloneName is empty
|
162 |
+
$name = ( array ) $this->options->cloneDirectoryName;
|
163 |
+
if( empty( $name ) ) {
|
164 |
+
return true;
|
165 |
+
}
|
166 |
+
|
167 |
+
// Live domain === Staging domain
|
168 |
+
if( $this->multisiteHomeDomain . $this->options->cloneDirectoryName === $this->multisiteHomeDomain ) {
|
169 |
+
return true;
|
170 |
+
}
|
171 |
+
|
172 |
+
return false;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Check if table exists
|
177 |
+
* @param string $table
|
178 |
+
* @return boolean
|
179 |
+
*/
|
180 |
+
protected function isTable( $table ) {
|
181 |
+
if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
|
182 |
+
$this->log( "Table {$table} does not exist", Logger::TYPE_ERROR );
|
183 |
+
return false;
|
184 |
+
}
|
185 |
+
return true;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Copy wp-config.php from the staging site if it is located outside of root one level up or
|
190 |
+
* copy default wp-config.php if production site uses bedrock or any other boilerplate solution that stores wp default config data elsewhere.
|
191 |
+
* @return boolean
|
192 |
+
*/
|
193 |
+
protected function step0() {
|
194 |
+
$this->log( "Preparing Data Step0: Copy wp-config.php file", Logger::TYPE_INFO );
|
195 |
+
|
196 |
+
$dir = trailingslashit( dirname( ABSPATH ) );
|
197 |
+
|
198 |
+
$source = $dir . 'wp-config.php';
|
199 |
+
|
200 |
+
$destination = $this->options->destinationDir . 'wp-config.php';
|
201 |
+
|
202 |
+
// Check if there is already a valid wp-config.php in root of staging site
|
203 |
+
if( $this->isValidWpConfig( $destination ) ) {
|
204 |
+
$this->log( "Preparing Data Step0: Found wp-config.php file in folder {$destination}", Logger::TYPE_INFO );
|
205 |
+
return true;
|
206 |
+
}
|
207 |
+
|
208 |
+
// Check if there is a valid wp-config.php outside root of wp production site
|
209 |
+
if( $this->isValidWpConfig( $source ) ) {
|
210 |
+
// Copy it to staging site
|
211 |
+
if( $this->copy( $source, $destination ) ) {
|
212 |
+
$this->log( "Preparing Data Step0: Copy wp-config.php file from source {$source} to {$destination}", Logger::TYPE_INFO );
|
213 |
+
return true;
|
214 |
+
}
|
215 |
+
}
|
216 |
+
|
217 |
+
// No valid wp-config.php found so let's copy wp stagings default wp-config.php to staging site
|
218 |
+
$source = WPSTG_PLUGIN_DIR . "apps/Backend/helpers/wp-config.php";
|
219 |
+
|
220 |
+
$this->log( "Preparing Data Step0: Copy default wp-config.php file from source {$source} to {$destination}", Logger::TYPE_INFO );
|
221 |
+
|
222 |
+
if( $this->copy( $source, $destination ) ) {
|
223 |
+
// add missing db credentials to wp-config.php
|
224 |
+
if( !$this->alterWpConfig( $destination ) ) {
|
225 |
+
$this->log( "Preparing Data Step0: Can not alter db credentials in wp-config.php", Logger::TYPE_INFO );
|
226 |
+
return false;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
$this->log( "Preparing Data Step0: Successful", Logger::TYPE_INFO );
|
231 |
+
return true;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Copy files with symlink support
|
236 |
+
* @param type $source
|
237 |
+
* @param type $destination
|
238 |
+
* @return boolean
|
239 |
+
*/
|
240 |
+
protected function copy( $source, $destination ) {
|
241 |
+
// Copy symbolic link
|
242 |
+
if( is_link( $source ) ) {
|
243 |
+
$this->log( "Preparing Data: Symbolic link found...", Logger::TYPE_INFO );
|
244 |
+
if( !@copy( readlink( $source ), $destination ) ) {
|
245 |
+
$errors = error_get_last();
|
246 |
+
$this->log( "Preparing Data: Failed to copy {$source} Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
|
247 |
+
return false;
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
// Copy file
|
252 |
+
if( !@copy( $source, $destination ) ) {
|
253 |
+
$errors = error_get_last();
|
254 |
+
$this->log( "Preparing Data Step0: Failed to copy {$source}! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
|
255 |
+
return false;
|
256 |
+
}
|
257 |
+
|
258 |
+
return true;
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Make sure wp-config.php contains correct db credentials
|
263 |
+
* @param type $source
|
264 |
+
* @return boolean
|
265 |
+
*/
|
266 |
+
protected function alterWpConfig( $source ) {
|
267 |
+
$this->log( "Preparing Data: Alter wp-config.php", Logger::TYPE_INFO );
|
268 |
+
|
269 |
+
$content = file_get_contents( $source );
|
270 |
+
|
271 |
+
if( false === ($content = file_get_contents( $source )) ) {
|
272 |
+
return false;
|
273 |
+
}
|
274 |
+
|
275 |
+
$search = "// ** MySQL settings ** //";
|
276 |
+
|
277 |
+
$replace = "// ** MySQL settings ** //\r\n
|
278 |
+
define( 'DB_NAME', '" . DB_NAME . "' );\r\n
|
279 |
+
/** MySQL database username */\r\n
|
280 |
+
define( 'DB_USER', '" . DB_USER . "' );\r\n
|
281 |
+
/** MySQL database password */\r\n
|
282 |
+
define( 'DB_PASSWORD', '" . DB_PASSWORD . "' );\r\n
|
283 |
+
/** MySQL hostname */\r\n
|
284 |
+
define( 'DB_HOST', '" . DB_HOST . "' );\r\n
|
285 |
+
/** Database Charset to use in creating database tables. */\r\n
|
286 |
+
define( 'DB_CHARSET', '" . DB_CHARSET . "' );\r\n
|
287 |
+
/** The Database Collate type. Don't change this if in doubt. */\r\n
|
288 |
+
define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
289 |
+
|
290 |
+
$content = str_replace( $search, $replace, $content );
|
291 |
+
|
292 |
+
if( false === @wpstg_put_contents( $source, $content ) ) {
|
293 |
+
$this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
|
294 |
+
return false;
|
295 |
+
}
|
296 |
+
|
297 |
+
return true;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Check if wp-config.php contains important constants
|
302 |
+
* @param type $source
|
303 |
+
* @return boolean
|
304 |
+
*/
|
305 |
+
protected function isValidWpConfig( $source ) {
|
306 |
+
|
307 |
+
if( !is_file( $source ) && !is_link( $source ) ) {
|
308 |
+
$this->log( "Preparing Data Step0: Can not find {$source}", Logger::TYPE_INFO );
|
309 |
+
return false;
|
310 |
+
}
|
311 |
+
|
312 |
+
$content = file_get_contents( $source );
|
313 |
+
|
314 |
+
if( false === ($content = file_get_contents( $source )) ) {
|
315 |
+
$this->log( "Preparing Data Step0: Can not read {$source}", Logger::TYPE_INFO );
|
316 |
+
return false;
|
317 |
+
}
|
318 |
+
|
319 |
+
// Get DB_NAME from wp-config.php
|
320 |
+
preg_match( "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
321 |
+
|
322 |
+
if( empty( $matches[1] ) ) {
|
323 |
+
$this->log( "Preparing Data Step0: Can not find DB_NAME in wp-config.php", Logger::TYPE_INFO );
|
324 |
+
return false;
|
325 |
+
}
|
326 |
+
|
327 |
+
// Get DB_USER from wp-config.php
|
328 |
+
preg_match( "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
329 |
+
|
330 |
+
if( empty( $matches[1] ) ) {
|
331 |
+
$this->log( "Preparing Data Step0: Can not find DB_USER in wp-config.php", Logger::TYPE_INFO );
|
332 |
+
return false;
|
333 |
+
}
|
334 |
+
|
335 |
+
// Get DB_PASSWORD from wp-config.php
|
336 |
+
preg_match( "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
337 |
+
|
338 |
+
if( empty( $matches[1] ) ) {
|
339 |
+
$this->log( "Preparing Data Step0: Can not find DB_PASSWORD in wp-config.php", Logger::TYPE_INFO );
|
340 |
+
return false;
|
341 |
+
}
|
342 |
+
|
343 |
+
// Get DB_HOST from wp-config.php
|
344 |
+
preg_match( "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
345 |
+
|
346 |
+
if( empty( $matches[1] ) ) {
|
347 |
+
$this->log( "Preparing Data Step0: Can not find DB_HOST in wp-config.php", Logger::TYPE_INFO );
|
348 |
+
return false;
|
349 |
+
}
|
350 |
+
return true;
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* Replace "siteurl" and "home"
|
355 |
+
* @return bool
|
356 |
+
*/
|
357 |
+
protected function step1() {
|
358 |
+
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
|
359 |
+
|
360 |
+
// Skip - Table does not exist
|
361 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
362 |
+
return true;
|
363 |
+
}
|
364 |
+
// Skip - Table is not selected or updated
|
365 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
366 |
+
$this->log( "Preparing Data Step1: Skipping" );
|
367 |
+
return true;
|
368 |
+
}
|
369 |
+
|
370 |
+
$this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . $this->getStagingSiteUrl() );
|
371 |
+
// Replace URLs
|
372 |
+
$result = $this->db->query(
|
373 |
+
$this->db->prepare(
|
374 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->getStagingSiteUrl()
|
375 |
+
)
|
376 |
+
);
|
377 |
+
|
378 |
+
|
379 |
+
|
380 |
+
// All good
|
381 |
+
if( $result ) {
|
382 |
+
return true;
|
383 |
+
}
|
384 |
+
|
385 |
+
$this->log( "Preparing Data Step1: Skip updating siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_WARNING );
|
386 |
+
return true;
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* Update "wpstg_is_staging_site"
|
391 |
+
* @return bool
|
392 |
+
*/
|
393 |
+
protected function step2() {
|
394 |
+
|
395 |
+
$this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
|
396 |
+
|
397 |
+
// Skip - Table does not exist
|
398 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
399 |
+
$this->log( "Preparing Data Step2: Skipping" );
|
400 |
+
return true;
|
401 |
+
}
|
402 |
+
// Skip - Table is not selected or updated
|
403 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
404 |
+
$this->log( "Preparing Data Step2: Skipping" );
|
405 |
+
return true;
|
406 |
+
}
|
407 |
+
|
408 |
+
$result = $this->db->query(
|
409 |
+
$this->db->prepare(
|
410 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
|
411 |
+
)
|
412 |
+
);
|
413 |
+
|
414 |
+
// No errors but no option name such as wpstg_is_staging_site
|
415 |
+
if( '' === $this->db->last_error && 0 == $result ) {
|
416 |
+
$result = $this->db->query(
|
417 |
+
$this->db->prepare(
|
418 |
+
"INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
|
419 |
+
)
|
420 |
+
);
|
421 |
+
}
|
422 |
+
|
423 |
+
// All good
|
424 |
+
if( $result ) {
|
425 |
+
return true;
|
426 |
+
}
|
427 |
+
|
428 |
+
$this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
429 |
+
return false;
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Update rewrite_rules
|
434 |
+
* @return bool
|
435 |
+
*/
|
436 |
+
protected function step3() {
|
437 |
+
|
438 |
+
$this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
|
439 |
+
|
440 |
+
// Keep Permalinks
|
441 |
+
if( isset( $this->settings->keepPermalinks ) && $this->settings->keepPermalinks === "1" ) {
|
442 |
+
$this->log( "Preparing Data Step3: Skipping" );
|
443 |
+
return true;
|
444 |
+
}
|
445 |
+
|
446 |
+
// Skip - Table does not exist
|
447 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
448 |
+
return true;
|
449 |
+
}
|
450 |
+
|
451 |
+
// Skip - Table is not selected or updated
|
452 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
453 |
+
$this->log( "Preparing Data Step3: Skipping" );
|
454 |
+
return true;
|
455 |
+
}
|
456 |
+
|
457 |
+
$result = $this->db->query(
|
458 |
+
$this->db->prepare(
|
459 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
|
460 |
+
)
|
461 |
+
);
|
462 |
+
|
463 |
+
// All good
|
464 |
+
if( $result ) {
|
465 |
+
return true;
|
466 |
+
}
|
467 |
+
|
468 |
+
//$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
469 |
+
return true;
|
470 |
+
}
|
471 |
+
|
472 |
+
/**
|
473 |
+
* Update Table Prefix in wp_usermeta
|
474 |
+
* @return bool
|
475 |
+
*/
|
476 |
+
protected function step4() {
|
477 |
+
$this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. " );
|
478 |
+
|
479 |
+
// Skip - Table does not exist
|
480 |
+
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
|
481 |
+
return true;
|
482 |
+
}
|
483 |
+
|
484 |
+
// Skip - Table is not selected or updated
|
485 |
+
if( !in_array( $this->prefix . 'usermeta', $this->tables ) ) {
|
486 |
+
$this->log( "Preparing Data Step4: Skipping" );
|
487 |
+
return true;
|
488 |
+
}
|
489 |
+
|
490 |
+
// Change the base table prefix of the main network site
|
491 |
+
$this->debugLog( "Preparing Data Step4: UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, {$this->productionDb->base_prefix}, {$this->prefix}) WHERE meta_key LIKE {$this->productionDb->base_prefix}_%" );
|
492 |
+
$update = $this->db->query(
|
493 |
+
$this->db->prepare(
|
494 |
+
"UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->productionDb->base_prefix, $this->prefix, $this->productionDb->base_prefix . "_%"
|
495 |
+
)
|
496 |
+
);
|
497 |
+
|
498 |
+
if( false === $update ) {
|
499 |
+
$this->log( "Preparing Data Step4: Skip updating {$this->prefix}usermeta meta_key database base_prefix; {$this->db->last_error}", Logger::TYPE_INFO );
|
500 |
+
}
|
501 |
+
|
502 |
+
// Change the table prefix of the child network site
|
503 |
+
$this->debugLog( "Preparing Data Step4: UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, {$this->db->prefix}, {$this->prefix}) WHERE meta_key LIKE {$this->db->prefix}_%" );
|
504 |
+
$update = $this->db->query(
|
505 |
+
$this->db->prepare(
|
506 |
+
"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 . "_%"
|
507 |
+
)
|
508 |
+
);
|
509 |
+
|
510 |
+
if( false === $update ) {
|
511 |
+
$this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
|
512 |
+
$this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
|
513 |
+
return false;
|
514 |
+
}
|
515 |
+
return true;
|
516 |
+
}
|
517 |
+
|
518 |
+
/**
|
519 |
+
* Update $table_prefix in wp-config.php
|
520 |
+
* @return bool
|
521 |
+
*/
|
522 |
+
protected function step5() {
|
523 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
524 |
+
|
525 |
+
$this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
|
526 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
527 |
+
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
528 |
+
return false;
|
529 |
+
}
|
530 |
+
|
531 |
+
// Replace table prefix
|
532 |
+
$pattern = '/\$table_prefix\s*=\s*(.*).*/';
|
533 |
+
$replacement = '$table_prefix = \'' . $this->prefix . '\'; // Changed by WP Staging';
|
534 |
+
$content = preg_replace( $pattern, $replacement, $content );
|
535 |
+
|
536 |
+
if( null === $content ) {
|
537 |
+
$this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
|
538 |
+
return false;
|
539 |
+
}
|
540 |
+
|
541 |
+
// Replace URLs
|
542 |
+
$content = str_replace( $this->multisiteHomeDomain, $this->getStagingSiteUrl(), $content );
|
543 |
+
|
544 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
545 |
+
$this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
|
546 |
+
return false;
|
547 |
+
}
|
548 |
+
|
549 |
+
return true;
|
550 |
+
}
|
551 |
+
|
552 |
+
/**
|
553 |
+
* Reset index.php to original file
|
554 |
+
* This is needed if live site is located in subfolder
|
555 |
+
* Check first if main wordpress is used in subfolder and index.php in parent directory
|
556 |
+
* @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
|
557 |
+
* @return bool
|
558 |
+
*/
|
559 |
+
protected function step6() {
|
560 |
+
|
561 |
+
if( !$this->isSubDir() ) {
|
562 |
+
$this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
|
563 |
+
return true;
|
564 |
+
}
|
565 |
+
|
566 |
+
$path = $this->options->destinationDir . "index.php";
|
567 |
+
|
568 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
569 |
+
$this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
|
570 |
+
return false;
|
571 |
+
}
|
572 |
+
|
573 |
+
|
574 |
+
if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
|
575 |
+
$this->log(
|
576 |
+
"Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
|
577 |
+
);
|
578 |
+
return false;
|
579 |
+
}
|
580 |
+
$this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
|
581 |
+
|
582 |
+
$pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);.*/";
|
583 |
+
|
584 |
+
$replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
|
585 |
+
//$replace.= " // Changed by WP-Staging";
|
586 |
+
|
587 |
+
|
588 |
+
|
589 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
590 |
+
$this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
|
591 |
+
return false;
|
592 |
+
}
|
593 |
+
|
594 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
595 |
+
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
596 |
+
return false;
|
597 |
+
}
|
598 |
+
$this->Log( "Preparing Data Step6: Finished successfully" );
|
599 |
+
return true;
|
600 |
+
}
|
601 |
+
|
602 |
+
/**
|
603 |
+
* Update wpstg_rmpermalinks_executed
|
604 |
+
* @return bool
|
605 |
+
*/
|
606 |
+
protected function step7() {
|
607 |
+
|
608 |
+
$this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
|
609 |
+
|
610 |
+
// Skip - Table does not exist
|
611 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
612 |
+
return true;
|
613 |
+
}
|
614 |
+
|
615 |
+
// Skip - Table is not selected or updated
|
616 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
617 |
+
$this->log( "Preparing Data Step7: Skipping" );
|
618 |
+
return true;
|
619 |
+
}
|
620 |
+
|
621 |
+
$result = $this->db->query(
|
622 |
+
$this->db->prepare(
|
623 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
|
624 |
+
)
|
625 |
+
);
|
626 |
+
|
627 |
+
$this->Log( "Preparing Data Step7: Finished successfully" );
|
628 |
+
return true;
|
629 |
+
}
|
630 |
+
|
631 |
+
/**
|
632 |
+
* Update permalink_structure
|
633 |
+
* @return bool
|
634 |
+
*/
|
635 |
+
protected function step8() {
|
636 |
+
|
637 |
+
$this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
|
638 |
+
|
639 |
+
// Keep Permalinks
|
640 |
+
if( isset( $this->settings->keepPermalinks ) && $this->settings->keepPermalinks === "1" ) {
|
641 |
+
$this->log( "Preparing Data Step8: Skipping" );
|
642 |
+
return true;
|
643 |
+
}
|
644 |
+
|
645 |
+
// Skip - Table does not exist
|
646 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
647 |
+
return true;
|
648 |
+
}
|
649 |
+
|
650 |
+
// Skip - Table is not selected or updated
|
651 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
652 |
+
$this->log( "Preparing Data Step8: Skipping" );
|
653 |
+
return true;
|
654 |
+
}
|
655 |
+
|
656 |
+
$result = $this->db->query(
|
657 |
+
$this->db->prepare(
|
658 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
|
659 |
+
)
|
660 |
+
);
|
661 |
+
|
662 |
+
// All good
|
663 |
+
if( $result ) {
|
664 |
+
$this->Log( "Preparing Data Step8: Finished successfully" );
|
665 |
+
return true;
|
666 |
+
}
|
667 |
+
|
668 |
+
$this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
669 |
+
return true;
|
670 |
+
}
|
671 |
+
|
672 |
+
/**
|
673 |
+
* Update blog_public option to not allow staging site to be indexed by search engines
|
674 |
+
* @return bool
|
675 |
+
*/
|
676 |
+
protected function step9() {
|
677 |
+
|
678 |
+
$this->log( "Preparing Data Step9: Set staging site to noindex" );
|
679 |
+
|
680 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
681 |
+
return true;
|
682 |
+
}
|
683 |
+
|
684 |
+
// Skip - Table is not selected or updated
|
685 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
686 |
+
$this->log( "Preparing Data Step9: Skipping" );
|
687 |
+
return true;
|
688 |
+
}
|
689 |
+
|
690 |
+
$result = $this->db->query(
|
691 |
+
$this->db->prepare(
|
692 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
|
693 |
+
)
|
694 |
+
);
|
695 |
+
|
696 |
+
// All good
|
697 |
+
if( $result ) {
|
698 |
+
$this->Log( "Preparing Data Step9: Finished successfully" );
|
699 |
+
return true;
|
700 |
+
}
|
701 |
+
|
702 |
+
$this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
|
703 |
+
return true;
|
704 |
+
}
|
705 |
+
|
706 |
+
/**
|
707 |
+
* Update WP_HOME in wp-config.php
|
708 |
+
* @return bool
|
709 |
+
*/
|
710 |
+
protected function step10() {
|
711 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
712 |
+
|
713 |
+
$this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
|
714 |
+
|
715 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
716 |
+
$this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
717 |
+
return false;
|
718 |
+
}
|
719 |
+
|
720 |
+
|
721 |
+
// Get WP_HOME from wp-config.php
|
722 |
+
preg_match( "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
723 |
+
|
724 |
+
if( !empty( $matches[1] ) ) {
|
725 |
+
$matches[1];
|
726 |
+
|
727 |
+
$pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);.*/";
|
728 |
+
|
729 |
+
$replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
730 |
+
//$replace .= " // Changed by WP-Staging";
|
731 |
+
|
732 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
733 |
+
$this->log( "Preparing Data: Failed to update WP_HOME", Logger::TYPE_ERROR );
|
734 |
+
return false;
|
735 |
+
}
|
736 |
+
} else {
|
737 |
+
$this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
|
738 |
+
}
|
739 |
+
|
740 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
741 |
+
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
742 |
+
return false;
|
743 |
+
}
|
744 |
+
$this->Log( "Preparing Data Step 10: Finished successfully" );
|
745 |
+
return true;
|
746 |
+
}
|
747 |
+
|
748 |
+
/**
|
749 |
+
* Update WP_SITEURL in wp-config.php
|
750 |
+
* @return bool
|
751 |
+
*/
|
752 |
+
protected function step11() {
|
753 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
754 |
+
|
755 |
+
$this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
|
756 |
+
|
757 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
758 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
759 |
+
return false;
|
760 |
+
}
|
761 |
+
|
762 |
+
|
763 |
+
// Get WP_SITEURL from wp-config.php
|
764 |
+
preg_match( "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
765 |
+
|
766 |
+
if( !empty( $matches[1] ) ) {
|
767 |
+
$matches[1];
|
768 |
+
|
769 |
+
$pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);.*/";
|
770 |
+
|
771 |
+
$replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
|
772 |
+
//$replace .= " // Changed by WP-Staging";
|
773 |
+
|
774 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
775 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
|
776 |
+
return false;
|
777 |
+
}
|
778 |
+
} else {
|
779 |
+
$this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
|
780 |
+
}
|
781 |
+
|
782 |
+
|
783 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
784 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
785 |
+
return false;
|
786 |
+
}
|
787 |
+
$this->Log( "Preparing Data Step 11: Finished successfully" );
|
788 |
+
return true;
|
789 |
+
}
|
790 |
+
|
791 |
+
/**
|
792 |
+
* Update WP_ALLOW_MULTISITE constant in wp-config.php
|
793 |
+
* @return bool
|
794 |
+
*/
|
795 |
+
protected function step12() {
|
796 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
797 |
+
|
798 |
+
$this->log( "Preparing Data Step12: Updating WP_ALLOW_MULTISITE in wp-config.php to false" );
|
799 |
+
|
800 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
801 |
+
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
802 |
+
return false;
|
803 |
+
}
|
804 |
+
|
805 |
+
|
806 |
+
// Get WP_SITEURL from wp-config.php
|
807 |
+
preg_match( "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
808 |
+
|
809 |
+
if( !empty( $matches[1] ) ) {
|
810 |
+
$matches[1];
|
811 |
+
|
812 |
+
$pattern = "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
|
813 |
+
|
814 |
+
$replace = "define('WP_ALLOW_MULTISITE',false); // " . $matches[1];
|
815 |
+
//$replace .= " // Changed by WP-Staging";
|
816 |
+
|
817 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
818 |
+
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE", Logger::TYPE_ERROR );
|
819 |
+
return false;
|
820 |
+
}
|
821 |
+
} else {
|
822 |
+
$this->log( "Preparing Data Step12: WP_ALLOW_MULTISITE not defined in wp-config.php. Skipping this step." );
|
823 |
+
}
|
824 |
+
|
825 |
+
|
826 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
827 |
+
$this->log( "Preparing Data Step12: Failed to update WP_ALLOW_MULTISITE. Can't save contents", Logger::TYPE_ERROR );
|
828 |
+
return false;
|
829 |
+
}
|
830 |
+
$this->Log( "Preparing Data: Finished Step 12 successfully" );
|
831 |
+
return true;
|
832 |
+
}
|
833 |
+
|
834 |
+
/**
|
835 |
+
* Update MULTISITE constant in wp-config.php
|
836 |
+
* @return bool
|
837 |
+
*/
|
838 |
+
protected function step13() {
|
839 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
840 |
+
|
841 |
+
$this->log( "Preparing Data Step13: Updating MULTISITE in wp-config.php to false" );
|
842 |
+
|
843 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
844 |
+
$this->log( "Preparing Data Step13: Failed to update MULTISITE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
845 |
+
return false;
|
846 |
+
}
|
847 |
+
|
848 |
+
|
849 |
+
// Get WP_SITEURL from wp-config.php
|
850 |
+
preg_match( "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
851 |
+
|
852 |
+
if( !empty( $matches[1] ) ) {
|
853 |
+
$matches[1];
|
854 |
+
|
855 |
+
$pattern = "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
|
856 |
+
|
857 |
+
$replace = "define('MULTISITE',false); // " . $matches[1];
|
858 |
+
//$replace .= " // Changed by WP-Staging";
|
859 |
+
|
860 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
861 |
+
$this->log( "Preparing Data Step13: Failed to update MULTISITE", Logger::TYPE_ERROR );
|
862 |
+
return false;
|
863 |
+
}
|
864 |
+
} else {
|
865 |
+
$this->log( "Preparing Data Step13: MULTISITE not defined in wp-config.php. Skipping this step." );
|
866 |
+
}
|
867 |
+
|
868 |
+
|
869 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
870 |
+
$this->log( "Preparing Data Step13: Failed to update MULTISITE. Can't save contents", Logger::TYPE_ERROR );
|
871 |
+
return false;
|
872 |
+
}
|
873 |
+
$this->Log( "Preparing Data Step13: Finished successfully" );
|
874 |
+
return true;
|
875 |
+
}
|
876 |
+
|
877 |
+
/**
|
878 |
+
* Get active_sitewide_plugins from wp_sitemeta and active_plugins from subsite
|
879 |
+
* Merge both arrays and copy them to the staging site into active_plugins
|
880 |
+
*/
|
881 |
+
protected function step14() {
|
882 |
+
|
883 |
+
|
884 |
+
$this->log( "Data Crunching Step14: Updating active_plugins" );
|
885 |
+
|
886 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
887 |
+
$this->log( 'Data Crunching Step14: Fatal Error ' . $this->prefix . 'options does not exist' );
|
888 |
+
$this->returnException( 'Data Crunching Step14: Fatal Error ' . $this->prefix . 'options does not exist' );
|
889 |
+
return false;
|
890 |
+
}
|
891 |
+
|
892 |
+
// Skip - Table is not selected or updated
|
893 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
894 |
+
$this->log( "Preparing Data Step14: Skipping" );
|
895 |
+
return true;
|
896 |
+
}
|
897 |
+
|
898 |
+
// Get active_plugins value from sub site options table
|
899 |
+
$active_plugins = $this->productionDb->get_var( "SELECT option_value FROM {$this->productionDb->prefix}options WHERE option_name = 'active_plugins' " );
|
900 |
+
|
901 |
+
if( !$active_plugins ) {
|
902 |
+
$this->log( "Data Crunching Step14: Option active_plugins are empty " );
|
903 |
+
$active_plugins = array();
|
904 |
+
}
|
905 |
+
// Get active_sitewide_plugins value from main multisite wp_sitemeta table
|
906 |
+
$active_sitewide_plugins = $this->productionDb->get_var( "SELECT meta_value FROM {$this->productionDb->base_prefix}sitemeta WHERE meta_key = 'active_sitewide_plugins' " );
|
907 |
+
|
908 |
+
if( !$active_sitewide_plugins ) {
|
909 |
+
$this->log( "Data Crunching Step14: Options {$this->productionDb->base_prefix}active_sitewide_plugins is empty " );
|
910 |
+
$active_sitewide_plugins = array();
|
911 |
+
}
|
912 |
+
|
913 |
+
$active_sitewide_plugins = unserialize( $active_sitewide_plugins );
|
914 |
+
$active_plugins = unserialize( $active_plugins );
|
915 |
+
|
916 |
+
$all_plugins = array_merge( $active_plugins, array_keys( $active_sitewide_plugins ) );
|
917 |
+
|
918 |
+
sort( $all_plugins );
|
919 |
+
|
920 |
+
|
921 |
+
// Update active_plugins
|
922 |
+
$update = $this->db->query(
|
923 |
+
"UPDATE {$this->prefix}options SET option_value = '" . serialize( $all_plugins ) . "' WHERE option_name = 'active_plugins'"
|
924 |
+
);
|
925 |
+
|
926 |
+
if( false === $update ) {
|
927 |
+
$this->log( "Data Crunching Step14: Can not update option active_plugins in {$this->prefix}options", Logger::TYPE_WARNING );
|
928 |
+
return false;
|
929 |
+
}
|
930 |
+
|
931 |
+
$this->log( "Data Crunching Step14: Successful!" );
|
932 |
+
return true;
|
933 |
+
}
|
934 |
+
|
935 |
+
/**
|
936 |
+
* Update Table Prefix in wp_options
|
937 |
+
* @return bool
|
938 |
+
*/
|
939 |
+
protected function step15() {
|
940 |
+
$this->log( "Preparing Data Step15: Updating db prefix in {$this->prefix}options." );
|
941 |
+
|
942 |
+
// Skip - Table does not exist
|
943 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
944 |
+
return true;
|
945 |
+
}
|
946 |
+
|
947 |
+
// Skip - Table is not selected or updated
|
948 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
949 |
+
$this->log( "Preparing Data Step4: Skipping" );
|
950 |
+
return true;
|
951 |
+
}
|
952 |
+
|
953 |
+
|
954 |
+
$this->log( "Updating db option_names in {$this->prefix}options. " );
|
955 |
+
|
956 |
+
// Filter the rows below. Do not update them!
|
957 |
+
$filters = array(
|
958 |
+
'wp_mail_smtp',
|
959 |
+
'wp_mail_smtp_version',
|
960 |
+
'wp_mail_smtp_debug',
|
961 |
+
);
|
962 |
+
|
963 |
+
$filters = apply_filters( 'wpstg_data_excl_rows', $filters );
|
964 |
+
|
965 |
+
$where = "";
|
966 |
+
foreach ( $filters as $filter ) {
|
967 |
+
$where .= " AND option_name <> '" . $filter . "'";
|
968 |
+
}
|
969 |
+
|
970 |
+
$updateOptions = $this->db->query(
|
971 |
+
$this->db->prepare(
|
972 |
+
"UPDATE IGNORE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s" . $where, $this->productionDb->prefix, $this->prefix, $this->productionDb->prefix . "_%"
|
973 |
+
)
|
974 |
+
);
|
975 |
+
|
976 |
+
if( false === $updateOptions ) {
|
977 |
+
$this->log( "Preparing Data Step15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_WARNING );
|
978 |
+
//$this->returnException( "Data Crunching Step 15: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
979 |
+
return true;
|
980 |
+
}
|
981 |
+
|
982 |
+
|
983 |
+
return true;
|
984 |
+
}
|
985 |
+
|
986 |
+
/**
|
987 |
+
* Change upload_path in wp_options (if it is defined)
|
988 |
+
* @return bool
|
989 |
+
*/
|
990 |
+
protected function step16() {
|
991 |
+
$this->log( "Preparing Data Step16: Updating upload_path {$this->prefix}options." );
|
992 |
+
|
993 |
+
// Skip - Table does not exist
|
994 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
995 |
+
return true;
|
996 |
+
}
|
997 |
+
|
998 |
+
$newUploadPath = $this->getNewUploadPath();
|
999 |
+
|
1000 |
+
if( false === $newUploadPath ) {
|
1001 |
+
$this->log( "Preparing Data Step16: Skipping" );
|
1002 |
+
return true;
|
1003 |
+
}
|
1004 |
+
|
1005 |
+
// Skip - Table is not selected or updated
|
1006 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1007 |
+
$this->log( "Preparing Data Step16: Skipping" );
|
1008 |
+
return true;
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
$error = isset( $this->db->last_error ) ? 'Last error: ' . $this->db->last_error : '';
|
1012 |
+
|
1013 |
+
$this->log( "Updating upload_path in {$this->prefix}options. {$error}" );
|
1014 |
+
|
1015 |
+
$updateOptions = $this->db->query(
|
1016 |
+
$this->db->prepare(
|
1017 |
+
"UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'upload_path'", $newUploadPath
|
1018 |
+
)
|
1019 |
+
);
|
1020 |
+
|
1021 |
+
if( false === $updateOptions ) {
|
1022 |
+
$this->log( "Preparing Data Step16: Failed to update upload_path in {$this->prefix}options. {$error}", Logger::TYPE_ERROR );
|
1023 |
+
return true;
|
1024 |
+
}
|
1025 |
+
$this->Log( "Preparing Data: Finished Step 16 successfully" );
|
1026 |
+
return true;
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
/**
|
1030 |
+
* Update WP_CACHE in wp-config.php
|
1031 |
+
* @return bool
|
1032 |
+
*/
|
1033 |
+
protected function step17() {
|
1034 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
1035 |
+
|
1036 |
+
$this->log( "Preparing Data Step17: Set WP_CACHE in wp-config.php to false" );
|
1037 |
+
|
1038 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
1039 |
+
$this->log( "Preparing Data Step17: Failed to update WP_CACHE in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
1040 |
+
return false;
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
|
1044 |
+
// Get WP_CACHE from wp-config.php
|
1045 |
+
preg_match( "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1046 |
+
|
1047 |
+
if( !empty( $matches[1] ) ) {
|
1048 |
+
$matches[1];
|
1049 |
+
|
1050 |
+
$pattern = "/define\s*\(\s*['\"]WP_CACHE['\"]\s*,\s*(.*)\s*\);.*/";
|
1051 |
+
|
1052 |
+
$replace = "define('WP_CACHE',false); // " . $matches[1];
|
1053 |
+
//$replace.= " // Changed by WP-Staging";
|
1054 |
+
|
1055 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1056 |
+
$this->log( "Preparing Data: Failed to change WP_CACHE", Logger::TYPE_ERROR );
|
1057 |
+
return false;
|
1058 |
+
}
|
1059 |
+
} else {
|
1060 |
+
$this->log( "Preparing Data Step17: WP_CACHE not defined in wp-config.php. Skipping this step." );
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
1064 |
+
$this->log( "Preparing Data Step17: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
1065 |
+
return false;
|
1066 |
+
}
|
1067 |
+
$this->Log( "Preparing Data: Finished Step 17 successfully" );
|
1068 |
+
return true;
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
/**
|
1072 |
+
* Add UPLOADS constant in wp-config.php or change it to correct destination (multisite type /sites/2/)
|
1073 |
+
* @return bool
|
1074 |
+
*/
|
1075 |
+
protected function step18() {
|
1076 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
1077 |
+
$this->log( "Preparing Data Step18: Update UPLOADS constant in wp-config.php" );
|
1078 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
1079 |
+
$this->log( "Preparing Data Step18: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
1080 |
+
return false;
|
1081 |
+
}
|
1082 |
+
// Get UPLOADS from wp-config.php if there is already one
|
1083 |
+
preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1084 |
+
$uploadFolder = $this->getMultisiteUploadFolder();
|
1085 |
+
if( !empty( $matches[0] ) ) {
|
1086 |
+
$pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
|
1087 |
+
|
1088 |
+
$replace = "define('UPLOADS', '" . $uploadFolder . "');";
|
1089 |
+
$this->log( "Preparing Data Step18: Change UPLOADS constant in wp-config.php to {$uploadFolder}." );
|
1090 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1091 |
+
$this->log( "Preparing Data Step 18: Failed to change UPLOADS", Logger::TYPE_ERROR );
|
1092 |
+
return false;
|
1093 |
+
}
|
1094 |
+
} else {
|
1095 |
+
$this->log( "Preparing Data Step18: UPLOADS not defined in wp-config.php. Creating new entry." );
|
1096 |
+
// Find ABSPATH and add UPLOAD constant above
|
1097 |
+
preg_match( "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/", $content, $matches );
|
1098 |
+
if( !empty( $matches[0] ) ) {
|
1099 |
+
$matches[0];
|
1100 |
+
$pattern = "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/";
|
1101 |
+
$replace = "define('UPLOADS', '" . $uploadFolder . "'); \n" .
|
1102 |
+
"if ( ! defined( 'ABSPATH' ) )";
|
1103 |
+
$this->log( "Preparing Data Step18: Change UPLOADS constant in wp-config.php to {$uploadFolder}." );
|
1104 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1105 |
+
$this->log( "Preparing Data Step 18: Failed to change UPLOADS", Logger::TYPE_ERROR );
|
1106 |
+
return false;
|
1107 |
+
}
|
1108 |
+
} else {
|
1109 |
+
$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 );
|
1110 |
+
}
|
1111 |
+
}
|
1112 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
1113 |
+
$this->log( "Preparing Data Step18: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
|
1114 |
+
return false;
|
1115 |
+
}
|
1116 |
+
$this->Log( "Preparing Data Step18: Finished successfully" );
|
1117 |
+
return true;
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
/**
|
1121 |
+
* Update database credentials in wp-config.php
|
1122 |
+
* @return bool
|
1123 |
+
*/
|
1124 |
+
protected function step19() {
|
1125 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
1126 |
+
|
1127 |
+
$this->log( "Preparing Data Step19: Change database credentials in wp-config.php" );
|
1128 |
+
|
1129 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
1130 |
+
$this->log( "Preparing Data Step19: Failed to update database credentials in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
1131 |
+
return false;
|
1132 |
+
}
|
1133 |
+
|
1134 |
+
|
1135 |
+
// Get DB_NAME from wp-config.php
|
1136 |
+
preg_match( "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1137 |
+
|
1138 |
+
if( !empty( $matches[1] ) ) {
|
1139 |
+
$matches[1];
|
1140 |
+
|
1141 |
+
$pattern = "/define\s*\(\s*'DB_NAME'\s*,\s*(.*)\s*\);.*/";
|
1142 |
+
|
1143 |
+
$replace = "define('DB_NAME','{$this->options->databaseDatabase}'); // " . $matches[1];
|
1144 |
+
//$replace.= " // Changed by WP-Staging";
|
1145 |
+
|
1146 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1147 |
+
$this->log( "Preparing Data: Failed to change DB_NAME", Logger::TYPE_ERROR );
|
1148 |
+
return false;
|
1149 |
+
}
|
1150 |
+
} else {
|
1151 |
+
$this->log( "Preparing Data Step19: DB_NAME not defined in wp-config.php. Skipping this step." );
|
1152 |
+
}
|
1153 |
+
// Get DB_USER from wp-config.php
|
1154 |
+
preg_match( "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1155 |
+
|
1156 |
+
if( !empty( $matches[1] ) ) {
|
1157 |
+
$matches[1];
|
1158 |
+
|
1159 |
+
$pattern = "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);.*/";
|
1160 |
+
|
1161 |
+
$replace = "define('DB_USER','{$this->options->databaseUser}'); // " . $matches[1];
|
1162 |
+
//$replace.= " // Changed by WP-Staging";
|
1163 |
+
|
1164 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1165 |
+
$this->log( "Preparing Data: Failed to change DB_USER", Logger::TYPE_ERROR );
|
1166 |
+
return false;
|
1167 |
+
}
|
1168 |
+
} else {
|
1169 |
+
$this->log( "Preparing Data Step19: DB_USER not defined in wp-config.php. Skipping this step." );
|
1170 |
+
}
|
1171 |
+
// Get DB_PASSWORD from wp-config.php
|
1172 |
+
preg_match( "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1173 |
+
|
1174 |
+
if( !empty( $matches[1] ) ) {
|
1175 |
+
$matches[1];
|
1176 |
+
|
1177 |
+
$pattern = "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);.*/";
|
1178 |
+
|
1179 |
+
$replace = "define('DB_PASSWORD','{$this->options->databasePassword}'); // Changed by WP Staging";
|
1180 |
+
//$replace.= " // Changed by WP-Staging";
|
1181 |
+
|
1182 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1183 |
+
$this->log( "Preparing Data: Failed to change DB_PASSWORD", Logger::TYPE_ERROR );
|
1184 |
+
return false;
|
1185 |
+
}
|
1186 |
+
} else {
|
1187 |
+
$this->log( "Preparing Data Step19: DB_PASSWORD not defined in wp-config.php. Skipping this step." );
|
1188 |
+
}
|
1189 |
+
// Get DB_HOST from wp-config.php
|
1190 |
+
preg_match( "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1191 |
+
|
1192 |
+
if( !empty( $matches[1] ) ) {
|
1193 |
+
$matches[1];
|
1194 |
+
|
1195 |
+
$pattern = "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);.*/";
|
1196 |
+
|
1197 |
+
$replace = "define('DB_HOST','{$this->options->databaseServer}'); // " . $matches[1];
|
1198 |
+
//$replace.= " // Changed by WP-Staging";
|
1199 |
+
|
1200 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1201 |
+
$this->log( "Preparing Data: Failed to change DB_HOST", Logger::TYPE_ERROR );
|
1202 |
+
return false;
|
1203 |
+
}
|
1204 |
+
} else {
|
1205 |
+
$this->log( "Preparing Data Step19: DB_HOST not defined in wp-config.php. Skipping this step." );
|
1206 |
+
}
|
1207 |
+
|
1208 |
+
|
1209 |
+
if( false === @wpstg_put_contents( $path, $content ) ) {
|
1210 |
+
$this->log( "Preparing Data Step19: Failed to update database credentials in wp-config.php. Can't save contents", Logger::TYPE_ERROR );
|
1211 |
+
return false;
|
1212 |
+
}
|
1213 |
+
$this->Log( "Preparing Data Step 19: Finished successfully" );
|
1214 |
+
return true;
|
1215 |
+
}
|
1216 |
+
|
1217 |
+
/**
|
1218 |
+
* Save hostname of parent production site in option_name wpstg_connection
|
1219 |
+
* @return boolean
|
1220 |
+
*/
|
1221 |
+
protected function step20() {
|
1222 |
+
|
1223 |
+
$table = $this->prefix . 'options';
|
1224 |
+
|
1225 |
+
$siteurl = get_site_url();
|
1226 |
+
|
1227 |
+
$connection = json_encode( array('prodHostname' => $siteurl) );
|
1228 |
+
|
1229 |
+
$data = array(
|
1230 |
+
'option_name' => 'wpstg_connection',
|
1231 |
+
'option_value' => $connection
|
1232 |
+
);
|
1233 |
+
|
1234 |
+
$format = array('%s', '%s');
|
1235 |
+
|
1236 |
+
$result = $this->db->replace( $table, $data, $format );
|
1237 |
+
|
1238 |
+
if( false === $result ) {
|
1239 |
+
$this->Log( "Preparing Data Step20: Could not save {$siteurl} in {$table}", Logger::TYPE_ERROR );
|
1240 |
+
}
|
1241 |
+
return true;
|
1242 |
+
}
|
1243 |
+
|
1244 |
+
/**
|
1245 |
+
* Add option_name wpstg_execute and set it to true
|
1246 |
+
* This option is used to determine if the staging website has not been loaded initiall for executing certain custom actions from \WPStaging\initActions()
|
1247 |
+
* @return boolean
|
1248 |
+
*/
|
1249 |
+
protected function step21() {
|
1250 |
+
|
1251 |
+
$table = $this->prefix . 'options';
|
1252 |
+
|
1253 |
+
// Skip - Table does not exist
|
1254 |
+
if( false === $this->isTable( $table ) ) {
|
1255 |
+
return true;
|
1256 |
+
}
|
1257 |
+
|
1258 |
+
|
1259 |
+
$result = $this->db->query(
|
1260 |
+
$this->db->prepare(
|
1261 |
+
"INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_execute',%s) ON DUPLICATE KEY UPDATE option_value = %s", "true", "true"
|
1262 |
+
)
|
1263 |
+
);
|
1264 |
+
|
1265 |
+
if( false === $result ) {
|
1266 |
+
$this->Log( "Preparing Data Step21: Could not save wpstg_execute in {$table}", Logger::TYPE_ERROR );
|
1267 |
+
}
|
1268 |
+
return true;
|
1269 |
+
}
|
1270 |
+
|
1271 |
+
/**
|
1272 |
+
* Preserve data and prevents data in wp_options from beeing cloned to staging site
|
1273 |
+
* @return bool
|
1274 |
+
*/
|
1275 |
+
protected function step22() {
|
1276 |
+
$this->log( "Preparing Data Step22: Preserve Data in " . $this->prefix . "options" );
|
1277 |
+
|
1278 |
+
// Skip - Table does not exist
|
1279 |
+
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
1280 |
+
return true;
|
1281 |
+
}
|
1282 |
+
|
1283 |
+
// Skip - Table is not selected or updated
|
1284 |
+
if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1285 |
+
$this->log( "Preparing Data Step22: Skipped" );
|
1286 |
+
return true;
|
1287 |
+
}
|
1288 |
+
|
1289 |
+
$sql = '';
|
1290 |
+
|
1291 |
+
$preserved_option_names = array('wpstg_existing_clones_beta');
|
1292 |
+
|
1293 |
+
$preserved_option_names = apply_filters( 'wpstg_preserved_options_cloning', $preserved_option_names );
|
1294 |
+
$preserved_options_escaped = esc_sql( $preserved_option_names );
|
1295 |
+
|
1296 |
+
$preserved_options_data = array();
|
1297 |
+
|
1298 |
+
// Get preserved data in wp_options tables
|
1299 |
+
$table = $this->db->prefix . 'options';
|
1300 |
+
$preserved_options_data[$this->prefix . 'options'] = $this->db->get_results(
|
1301 |
+
sprintf(
|
1302 |
+
"SELECT * FROM `{$table}` WHERE `option_name` IN ('%s')", implode( "','", $preserved_options_escaped )
|
1303 |
+
), ARRAY_A
|
1304 |
+
);
|
1305 |
+
|
1306 |
+
// Create preserved data queries for options tables
|
1307 |
+
foreach ( $preserved_options_data as $key => $value ) {
|
1308 |
+
if( false === empty( $value ) ) {
|
1309 |
+
foreach ( $value as $option ) {
|
1310 |
+
$sql .= $this->db->prepare(
|
1311 |
+
"DELETE FROM `{$key}` WHERE `option_name` = %s;\n", $option['option_name']
|
1312 |
+
);
|
1313 |
+
|
1314 |
+
$sql .= $this->db->prepare(
|
1315 |
+
"INSERT INTO `{$key}` ( `option_id`, `option_name`, `option_value`, `autoload` ) VALUES ( NULL , %s, %s, %s );\n", $option['option_name'], $option['option_value'], $option['autoload']
|
1316 |
+
);
|
1317 |
+
}
|
1318 |
+
}
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
$this->debugLog( "Preparing Data Step22: Preserve values " . json_encode( $preserved_options_data ), Logger::TYPE_INFO );
|
1322 |
+
|
1323 |
+
$this->executeSql( $sql );
|
1324 |
+
|
1325 |
+
$this->log( "Preparing Data Step22: Successful!" );
|
1326 |
+
return true;
|
1327 |
+
}
|
1328 |
+
|
1329 |
+
/**
|
1330 |
+
* Execute a batch of sql queries
|
1331 |
+
* @param string $sqlbatch
|
1332 |
+
*/
|
1333 |
+
private function executeSql( $sqlbatch ) {
|
1334 |
+
$queries = array_filter( explode( ";\n", $sqlbatch ) );
|
1335 |
+
|
1336 |
+
foreach ( $queries as $query ) {
|
1337 |
+
if( false === $this->db->query( $query ) ) {
|
1338 |
+
$this->log( "Data Crunching Warning: Can not execute query {$query}", Logger::TYPE_WARNING );
|
1339 |
+
}
|
1340 |
+
}
|
1341 |
+
return true;
|
1342 |
+
}
|
1343 |
+
|
1344 |
+
/**
|
1345 |
+
* Get relative path to the uploads media folder of multisite e.g.
|
1346 |
+
* wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
|
1347 |
+
* @return boolean
|
1348 |
+
*/
|
1349 |
+
protected function getMultisiteUploadFolder() {
|
1350 |
+
$strings = new Strings();
|
1351 |
+
// Get absolute path to uploads folder
|
1352 |
+
$uploads = wp_upload_dir();
|
1353 |
+
$basedir = $uploads['basedir'];
|
1354 |
+
// Get relative upload path
|
1355 |
+
$relDir = str_replace( wpstg_replace_windows_directory_separator( ABSPATH ), null, wpstg_replace_windows_directory_separator( $basedir ) );
|
1356 |
+
return $relDir;
|
1357 |
+
}
|
1358 |
+
|
1359 |
+
/**
|
1360 |
+
* Get Upload Path to staging site
|
1361 |
+
* @return boolean|string
|
1362 |
+
*/
|
1363 |
+
protected function getNewUploadPath() {
|
1364 |
+
$uploadPath = get_option( 'upload_path' );
|
1365 |
+
|
1366 |
+
if( !$uploadPath ) {
|
1367 |
+
return false;
|
1368 |
+
}
|
1369 |
+
|
1370 |
+
$customSlug = str_replace( wpstg_replace_windows_directory_separator( \WPStaging\WPStaging::getWPpath() ), '', wpstg_replace_windows_directory_separator( $uploadPath ) );
|
1371 |
+
|
1372 |
+
$newUploadPath = wpstg_replace_windows_directory_separator( \WPStaging\WPStaging::getWPpath() ) . $this->options->cloneDirectoryName . '/' . $customSlug;
|
1373 |
+
|
1374 |
+
return $newUploadPath;
|
1375 |
+
}
|
1376 |
+
|
1377 |
+
/**
|
1378 |
+
* Return URL to staging site
|
1379 |
+
* @return string
|
1380 |
+
*/
|
1381 |
+
protected function getStagingSiteUrl() {
|
1382 |
+
|
1383 |
+
if( !empty( $this->options->cloneHostname ) ) {
|
1384 |
+
return $this->options->cloneHostname;
|
1385 |
+
}
|
1386 |
+
|
1387 |
+
if( $this->isSubDir() ) {
|
1388 |
+
return trailingslashit( $this->multisiteHomeDomain ) . trailingslashit( $this->getSubDir() ) . $this->options->cloneDirectoryName;
|
1389 |
+
}
|
1390 |
+
|
1391 |
+
// Get the path to the main multisite without appending and trailingslash e.g. wordpress
|
1392 |
+
$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : '/';
|
1393 |
+
$url = rtrim( $this->multisiteHomeDomain, '/\\' ) . $multisitePath . $this->options->cloneDirectoryName;
|
1394 |
+
|
1395 |
+
return $url;
|
1396 |
+
}
|
1397 |
+
|
1398 |
+
/**
|
1399 |
+
* Check if WP is installed in subdir
|
1400 |
+
* @return boolean
|
1401 |
+
*/
|
1402 |
+
protected function isSubDir() {
|
1403 |
+
// Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
|
1404 |
+
// This is happening much more often than you would expect
|
1405 |
+
$siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
|
1406 |
+
$home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
|
1407 |
+
|
1408 |
+
if( $home !== $siteurl ) {
|
1409 |
+
return true;
|
1410 |
+
}
|
1411 |
+
return false;
|
1412 |
+
}
|
1413 |
+
|
1414 |
+
/**
|
1415 |
+
* Get the install sub directory if WP is installed in sub directory
|
1416 |
+
* @return string
|
1417 |
+
*/
|
1418 |
+
protected function getSubDir() {
|
1419 |
+
$home = get_option( 'home' );
|
1420 |
+
$siteurl = get_option( 'siteurl' );
|
1421 |
+
|
1422 |
+
if( empty( $home ) || empty( $siteurl ) ) {
|
1423 |
+
return '';
|
1424 |
+
}
|
1425 |
+
|
1426 |
+
$dir = str_replace( $home, '', $siteurl );
|
1427 |
+
return str_replace( '/', '', $dir );
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/Database.php
CHANGED
@@ -1,363 +1,366 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Strings;
|
12 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Class Database
|
16 |
-
* @package WPStaging\Backend\Modules\Jobs
|
17 |
-
*/
|
18 |
-
class Database extends JobExecutable {
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @var int
|
22 |
-
*/
|
23 |
-
private $total = 0;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* @var \WPDB
|
27 |
-
*/
|
28 |
-
private $db;
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Initialize
|
32 |
-
*/
|
33 |
-
public function initialize() {
|
34 |
-
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
35 |
-
$this->getTables();
|
36 |
-
// Add wp_users and wp_usermeta to the tables object because they are not available in MU installation but we need them on the staging site
|
37 |
-
$this->total = count( $this->options->tables );
|
38 |
-
$this->isFatalError();
|
39 |
-
}
|
40 |
-
|
41 |
-
private function getTables() {
|
42 |
-
// Add wp_users and wp_usermeta to the tables if they do not exist
|
43 |
-
// because they are not available in MU installation but we need them on the staging site
|
44 |
-
|
45 |
-
if( !in_array( $this->db->prefix . 'users', $this->options->tables ) ) {
|
46 |
-
array_push( $this->options->tables, $this->db->prefix . 'users' );
|
47 |
-
$this->saveOptions();
|
48 |
-
}
|
49 |
-
if( !in_array( $this->db->prefix . 'usermeta', $this->options->tables ) ) {
|
50 |
-
array_push( $this->options->tables, $this->db->prefix . 'usermeta' );
|
51 |
-
$this->saveOptions();
|
52 |
-
}
|
53 |
-
}
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Return fatal error and stops here if subfolder already exists
|
57 |
-
* and mainJob is not updating the clone
|
58 |
-
* @return boolean
|
59 |
-
*/
|
60 |
-
private function isFatalError() {
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
*
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
*
|
79 |
-
*
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
$this->
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
//
|
109 |
-
//
|
110 |
-
//
|
111 |
-
//
|
112 |
-
//
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
*
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
$
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
*
|
139 |
-
* @
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
$
|
145 |
-
$
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
*
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
//// $
|
181 |
-
//// $
|
182 |
-
|
183 |
-
//
|
184 |
-
// $
|
185 |
-
//
|
186 |
-
//
|
187 |
-
//
|
188 |
-
//
|
189 |
-
//
|
190 |
-
//
|
191 |
-
//
|
192 |
-
//
|
193 |
-
//
|
194 |
-
//
|
195 |
-
//
|
196 |
-
//
|
197 |
-
//
|
198 |
-
//
|
199 |
-
//
|
200 |
-
//
|
201 |
-
//
|
202 |
-
//
|
203 |
-
//
|
204 |
-
//
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
*
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
//// $
|
213 |
-
//// $
|
214 |
-
|
215 |
-
// $
|
216 |
-
//
|
217 |
-
//
|
218 |
-
//
|
219 |
-
//
|
220 |
-
//
|
221 |
-
//
|
222 |
-
//
|
223 |
-
//
|
224 |
-
//
|
225 |
-
//
|
226 |
-
//
|
227 |
-
//
|
228 |
-
//
|
229 |
-
//
|
230 |
-
//
|
231 |
-
//
|
232 |
-
//
|
233 |
-
//
|
234 |
-
//
|
235 |
-
//
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
*
|
240 |
-
* @param string $
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
$this->
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
*
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
$this->options->job->
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
*
|
278 |
-
* @param string $
|
279 |
-
* @
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
$this->options->job->total =
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
*
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
$this->
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
*
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Strings;
|
12 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Class Database
|
16 |
+
* @package WPStaging\Backend\Modules\Jobs
|
17 |
+
*/
|
18 |
+
class Database extends JobExecutable {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
private $total = 0;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var \WPDB
|
27 |
+
*/
|
28 |
+
private $db;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Initialize
|
32 |
+
*/
|
33 |
+
public function initialize() {
|
34 |
+
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
35 |
+
$this->getTables();
|
36 |
+
// Add wp_users and wp_usermeta to the tables object because they are not available in MU installation but we need them on the staging site
|
37 |
+
$this->total = count( $this->options->tables );
|
38 |
+
$this->isFatalError();
|
39 |
+
}
|
40 |
+
|
41 |
+
private function getTables() {
|
42 |
+
// Add wp_users and wp_usermeta to the tables if they do not exist
|
43 |
+
// because they are not available in MU installation but we need them on the staging site
|
44 |
+
|
45 |
+
if( !in_array( $this->db->prefix . 'users', $this->options->tables ) ) {
|
46 |
+
array_push( $this->options->tables, $this->db->prefix . 'users' );
|
47 |
+
$this->saveOptions();
|
48 |
+
}
|
49 |
+
if( !in_array( $this->db->prefix . 'usermeta', $this->options->tables ) ) {
|
50 |
+
array_push( $this->options->tables, $this->db->prefix . 'usermeta' );
|
51 |
+
$this->saveOptions();
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Return fatal error and stops here if subfolder already exists
|
57 |
+
* and mainJob is not updating the clone
|
58 |
+
* @return boolean
|
59 |
+
*/
|
60 |
+
private function isFatalError() {
|
61 |
+
//$path = trailingslashit( get_home_path() ) . $this->options->cloneDirectoryName;
|
62 |
+
$path = trailingslashit($this->options->cloneDir);
|
63 |
+
if( isset( $this->options->mainJob ) && $this->options->mainJob !== 'updating' && is_dir( $path ) && !wpstg_is_empty_dir( $path) ) {
|
64 |
+
$this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists and is not empty: " . $path );
|
65 |
+
}
|
66 |
+
return false;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
71 |
+
* @return void
|
72 |
+
*/
|
73 |
+
protected function calculateTotalSteps() {
|
74 |
+
$this->options->totalSteps = ($this->total === 0) ? 1 : $this->total;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Execute the Current Step
|
79 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
80 |
+
* @return bool
|
81 |
+
*/
|
82 |
+
protected function execute() {
|
83 |
+
|
84 |
+
|
85 |
+
// Over limits threshold
|
86 |
+
if( $this->isOverThreshold() ) {
|
87 |
+
// Prepare response and save current progress
|
88 |
+
$this->prepareResponse( false, false );
|
89 |
+
$this->saveOptions();
|
90 |
+
return false;
|
91 |
+
}
|
92 |
+
|
93 |
+
// No more steps, finished
|
94 |
+
if( !isset( $this->options->isRunning ) || $this->options->currentStep > $this->total ) {
|
95 |
+
$this->prepareResponse( true, false );
|
96 |
+
return false;
|
97 |
+
}
|
98 |
+
|
99 |
+
// Copy table
|
100 |
+
if( isset( $this->options->tables[$this->options->currentStep] ) && !$this->copyTable( $this->options->tables[$this->options->currentStep] ) ) {
|
101 |
+
// Prepare Response
|
102 |
+
$this->prepareResponse( false, false );
|
103 |
+
|
104 |
+
// Not finished
|
105 |
+
return true;
|
106 |
+
}
|
107 |
+
|
108 |
+
// if( isset( $this->options->tables[$this->options->currentStep] ) && $this->db->prefix . 'users' === $this->options->tables[$this->options->currentStep] ) {
|
109 |
+
// $this->copyWpUsers();
|
110 |
+
// }
|
111 |
+
// if( isset( $this->options->tables[$this->options->currentStep] ) && $this->db->prefix . 'usermeta' === $this->options->tables[$this->options->currentStep] ) {
|
112 |
+
// $this->copyWpUsermeta();
|
113 |
+
// }
|
114 |
+
// Prepare Response
|
115 |
+
$this->prepareResponse();
|
116 |
+
|
117 |
+
// Not finished
|
118 |
+
return true;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Get new prefix for the staging site
|
123 |
+
* @return string
|
124 |
+
*/
|
125 |
+
private function getStagingPrefix() {
|
126 |
+
$stagingPrefix = $this->options->prefix;
|
127 |
+
// Make sure prefix of staging site is NEVER identical to prefix of live site!
|
128 |
+
if( $stagingPrefix == $this->db->prefix ) {
|
129 |
+
$error = 'Fatal error 7: The new database table prefix ' . $stagingPrefix . ' would be identical to the table prefix of the live site. Please open a support ticket at support@wp-staging.com';
|
130 |
+
$this->returnException($error);
|
131 |
+
wp_die( $error );
|
132 |
+
|
133 |
+
}
|
134 |
+
return $stagingPrefix;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* No worries, SQL queries don't eat from PHP execution time!
|
139 |
+
* @param string $tableName
|
140 |
+
* @return bool
|
141 |
+
*/
|
142 |
+
private function copyTable( $tableName ) {
|
143 |
+
|
144 |
+
$strings = new Strings();
|
145 |
+
$tableName = is_object( $tableName ) ? $tableName->name : $tableName;
|
146 |
+
$newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->prefix, null, $tableName );
|
147 |
+
|
148 |
+
// Get wp_users from main site
|
149 |
+
if( 'users' === $strings->str_replace_first( $this->db->prefix, null, $tableName ) ) {
|
150 |
+
$tableName = $this->db->base_prefix . 'users';
|
151 |
+
}
|
152 |
+
// Get wp_usermeta from main site
|
153 |
+
if( 'usermeta' === $strings->str_replace_first( $this->db->prefix, null, $tableName ) ) {
|
154 |
+
$tableName = $this->db->base_prefix . 'usermeta';
|
155 |
+
}
|
156 |
+
|
157 |
+
// Drop table if necessary
|
158 |
+
$this->dropTable( $newTableName );
|
159 |
+
|
160 |
+
// Save current job
|
161 |
+
$this->setJob( $newTableName );
|
162 |
+
|
163 |
+
// Beginning of the job
|
164 |
+
if( !$this->startJob( $newTableName, $tableName ) ) {
|
165 |
+
return true;
|
166 |
+
}
|
167 |
+
|
168 |
+
// Copy data
|
169 |
+
$this->copyData( $newTableName, $tableName );
|
170 |
+
|
171 |
+
// Finish the step
|
172 |
+
return $this->finishStep();
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Copy multisite global user table wp_users to wpstgX_users
|
177 |
+
* @return bool
|
178 |
+
*/
|
179 |
+
// private function copyWpUsers() {
|
180 |
+
//// $strings = new Strings();
|
181 |
+
//// $tableName = $this->db->base_prefix . 'users';
|
182 |
+
//// $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
|
183 |
+
//
|
184 |
+
// $tableName = $this->db->base_prefix . 'users';
|
185 |
+
// $newTableName = $this->getStagingPrefix() . 'users';
|
186 |
+
//
|
187 |
+
// $this->log( "DB Copy: Try to create table {$newTableName}" );
|
188 |
+
//
|
189 |
+
// // Drop table if necessary
|
190 |
+
// $this->dropTable( $newTableName );
|
191 |
+
//
|
192 |
+
// // Save current job
|
193 |
+
// $this->setJob( $newTableName );
|
194 |
+
//
|
195 |
+
// // Beginning of the job
|
196 |
+
// if( !$this->startJob( $newTableName, $tableName ) ) {
|
197 |
+
// return true;
|
198 |
+
// }
|
199 |
+
//
|
200 |
+
// // Copy data
|
201 |
+
// $this->copyData( $newTableName, $tableName );
|
202 |
+
//
|
203 |
+
// // Finish the step
|
204 |
+
// return $this->finishStep();
|
205 |
+
// }
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Copy multisite global user table wp_usermeta to wpstgX_users
|
209 |
+
* @return bool
|
210 |
+
*/
|
211 |
+
// private function copyWpUsermeta() {
|
212 |
+
//// $strings = new Strings();
|
213 |
+
//// $tableName = $this->db->base_prefix . 'usermeta';
|
214 |
+
//// $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
|
215 |
+
// $tableName = $this->db->base_prefix . 'usermeta';
|
216 |
+
// $newTableName = $this->getStagingPrefix() . 'usermeta';
|
217 |
+
//
|
218 |
+
// $this->log( "DB Copy: Try to create table {$newTableName}" );
|
219 |
+
//
|
220 |
+
//
|
221 |
+
// // Drop table if necessary
|
222 |
+
// $this->dropTable( $newTableName );
|
223 |
+
//
|
224 |
+
// // Save current job
|
225 |
+
// $this->setJob( $newTableName );
|
226 |
+
//
|
227 |
+
// // Beginning of the job
|
228 |
+
// if( !$this->startJob( $newTableName, $tableName ) ) {
|
229 |
+
// return true;
|
230 |
+
// }
|
231 |
+
// // Copy data
|
232 |
+
// $this->copyData( $newTableName, $tableName );
|
233 |
+
//
|
234 |
+
// // Finish the step
|
235 |
+
// return $this->finishStep();
|
236 |
+
// }
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Copy data from old table to new table
|
240 |
+
* @param string $new
|
241 |
+
* @param string $old
|
242 |
+
*/
|
243 |
+
private function copyData( $new, $old ) {
|
244 |
+
$rows = $this->options->job->start + $this->settings->queryLimit;
|
245 |
+
$this->log(
|
246 |
+
"DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
|
247 |
+
);
|
248 |
+
|
249 |
+
$limitation = '';
|
250 |
+
|
251 |
+
if( 0 < ( int ) $this->settings->queryLimit ) {
|
252 |
+
$limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
|
253 |
+
}
|
254 |
+
|
255 |
+
$this->db->query(
|
256 |
+
"INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
|
257 |
+
);
|
258 |
+
|
259 |
+
// Set new offset
|
260 |
+
$this->options->job->start += $this->settings->queryLimit;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Set the job
|
265 |
+
* @param string $table
|
266 |
+
*/
|
267 |
+
private function setJob( $table ) {
|
268 |
+
if( isset( $this->options->job->current ) ) {
|
269 |
+
return;
|
270 |
+
}
|
271 |
+
|
272 |
+
$this->options->job->current = $table;
|
273 |
+
$this->options->job->start = 0;
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Start Job
|
278 |
+
* @param string $new
|
279 |
+
* @param string $old
|
280 |
+
* @return bool
|
281 |
+
*/
|
282 |
+
private function startJob( $new, $old ) {
|
283 |
+
|
284 |
+
if( 0 != $this->options->job->start ) {
|
285 |
+
return true;
|
286 |
+
}
|
287 |
+
|
288 |
+
// Table does not exist
|
289 |
+
$result = $this->db->query( "SHOW TABLES LIKE '{$old}'" );
|
290 |
+
if( !$result || 0 === $result ) {
|
291 |
+
return true;
|
292 |
+
}
|
293 |
+
|
294 |
+
$this->log( "DB Copy: Creating table {$new}" );
|
295 |
+
|
296 |
+
$this->db->query( "CREATE TABLE {$new} LIKE {$old}" );
|
297 |
+
|
298 |
+
$this->options->job->total = 0;
|
299 |
+
$this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
|
300 |
+
|
301 |
+
if( 0 == $this->options->job->total ) {
|
302 |
+
$this->finishStep();
|
303 |
+
return false;
|
304 |
+
}
|
305 |
+
|
306 |
+
return true;
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Finish the step
|
311 |
+
*/
|
312 |
+
private function finishStep() {
|
313 |
+
// This job is not finished yet
|
314 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
315 |
+
return false;
|
316 |
+
}
|
317 |
+
|
318 |
+
// Add it to cloned tables listing
|
319 |
+
$this->options->clonedTables[] = isset( $this->options->tables[$this->options->currentStep] ) ? $this->options->tables[$this->options->currentStep] : false;
|
320 |
+
|
321 |
+
// Reset job
|
322 |
+
$this->options->job = new \stdClass();
|
323 |
+
|
324 |
+
return true;
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Drop table if necessary
|
329 |
+
* @param string $new
|
330 |
+
*/
|
331 |
+
private function dropTable( $new ) {
|
332 |
+
|
333 |
+
$old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
|
334 |
+
|
335 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
336 |
+
return;
|
337 |
+
}
|
338 |
+
|
339 |
+
$this->log( "DB Copy: {$new} already exists, dropping it first" );
|
340 |
+
$this->db->query( "SET FOREIGN_KEY_CHECKS=0" );
|
341 |
+
$this->db->query( "DROP TABLE {$new}" );
|
342 |
+
$this->db->query( "SET FOREIGN_KEY_CHECKS=1" );
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Check if table needs to be dropped
|
347 |
+
* @param string $new
|
348 |
+
* @param string $old
|
349 |
+
* @return bool
|
350 |
+
*/
|
351 |
+
private function shouldDropTable( $new, $old ) {
|
352 |
+
|
353 |
+
|
354 |
+
|
355 |
+
if( $old === $new &&
|
356 |
+
(
|
357 |
+
!isset( $this->options->job->current ) ||
|
358 |
+
!isset( $this->options->job->start ) ||
|
359 |
+
0 == $this->options->job->start
|
360 |
+
) ) {
|
361 |
+
return true;
|
362 |
+
}
|
363 |
+
return false;
|
364 |
+
}
|
365 |
+
|
366 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/DatabaseExternal.php
CHANGED
@@ -1,411 +1,446 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Strings;
|
12 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Class Database
|
16 |
-
* @package WPStaging\Backend\Modules\Jobs
|
17 |
-
*/
|
18 |
-
class DatabaseExternal extends JobExecutable {
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
//
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
//
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
//
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Strings;
|
12 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Class Database
|
16 |
+
* @package WPStaging\Backend\Modules\Jobs
|
17 |
+
*/
|
18 |
+
class DatabaseExternal extends JobExecutable {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
private $total = 0;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var \WPDB
|
27 |
+
*/
|
28 |
+
private $db;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Staging Database
|
32 |
+
* @var \WPDB
|
33 |
+
*/
|
34 |
+
private $stagingDb;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Helper class for string methods
|
38 |
+
* @var object
|
39 |
+
*/
|
40 |
+
private $strings;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Initialize
|
44 |
+
*/
|
45 |
+
public function initialize() {
|
46 |
+
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
47 |
+
$this->stagingDb = $this->getExternalDBConnection();
|
48 |
+
$this->getTables();
|
49 |
+
// Add 2 more tables to total count because we need to copy two more tables from the main multisite installation wp_users and wp_usermeta
|
50 |
+
$this->total = count( $this->options->tables ) + 2;
|
51 |
+
$this->isFatalError();
|
52 |
+
$this->strings = new Strings();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Get external db object
|
57 |
+
* @return mixed db | false
|
58 |
+
*/
|
59 |
+
private function getExternalDBConnection() {
|
60 |
+
|
61 |
+
$db = new \wpdb( $this->options->databaseUser, str_replace( "\\\\", "\\", $this->options->databasePassword ), $this->options->databaseDatabase, $this->options->databaseServer );
|
62 |
+
|
63 |
+
// Can not connect to mysql
|
64 |
+
if( !empty( $db->error->errors['db_connect_fail']['0'] ) ) {
|
65 |
+
$this->returnException( "Can not connect to external database {$this->options->databaseDatabase}" );
|
66 |
+
return false;
|
67 |
+
}
|
68 |
+
|
69 |
+
// Can not connect to database
|
70 |
+
$db->select( $this->options->databaseDatabase );
|
71 |
+
if( !$db->ready ) {
|
72 |
+
$error = isset( $db->error->errors['db_select_fail'] ) ? $db->error->errors['db_select_fail'] : "Error: Can't select {database} Either it does not exist or you don't have privileges to access it.";
|
73 |
+
$this->returnException( $error );
|
74 |
+
exit;
|
75 |
+
}
|
76 |
+
return $db;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Add wp_users and wp_usermeta to the tables if they do not exist
|
81 |
+
* because they are not available in MU installation but we need them on the staging site
|
82 |
+
*
|
83 |
+
* return void
|
84 |
+
*/
|
85 |
+
private function getTables() {
|
86 |
+
|
87 |
+
if( !in_array( $this->db->prefix . 'users', $this->options->tables ) ) {
|
88 |
+
array_push( $this->options->tables, $this->db->prefix . 'users' );
|
89 |
+
$this->saveOptions();
|
90 |
+
}
|
91 |
+
if( !in_array( $this->db->prefix . 'usermeta', $this->options->tables ) ) {
|
92 |
+
array_push( $this->options->tables, $this->db->prefix . 'usermeta' );
|
93 |
+
$this->saveOptions();
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Return fatal error and stops here if subfolder already exists & is not empty
|
99 |
+
* and mainJob is not updating the clone
|
100 |
+
* @return boolean
|
101 |
+
*/
|
102 |
+
private function isFatalError() {
|
103 |
+
//$path = trailingslashit( get_home_path() ) . $this->options->cloneDirectoryName;
|
104 |
+
$path = trailingslashit($this->options->cloneDir);
|
105 |
+
if( isset( $this->options->mainJob ) && $this->options->mainJob !== 'updating' && (is_dir( $path ) && !wpstg_is_empty_dir( $path ) ) ) {
|
106 |
+
$this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists and is not empty: " . $path );
|
107 |
+
}
|
108 |
+
return false;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
113 |
+
* @return void
|
114 |
+
*/
|
115 |
+
protected function calculateTotalSteps() {
|
116 |
+
$this->options->totalSteps = ($this->total === 0) ? 1 : $this->total;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Execute the Current Step
|
121 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
122 |
+
* @return bool
|
123 |
+
*/
|
124 |
+
protected function execute() {
|
125 |
+
|
126 |
+
|
127 |
+
// Over limits threshold
|
128 |
+
if( $this->isOverThreshold() ) {
|
129 |
+
// Prepare response and save current progress
|
130 |
+
$this->prepareResponse( false, false );
|
131 |
+
$this->saveOptions();
|
132 |
+
return false;
|
133 |
+
}
|
134 |
+
|
135 |
+
// No more steps, finished
|
136 |
+
if( !isset( $this->options->isRunning ) || $this->options->currentStep > $this->total ) {
|
137 |
+
$this->prepareResponse( true, false );
|
138 |
+
return false;
|
139 |
+
}
|
140 |
+
|
141 |
+
// Copy table
|
142 |
+
if( isset( $this->options->tables[$this->options->currentStep] ) && !$this->copyTable( $this->options->tables[$this->options->currentStep] ) ) {
|
143 |
+
// Prepare Response
|
144 |
+
$this->prepareResponse( false, false );
|
145 |
+
|
146 |
+
// Not finished
|
147 |
+
return true;
|
148 |
+
}
|
149 |
+
|
150 |
+
// Prepare Response
|
151 |
+
$this->prepareResponse();
|
152 |
+
|
153 |
+
// Not finished
|
154 |
+
return true;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Get new prefix for the staging site
|
159 |
+
* @return string
|
160 |
+
*/
|
161 |
+
private function getStagingPrefix() {
|
162 |
+
|
163 |
+
$this->options->prefix = !empty( $this->options->databasePrefix ) ? $this->options->databasePrefix : $this->db->prefix;
|
164 |
+
|
165 |
+
return $this->options->prefix;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* No worries, SQL queries don't eat from PHP execution time!
|
170 |
+
* @param string $tableName
|
171 |
+
* @return bool
|
172 |
+
*/
|
173 |
+
private function copyTable( $tableName ) {
|
174 |
+
|
175 |
+
$tableName = is_object( $tableName ) ? $tableName->name : $tableName;
|
176 |
+
$newTableName = $this->getStagingPrefix() . $this->strings->str_replace_first( $this->db->prefix, null, $tableName );
|
177 |
+
|
178 |
+
// Get wp_users from main site
|
179 |
+
if( 'users' === $this->strings->str_replace_first( $this->db->prefix, null, $tableName ) ) {
|
180 |
+
$tableName = $this->db->base_prefix . 'users';
|
181 |
+
}
|
182 |
+
// Get wp_usermeta from main site
|
183 |
+
if( 'usermeta' === $this->strings->str_replace_first( $this->db->prefix, null, $tableName ) ) {
|
184 |
+
$tableName = $this->db->base_prefix . 'usermeta';
|
185 |
+
}
|
186 |
+
|
187 |
+
// Drop table if necessary
|
188 |
+
$this->dropTable( $newTableName );
|
189 |
+
|
190 |
+
// Save current job
|
191 |
+
$this->setJob( $newTableName );
|
192 |
+
|
193 |
+
// Beginning of the job
|
194 |
+
if( !$this->startJob( $newTableName, $tableName ) ) {
|
195 |
+
return true;
|
196 |
+
}
|
197 |
+
|
198 |
+
// Copy data
|
199 |
+
$this->copyData( $newTableName, $tableName );
|
200 |
+
|
201 |
+
// Finish the step
|
202 |
+
return $this->finishStep();
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Set the job
|
207 |
+
* @param string $table
|
208 |
+
*/
|
209 |
+
private function setJob( $table ) {
|
210 |
+
if( isset( $this->options->job->current ) ) {
|
211 |
+
return;
|
212 |
+
}
|
213 |
+
|
214 |
+
$this->options->job->current = $table;
|
215 |
+
$this->options->job->start = 0;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Start Job and create tables
|
220 |
+
* @param string $new
|
221 |
+
* @param string $old
|
222 |
+
* @return bool
|
223 |
+
*/
|
224 |
+
private function startJob( $new, $old ) {
|
225 |
+
|
226 |
+
if( $this->isExcludedTable( $new ) ) {
|
227 |
+
return false;
|
228 |
+
}
|
229 |
+
|
230 |
+
if( 0 != $this->options->job->start ) {
|
231 |
+
return true;
|
232 |
+
}
|
233 |
+
|
234 |
+
// Table does not exist
|
235 |
+
$result = $this->db->query( "SHOW TABLES LIKE '{$old}'" );
|
236 |
+
if( false === $result || 0 === $result ) {
|
237 |
+
$this->log( "DB External Copy: Table {$this->options->databaseDatabase}.{$old} does not exists. Skip!" );
|
238 |
+
return true;
|
239 |
+
}
|
240 |
+
|
241 |
+
$this->log( "DB External Copy: CREATE table {$this->options->databaseDatabase}.{$new}" );
|
242 |
+
$sql = $this->getCreateStatement( $old );
|
243 |
+
|
244 |
+
$search = '';
|
245 |
+
// Get table 'wp_users' from main site
|
246 |
+
if( 'users' === $this->strings->str_replace_first( $this->db->base_prefix, null, $old ) ) {
|
247 |
+
$search = $this->db->prefix . 'users';
|
248 |
+
}
|
249 |
+
// Get table 'wp_usermeta' from main site
|
250 |
+
if( 'usermeta' === $this->strings->str_replace_first( $this->db->base_prefix, null, $old ) ) {
|
251 |
+
$search = $this->db->prefix . 'usermeta';
|
252 |
+
}
|
253 |
+
|
254 |
+
// Replace table prefix to the new one
|
255 |
+
$sql = str_replace( "CREATE TABLE `{$search}`", "CREATE TABLE `{$new}`", $sql );
|
256 |
+
|
257 |
+
$this->stagingDb->query('SET FOREIGN_KEY_CHECKS=0;');
|
258 |
+
|
259 |
+
if( false === $this->stagingDb->query( $sql ) ) {
|
260 |
+
$this->returnException( "DB External Copy - Fatal Error: {$this->stagingDb->last_error} Query: {$sql}" );
|
261 |
+
}
|
262 |
+
|
263 |
+
|
264 |
+
// Count amount of rows to insert with next step
|
265 |
+
$this->options->job->total = 0;
|
266 |
+
$this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM `{$this->db->dbname}`.`{$old}`" );
|
267 |
+
|
268 |
+
$this->log( "DB External Copy: Table {$old} contains {$this->options->job->total} rows " );
|
269 |
+
|
270 |
+
|
271 |
+
if( 0 == $this->options->job->total ) {
|
272 |
+
$this->finishStep();
|
273 |
+
return false;
|
274 |
+
}
|
275 |
+
|
276 |
+
return true;
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Get MySQL query create table
|
281 |
+
*
|
282 |
+
* @param string $table_name Table name
|
283 |
+
* @return array
|
284 |
+
*/
|
285 |
+
private function getCreateStatement( $tableName ) {
|
286 |
+
|
287 |
+
// Query: Default
|
288 |
+
$row = $this->db->get_results( "SHOW CREATE TABLE `{$tableName}`", ARRAY_A );
|
289 |
+
|
290 |
+
// Query: Get wp_users from main site
|
291 |
+
if( 'users' === $this->strings->str_replace_first( $this->db->base_prefix, null, $tableName ) ) {
|
292 |
+
//$tableName = $this->db->base_prefix . 'users';
|
293 |
+
//$row = $this->db->get_results( "SHOW CREATE TABLE `{$tableName}`", ARRAY_A );
|
294 |
+
$row[0] = str_replace( $tableName, $this->db->prefix . 'users', $row[0] );
|
295 |
+
}
|
296 |
+
// Query: Get wp_usermeta from main site
|
297 |
+
if( 'usermeta' === $this->strings->str_replace_first( $this->db->base_prefix, null, $tableName ) ) {
|
298 |
+
//$tableName = $this->db->base_prefix . 'usermeta';
|
299 |
+
//$row = $this->db->get_results( "SHOW CREATE TABLE `{$tableName}`", ARRAY_A );
|
300 |
+
$row[0] = str_replace( $tableName, $this->db->prefix . 'usermeta', $row[0] );
|
301 |
+
}
|
302 |
+
|
303 |
+
// Get create table
|
304 |
+
if( isset( $row[0]['Create Table'] ) ) {
|
305 |
+
return $row[0]['Create Table'];
|
306 |
+
}
|
307 |
+
return array();
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* Copy data from old table to new table
|
312 |
+
* @param string $new
|
313 |
+
* @param string $old
|
314 |
+
*/
|
315 |
+
private function copyData( $new, $old ) {
|
316 |
+
|
317 |
+
$rows = $this->options->job->start + $this->settings->queryLimit;
|
318 |
+
$this->log(
|
319 |
+
"DB External Copy: INSERT {$this->db->dbname}.{$old} as {$this->options->databaseDatabase}.{$new} from {$this->options->job->start} to {$rows} records"
|
320 |
+
);
|
321 |
+
|
322 |
+
$limitation = '';
|
323 |
+
|
324 |
+
if( 0 < ( int ) $this->settings->queryLimit ) {
|
325 |
+
$limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
|
326 |
+
}
|
327 |
+
|
328 |
+
// Get data from production site
|
329 |
+
$rows = $this->db->get_results( "SELECT * FROM `{$old}` {$limitation}", ARRAY_A );
|
330 |
+
|
331 |
+
// Start transaction
|
332 |
+
$this->stagingDb->query( 'SET autocommit=0;' );
|
333 |
+
$this->stagingDb->query('SET FOREIGN_KEY_CHECKS=0;');
|
334 |
+
$this->stagingDb->query( 'START TRANSACTION;' );
|
335 |
+
|
336 |
+
// Copy into staging site
|
337 |
+
foreach ( $rows as $row ) {
|
338 |
+
$escaped_values = $this->mysqlEscapeMimic( array_values( $row ) );
|
339 |
+
$values = implode( "', '", $escaped_values );
|
340 |
+
$this->stagingDb->query( "INSERT INTO `{$new}` VALUES ('{$values}')" );
|
341 |
+
}
|
342 |
+
// Commit transaction
|
343 |
+
$this->stagingDb->query( 'COMMIT;' );
|
344 |
+
$this->stagingDb->query( 'SET autocommit=1;' );
|
345 |
+
|
346 |
+
|
347 |
+
// Set new offset
|
348 |
+
$this->options->job->start += $this->settings->queryLimit;
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
353 |
+
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
|
354 |
+
* @access public
|
355 |
+
* @param string $input The string to escape.
|
356 |
+
* @return string
|
357 |
+
*/
|
358 |
+
private function mysqlEscapeMimic( $input ) {
|
359 |
+
if( is_array( $input ) ) {
|
360 |
+
return array_map( __METHOD__, $input );
|
361 |
+
}
|
362 |
+
if( !empty( $input ) && is_string( $input ) ) {
|
363 |
+
return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
|
364 |
+
}
|
365 |
+
|
366 |
+
return $input;
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Is table excluded from search replace processing?
|
371 |
+
* @param string $table
|
372 |
+
* @return boolean
|
373 |
+
*/
|
374 |
+
private function isExcludedTable( $table ) {
|
375 |
+
|
376 |
+
$customTables = apply_filters( 'wpstg_clone_database_tables_exclude', array() );
|
377 |
+
$defaultTables = array('blogs', 'blog_versions');
|
378 |
+
|
379 |
+
$tables = array_merge( $customTables, $defaultTables );
|
380 |
+
|
381 |
+
$excludedTables = array();
|
382 |
+
foreach ( $tables as $key => $value ) {
|
383 |
+
$excludedTables[] = $this->options->prefix . $value;
|
384 |
+
}
|
385 |
+
|
386 |
+
if( in_array( $table, $excludedTables ) ) {
|
387 |
+
return true;
|
388 |
+
}
|
389 |
+
return false;
|
390 |
+
}
|
391 |
+
|
392 |
+
/**
|
393 |
+
* Finish the step
|
394 |
+
*/
|
395 |
+
private function finishStep() {
|
396 |
+
// This job is not finished yet
|
397 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
398 |
+
return false;
|
399 |
+
}
|
400 |
+
|
401 |
+
// Add it to cloned tables listing
|
402 |
+
$this->options->clonedTables[] = isset( $this->options->tables[$this->options->currentStep] ) ? $this->options->tables[$this->options->currentStep] : false;
|
403 |
+
|
404 |
+
// Reset job
|
405 |
+
$this->options->job = new \stdClass();
|
406 |
+
|
407 |
+
return true;
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Drop table if necessary
|
412 |
+
* @param string $new
|
413 |
+
*/
|
414 |
+
private function dropTable( $new ) {
|
415 |
+
|
416 |
+
$old = $this->stagingDb->get_var( $this->stagingDb->prepare( "SHOW TABLES LIKE %s", $new ) );
|
417 |
+
|
418 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
419 |
+
return;
|
420 |
+
}
|
421 |
+
|
422 |
+
$this->log( "DB External Copy: {$new} already exists, dropping it first" );
|
423 |
+
$this->stagingDb->query( "SET FOREIGN_KEY_CHECKS=0" );
|
424 |
+
$this->stagingDb->query( "DROP TABLE {$new}" );
|
425 |
+
$this->stagingDb->query( "SET FOREIGN_KEY_CHECKS=1" );
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Check if table needs to be dropped
|
430 |
+
* @param string $new
|
431 |
+
* @param string $old
|
432 |
+
* @return bool
|
433 |
+
*/
|
434 |
+
private function shouldDropTable( $new, $old ) {
|
435 |
+
if( $old === $new &&
|
436 |
+
(
|
437 |
+
!isset( $this->options->job->current ) ||
|
438 |
+
!isset( $this->options->job->start ) ||
|
439 |
+
0 == $this->options->job->start
|
440 |
+
) ) {
|
441 |
+
return true;
|
442 |
+
}
|
443 |
+
return false;
|
444 |
+
}
|
445 |
+
|
446 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/Directories.php
CHANGED
@@ -1,632 +1,609 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Logger;
|
12 |
-
use WPStaging\Utils\Strings;
|
13 |
-
use WPStaging\Iterators\RecursiveDirectoryIterator;
|
14 |
-
use WPStaging\Iterators\RecursiveFilterExclude;
|
15 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Class Files
|
19 |
-
* @package WPStaging\Backend\Modules\Directories
|
20 |
-
*/
|
21 |
-
class Directories extends JobExecutable {
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
$
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
}
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
private function sanitizeDirectorySeparator( $path ) {
|
611 |
-
$string = str_replace( "/", "\\", $path );
|
612 |
-
return str_replace( '\\\\', '\\', $string );
|
613 |
-
}
|
614 |
-
|
615 |
-
/**
|
616 |
-
* Check if directory is excluded
|
617 |
-
* @param string $directory
|
618 |
-
* @return bool
|
619 |
-
*/
|
620 |
-
protected function isDirectoryExcluded( $directory ) {
|
621 |
-
$directory = $this->sanitizeDirectorySeparator( $directory );
|
622 |
-
foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
|
623 |
-
$excludedDirectory = $this->sanitizeDirectorySeparator( $excludedDirectory );
|
624 |
-
if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
|
625 |
-
return true;
|
626 |
-
}
|
627 |
-
}
|
628 |
-
|
629 |
-
return false;
|
630 |
-
}
|
631 |
-
|
632 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Logger;
|
12 |
+
use WPStaging\Utils\Strings;
|
13 |
+
use WPStaging\Iterators\RecursiveDirectoryIterator;
|
14 |
+
use WPStaging\Iterators\RecursiveFilterExclude;
|
15 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Class Files
|
19 |
+
* @package WPStaging\Backend\Modules\Directories
|
20 |
+
*/
|
21 |
+
class Directories extends JobExecutable {
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
private $files = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Total steps to do
|
30 |
+
* @var int
|
31 |
+
*/
|
32 |
+
private $total = 5;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* path to the cache file
|
36 |
+
* @var string
|
37 |
+
*/
|
38 |
+
private $filename;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Initialize
|
42 |
+
*/
|
43 |
+
public function initialize() {
|
44 |
+
$this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
49 |
+
* @return void
|
50 |
+
*/
|
51 |
+
protected function calculateTotalSteps() {
|
52 |
+
|
53 |
+
$this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Start Module
|
58 |
+
* @return object
|
59 |
+
*/
|
60 |
+
public function start() {
|
61 |
+
|
62 |
+
// Execute steps
|
63 |
+
$this->run();
|
64 |
+
|
65 |
+
// Save option, progress
|
66 |
+
$this->saveProgress();
|
67 |
+
|
68 |
+
return ( object ) $this->response;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Step 0
|
73 |
+
* Get WP Root files
|
74 |
+
* Does not collect any sub folders
|
75 |
+
*/
|
76 |
+
private function getWpRootFiles() {
|
77 |
+
|
78 |
+
// Skip it
|
79 |
+
if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
|
80 |
+
$this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
|
84 |
+
// open file handle
|
85 |
+
$files = $this->open( $this->filename, 'a' );
|
86 |
+
|
87 |
+
try {
|
88 |
+
|
89 |
+
// Iterate over wp root directory
|
90 |
+
$iterator = new \DirectoryIterator( \WPStaging\WPStaging::getWPpath() );
|
91 |
+
|
92 |
+
$this->log( "Scanning / for files" );
|
93 |
+
|
94 |
+
// Write path line
|
95 |
+
foreach ( $iterator as $item ) {
|
96 |
+
if( !$item->isDot() && $item->isFile() ) {
|
97 |
+
if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
|
98 |
+
$this->options->totalFiles++;
|
99 |
+
|
100 |
+
// Too much cpu time
|
101 |
+
//$this->options->totalFileSize += $iterator->getSize();
|
102 |
+
}
|
103 |
+
}
|
104 |
+
}
|
105 |
+
} catch ( \Exception $e ) {
|
106 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
107 |
+
}
|
108 |
+
|
109 |
+
$this->close( $files );
|
110 |
+
return true;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Step 2
|
115 |
+
* Get WP Content Files without multisite folder wp-content/uploads/sites or wp-content/blogs.dir/
|
116 |
+
*/
|
117 |
+
private function getWpContentFiles() {
|
118 |
+
|
119 |
+
// Skip it
|
120 |
+
if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
|
121 |
+
$this->log( "Skip " . WP_CONTENT_DIR );
|
122 |
+
return true;
|
123 |
+
}
|
124 |
+
// open file handle
|
125 |
+
$files = $this->open( $this->filename, 'a' );
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Excluded folders relative to the folder to iterate
|
129 |
+
*/
|
130 |
+
$excludePaths = array(
|
131 |
+
'cache',
|
132 |
+
'plugins/wps-hide-login',
|
133 |
+
'uploads/sites',
|
134 |
+
'blogs.dir'
|
135 |
+
);
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Get user excluded folders
|
139 |
+
*/
|
140 |
+
$directory = array();
|
141 |
+
foreach ( $this->options->excludedDirectories as $dir ) {
|
142 |
+
// Windows compatibility fix
|
143 |
+
$dir = wpstg_replace_windows_directory_separator( $dir );
|
144 |
+
$wpContentDir = wpstg_replace_windows_directory_separator( WP_CONTENT_DIR );
|
145 |
+
|
146 |
+
if( strpos( $dir, $wpContentDir ) !== false ) {
|
147 |
+
$directory[] = ltrim( str_replace( $wpContentDir, '', $dir ), '/\\' );
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
$excludePaths = array_merge( $excludePaths, $directory );
|
152 |
+
|
153 |
+
try {
|
154 |
+
|
155 |
+
// Iterate over content directory
|
156 |
+
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
|
157 |
+
|
158 |
+
// Exclude sites, uploads, plugins or themes
|
159 |
+
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_mu_excl_folders', $excludePaths ) );
|
160 |
+
|
161 |
+
// Recursively iterate over content directory
|
162 |
+
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
163 |
+
|
164 |
+
$this->log( "Scanning wp-content folder " . WP_CONTENT_DIR );
|
165 |
+
|
166 |
+
// Write path line
|
167 |
+
foreach ( $iterator as $item ) {
|
168 |
+
if( $item->isFile() ) {
|
169 |
+
$file = 'wp-content/' . $iterator->getSubPathName() . PHP_EOL;
|
170 |
+
if( $this->write( $files, $file ) ) {
|
171 |
+
$this->options->totalFiles++;
|
172 |
+
|
173 |
+
// Add current file size
|
174 |
+
$this->options->totalFileSize += $iterator->getSize();
|
175 |
+
}
|
176 |
+
}
|
177 |
+
}
|
178 |
+
} catch ( \Exception $e ) {
|
179 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
180 |
+
//throw new \Exception( 'Error: ' . $e->getMessage() );
|
181 |
+
}
|
182 |
+
|
183 |
+
// close the file handler
|
184 |
+
$this->close( $files );
|
185 |
+
return true;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Step 2
|
190 |
+
* @return boolean
|
191 |
+
* @throws \Exception
|
192 |
+
*/
|
193 |
+
private function getWpIncludesFiles() {
|
194 |
+
|
195 |
+
// Skip it
|
196 |
+
if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-includes' ) ) {
|
197 |
+
$this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-includes' );
|
198 |
+
return true;
|
199 |
+
}
|
200 |
+
|
201 |
+
// open file handle and attach data to end of file
|
202 |
+
$files = $this->open( $this->filename, 'a' );
|
203 |
+
|
204 |
+
try {
|
205 |
+
|
206 |
+
// Iterate over wp-admin directory
|
207 |
+
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes/' );
|
208 |
+
|
209 |
+
// Recursively iterate over wp-includes directory
|
210 |
+
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
211 |
+
|
212 |
+
$this->log( "Scanning /wp-includes for its sub-directories and files" );
|
213 |
+
|
214 |
+
// Write files
|
215 |
+
foreach ( $iterator as $item ) {
|
216 |
+
if( $item->isFile() ) {
|
217 |
+
if( $this->write( $files, 'wp-includes/' . $iterator->getSubPathName() . PHP_EOL ) ) {
|
218 |
+
$this->options->totalFiles++;
|
219 |
+
|
220 |
+
// Add current file size
|
221 |
+
$this->options->totalFileSize += $iterator->getSize();
|
222 |
+
}
|
223 |
+
}
|
224 |
+
}
|
225 |
+
} catch ( \Exception $e ) {
|
226 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
227 |
+
}
|
228 |
+
|
229 |
+
// close the file handler
|
230 |
+
$this->close( $files );
|
231 |
+
return true;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Step 3
|
236 |
+
* @return boolean
|
237 |
+
* @throws \Exception
|
238 |
+
*/
|
239 |
+
private function getWpAdminFiles() {
|
240 |
+
|
241 |
+
// Skip it
|
242 |
+
if( $this->isDirectoryExcluded( \WPStaging\WPStaging::getWPpath() . 'wp-admin/' ) ) {
|
243 |
+
$this->log( "Skip " . \WPStaging\WPStaging::getWPpath() . 'wp-admin/' );
|
244 |
+
return true;
|
245 |
+
}
|
246 |
+
|
247 |
+
// open file handle and attach data to end of file
|
248 |
+
$files = $this->open( $this->filename, 'a' );
|
249 |
+
|
250 |
+
try {
|
251 |
+
|
252 |
+
// Iterate over wp-admin directory
|
253 |
+
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin/' );
|
254 |
+
|
255 |
+
// Recursively iterate over content directory
|
256 |
+
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
257 |
+
|
258 |
+
$this->log( "Scanning /wp-admin for its sub-directories and files" );
|
259 |
+
|
260 |
+
// Write path line
|
261 |
+
foreach ( $iterator as $item ) {
|
262 |
+
if( $item->isFile() ) {
|
263 |
+
if( $this->write( $files, 'wp-admin/' . $iterator->getSubPathName() . PHP_EOL ) ) {
|
264 |
+
$this->options->totalFiles++;
|
265 |
+
// Too much cpu time
|
266 |
+
//$this->options->totalFileSize += $iterator->getSize();
|
267 |
+
}
|
268 |
+
}
|
269 |
+
}
|
270 |
+
} catch ( \Exception $e ) {
|
271 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
272 |
+
}
|
273 |
+
|
274 |
+
// close the file handler
|
275 |
+
$this->close( $files );
|
276 |
+
return true;
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Step 4
|
281 |
+
* Get WP Content Uploads Files multisite folder wp-content/uploads/sites or wp-content/blogs.dir/ID/files
|
282 |
+
*/
|
283 |
+
private function getWpContentUploadsSites() {
|
284 |
+
|
285 |
+
// Skip if main site is cloned
|
286 |
+
if( is_main_site() ) {
|
287 |
+
return true;
|
288 |
+
}
|
289 |
+
|
290 |
+
$blogId = get_current_blog_id();
|
291 |
+
|
292 |
+
// Absolute path to uploads folder
|
293 |
+
//$path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . $blogId . DIRECTORY_SEPARATOR;
|
294 |
+
$path = $this->getAbsUploadPath();
|
295 |
+
|
296 |
+
// Skip it
|
297 |
+
if( !is_dir( $path ) ) {
|
298 |
+
$this->log( "Skipping: {$path} does not exist." );
|
299 |
+
return true;
|
300 |
+
}
|
301 |
+
|
302 |
+
// Skip it
|
303 |
+
if( $this->isDirectoryExcluded( $path ) ) {
|
304 |
+
$this->log( "Skipping: {$path}" );
|
305 |
+
return true;
|
306 |
+
}
|
307 |
+
|
308 |
+
|
309 |
+
// open file handle
|
310 |
+
$files = $this->open( $this->filename, 'a' );
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Excluded folders relative to the folder to iterate
|
314 |
+
*/
|
315 |
+
$excludePaths = array(
|
316 |
+
'cache',
|
317 |
+
'plugins/wps-hide-login',
|
318 |
+
'uploads/sites'
|
319 |
+
);
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Get user excluded folders
|
323 |
+
*/
|
324 |
+
$directory = array();
|
325 |
+
foreach ( $this->options->excludedDirectories as $dir ) {
|
326 |
+
$path = wpstg_replace_windows_directory_separator( $path );
|
327 |
+
$dir = wpstg_replace_windows_directory_separator( $dir );
|
328 |
+
if( strpos( $dir, $path ) !== false ) {
|
329 |
+
$directory[] = ltrim( str_replace( $path, '', $dir ), '/' );
|
330 |
+
}
|
331 |
+
}
|
332 |
+
|
333 |
+
$excludePaths = array_merge( $excludePaths, $directory );
|
334 |
+
|
335 |
+
try {
|
336 |
+
|
337 |
+
// Iterate over content directory
|
338 |
+
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $path );
|
339 |
+
|
340 |
+
// Exclude sites, uploads, plugins or themes
|
341 |
+
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $excludePaths );
|
342 |
+
|
343 |
+
// Recursively iterate over content directory
|
344 |
+
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
345 |
+
$uploadDir = wpstg_get_upload_dir();
|
346 |
+
$this->log( "Scanning {$uploadDir} for its sub-directories and files..." );
|
347 |
+
|
348 |
+
// Write path line
|
349 |
+
foreach ( $iterator as $item ) {
|
350 |
+
if( $item->isFile() ) {
|
351 |
+
$file = $this->getRelUploadPath() . $iterator->getSubPathName() . PHP_EOL;
|
352 |
+
if( $this->write( $files, $file ) ) {
|
353 |
+
$this->options->totalFiles++;
|
354 |
+
|
355 |
+
// Add current file size
|
356 |
+
$this->options->totalFileSize += $iterator->getSize();
|
357 |
+
}
|
358 |
+
}
|
359 |
+
}
|
360 |
+
} catch ( \Exception $e ) {
|
361 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
362 |
+
}
|
363 |
+
|
364 |
+
// close the file handler
|
365 |
+
$this->close( $files );
|
366 |
+
return true;
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Get absolute path to the upload folder e.g. /srv/www/wp-content/blogs.dir/ID/files or /srv/www/wp-content/uploads/sites/ID/
|
371 |
+
* @return type
|
372 |
+
*/
|
373 |
+
private function getAbsUploadPath() {
|
374 |
+
// Check first which method is used
|
375 |
+
$uploads = wp_upload_dir();
|
376 |
+
$basedir = $uploads['basedir'];
|
377 |
+
|
378 |
+
return trailingslashit( $basedir );
|
379 |
+
}
|
380 |
+
|
381 |
+
/**
|
382 |
+
* Get relative path to the upload folder like wp-content/uploads or wp-content/blogs.dir/2/files
|
383 |
+
* @return string
|
384 |
+
*/
|
385 |
+
private function getRelUploadPath() {
|
386 |
+
$uploads = wp_upload_dir();
|
387 |
+
$basedir = $uploads['basedir'];
|
388 |
+
|
389 |
+
return trailingslashit( str_replace( wpstg_replace_windows_directory_separator(\WPStaging\WPStaging::getWPpath()), null, wpstg_replace_windows_directory_separator($basedir) ) );
|
390 |
+
}
|
391 |
+
|
392 |
+
/**
|
393 |
+
* Step 5 - x
|
394 |
+
* Get extra folders of the wp root level
|
395 |
+
* Does not collect wp-includes, wp-admin and wp-content folder
|
396 |
+
*/
|
397 |
+
private function getExtraFiles( $folder ) {
|
398 |
+
|
399 |
+
if( !is_dir( $folder ) ) {
|
400 |
+
return true;
|
401 |
+
}
|
402 |
+
|
403 |
+
// open file handle and attach data to end of file
|
404 |
+
$files = $this->open( $this->filename, 'a' );
|
405 |
+
|
406 |
+
try {
|
407 |
+
|
408 |
+
// Iterate over extra directory
|
409 |
+
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
|
410 |
+
|
411 |
+
$exclude = array();
|
412 |
+
|
413 |
+
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
|
414 |
+
// Recursively iterate over content directory
|
415 |
+
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
416 |
+
|
417 |
+
$strings = new Strings();
|
418 |
+
$this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
|
419 |
+
|
420 |
+
// Write path line
|
421 |
+
foreach ( $iterator as $item ) {
|
422 |
+
if( $item->isFile() ) {
|
423 |
+
$path = str_replace( wpstg_replace_windows_directory_separator(\WPStaging\WPStaging::getWPpath()), '', wpstg_replace_windows_directory_separator($folder) ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL;
|
424 |
+
if( $this->write( $files, $path ) ) {
|
425 |
+
$this->options->totalFiles++;
|
426 |
+
// Add current file size
|
427 |
+
$this->options->totalFileSize += $iterator->getSize();
|
428 |
+
}
|
429 |
+
}
|
430 |
+
}
|
431 |
+
} catch ( \Exception $e ) {
|
432 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
433 |
+
}
|
434 |
+
|
435 |
+
// close the file handler
|
436 |
+
$this->close( $files );
|
437 |
+
return true;
|
438 |
+
}
|
439 |
+
|
440 |
+
/**
|
441 |
+
* Closes a file handle
|
442 |
+
*
|
443 |
+
* @param resource $handle File handle to close
|
444 |
+
* @return boolean
|
445 |
+
*/
|
446 |
+
public function close( $handle ) {
|
447 |
+
return @fclose( $handle );
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Opens a file in specified mode
|
452 |
+
*
|
453 |
+
* @param string $file Path to the file to open
|
454 |
+
* @param string $mode Mode in which to open the file
|
455 |
+
* @return resource
|
456 |
+
* @throws Exception
|
457 |
+
*/
|
458 |
+
public function open( $file, $mode ) {
|
459 |
+
|
460 |
+
$file_handle = @fopen( $file, $mode );
|
461 |
+
if( false === $file_handle ) {
|
462 |
+
$this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wp-staging' ), $file, $mode ) );
|
463 |
+
}
|
464 |
+
|
465 |
+
return $file_handle;
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* Write contents to a file
|
470 |
+
*
|
471 |
+
* @param resource $handle File handle to write to
|
472 |
+
* @param string $content Contents to write to the file
|
473 |
+
* @return integer
|
474 |
+
* @throws Exception
|
475 |
+
* @throws Exception
|
476 |
+
*/
|
477 |
+
public function write( $handle, $content ) {
|
478 |
+
$write_result = @fwrite( $handle, $content );
|
479 |
+
if( false === $write_result ) {
|
480 |
+
if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
|
481 |
+
throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wp-staging' ), $meta['uri'] ) );
|
482 |
+
}
|
483 |
+
} elseif( strlen( $content ) !== $write_result ) {
|
484 |
+
throw new \Exception( __( 'Out of disk space.', 'wp-staging' ) );
|
485 |
+
}
|
486 |
+
|
487 |
+
return $write_result;
|
488 |
+
}
|
489 |
+
|
490 |
+
/**
|
491 |
+
* Execute the Current Step
|
492 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
493 |
+
* @return bool
|
494 |
+
*/
|
495 |
+
protected function execute() {
|
496 |
+
|
497 |
+
// No job left to execute
|
498 |
+
if( $this->isFinished() ) {
|
499 |
+
$this->prepareResponse( true, false );
|
500 |
+
return false;
|
501 |
+
}
|
502 |
+
|
503 |
+
|
504 |
+
if( $this->options->currentStep == 0 ) {
|
505 |
+
$this->getWpRootFiles();
|
506 |
+
$this->prepareResponse( false, true );
|
507 |
+
return false;
|
508 |
+
}
|
509 |
+
|
510 |
+
if( $this->options->currentStep == 1 ) {
|
511 |
+
$this->getWpContentFiles();
|
512 |
+
$this->prepareResponse( false, true );
|
513 |
+
return false;
|
514 |
+
}
|
515 |
+
|
516 |
+
if( $this->options->currentStep == 2 ) {
|
517 |
+
$this->getWpIncludesFiles();
|
518 |
+
$this->prepareResponse( false, true );
|
519 |
+
return false;
|
520 |
+
}
|
521 |
+
|
522 |
+
if( $this->options->currentStep == 3 ) {
|
523 |
+
$this->getWpAdminFiles();
|
524 |
+
$this->prepareResponse( false, true );
|
525 |
+
return false;
|
526 |
+
}
|
527 |
+
|
528 |
+
if( $this->options->currentStep == 4 ) {
|
529 |
+
$this->getWpContentUploadsSites();
|
530 |
+
$this->prepareResponse( false, true );
|
531 |
+
return false;
|
532 |
+
}
|
533 |
+
|
534 |
+
if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
|
535 |
+
$this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
|
536 |
+
$this->prepareResponse( false, true );
|
537 |
+
return false;
|
538 |
+
}
|
539 |
+
|
540 |
+
|
541 |
+
// Prepare response
|
542 |
+
$this->prepareResponse( false, true );
|
543 |
+
// Not finished
|
544 |
+
return true;
|
545 |
+
}
|
546 |
+
|
547 |
+
/**
|
548 |
+
* Checks Whether There is Any Job to Execute or Not
|
549 |
+
* @return bool
|
550 |
+
*/
|
551 |
+
protected function isFinished() {
|
552 |
+
if( $this->options->currentStep >= $this->options->totalSteps ) {
|
553 |
+
return true;
|
554 |
+
}
|
555 |
+
}
|
556 |
+
|
557 |
+
/**
|
558 |
+
* Save files
|
559 |
+
* @return bool
|
560 |
+
*/
|
561 |
+
protected function saveProgress() {
|
562 |
+
return $this->saveOptions();
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* Get files
|
567 |
+
* @return void
|
568 |
+
*/
|
569 |
+
protected function getFiles() {
|
570 |
+
$fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
|
571 |
+
|
572 |
+
if( false === ($this->files = @file_get_contents( $fileName )) ) {
|
573 |
+
$this->files = array();
|
574 |
+
return;
|
575 |
+
}
|
576 |
+
|
577 |
+
$this->files = explode( PHP_EOL, $this->files );
|
578 |
+
}
|
579 |
+
|
580 |
+
/**
|
581 |
+
* Replace forward slash with backslash directory separator
|
582 |
+
*
|
583 |
+
* @param string $path Path
|
584 |
+
*
|
585 |
+
* @return string
|
586 |
+
*/
|
587 |
+
// private function sanitizeDirectorySeparator( $path ) {
|
588 |
+
// $string = str_replace( "/", "\\", $path );
|
589 |
+
// return str_replace( '\\\\', '\\', $string );
|
590 |
+
// }
|
591 |
+
|
592 |
+
/**
|
593 |
+
* Check if directory is excluded
|
594 |
+
* @param string $directory
|
595 |
+
* @return bool
|
596 |
+
*/
|
597 |
+
protected function isDirectoryExcluded( $directory ) {
|
598 |
+
$directory = wpstg_replace_windows_directory_separator( $directory );
|
599 |
+
foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
|
600 |
+
$excludedDirectory = wpstg_replace_windows_directory_separator( $excludedDirectory );
|
601 |
+
if( strpos( trailingslashit( $directory ), trailingslashit( $excludedDirectory ) ) === 0 ) {
|
602 |
+
return true;
|
603 |
+
}
|
604 |
+
}
|
605 |
+
|
606 |
+
return false;
|
607 |
+
}
|
608 |
+
|
609 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/Backend/Modules/Jobs/Multisite/Files.php
CHANGED
@@ -1,431 +1,481 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
6 |
-
// No Direct Access
|
7 |
-
use WPStaging\Utils\Logger;
|
8 |
-
|
9 |
-
if( !defined( "WPINC" ) ) {
|
10 |
-
die;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class Files
|
15 |
-
* @package WPStaging\Backend\Modules\Jobs
|
16 |
-
*/
|
17 |
-
class Files extends JobExecutable {
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @var \SplFileObject
|
21 |
-
*/
|
22 |
-
private $file;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @var int
|
26 |
-
*/
|
27 |
-
private $maxFilesPerRun;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* @var string
|
31 |
-
*/
|
32 |
-
private $destination;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Initialization
|
36 |
-
*/
|
37 |
-
public function initialize() {
|
38 |
-
|
39 |
-
$this->destination = $this->options->destinationDir;
|
40 |
-
|
41 |
-
$filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
|
42 |
-
|
43 |
-
if( is_file( $filePath ) ) {
|
44 |
-
$this->file = new \SplFileObject( $filePath, 'r' );
|
45 |
-
}
|
46 |
-
|
47 |
-
// Informational logs
|
48 |
-
if( 0 == $this->options->currentStep ) {
|
49 |
-
$this->log( "Copying files..." );
|
50 |
-
}
|
51 |
-
|
52 |
-
$this->settings->batchSize = $this->settings->batchSize * 1000000;
|
53 |
-
$this->maxFilesPerRun = $this->settings->fileLimit;
|
54 |
-
//$this->maxFilesPerRun = ($this->settings->cpuLoad === 'low') ? 50 : 1;
|
55 |
-
}
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
59 |
-
* @return void
|
60 |
-
*/
|
61 |
-
protected function calculateTotalSteps() {
|
62 |
-
$this->options->totalSteps = ceil( $this->options->totalFiles / $this->maxFilesPerRun );
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* Execute the Current Step
|
67 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
68 |
-
* @return bool
|
69 |
-
*/
|
70 |
-
protected function execute() {
|
71 |
-
// Finished
|
72 |
-
if( $this->isFinished() ) {
|
73 |
-
$this->log( "Copying files finished" );
|
74 |
-
$this->prepareResponse( true, false );
|
75 |
-
return false;
|
76 |
-
}
|
77 |
-
|
78 |
-
// Get files and copy'em
|
79 |
-
if( !$this->getFilesAndCopy() ) {
|
80 |
-
$this->prepareResponse( false, false );
|
81 |
-
return false;
|
82 |
-
}
|
83 |
-
|
84 |
-
// Prepare and return response
|
85 |
-
$this->prepareResponse();
|
86 |
-
|
87 |
-
// Not finished
|
88 |
-
return true;
|
89 |
-
}
|
90 |
-
|
91 |
-
/**
|
92 |
-
* Get files and copy
|
93 |
-
* @return bool
|
94 |
-
*/
|
95 |
-
private function getFilesAndCopy() {
|
96 |
-
// Over limits threshold
|
97 |
-
if( $this->isOverThreshold() ) {
|
98 |
-
// Prepare response and save current progress
|
99 |
-
$this->prepareResponse( false, false );
|
100 |
-
$this->saveOptions();
|
101 |
-
return false;
|
102 |
-
}
|
103 |
-
|
104 |
-
// Go to last copied line and than to next one
|
105 |
-
//if ($this->options->copiedFiles != 0) {
|
106 |
-
if( isset( $this->options->copiedFiles ) && $this->options->copiedFiles != 0 ) {
|
107 |
-
$this->file->seek( $this->options->copiedFiles - 1 );
|
108 |
-
}
|
109 |
-
|
110 |
-
$this->file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );
|
111 |
-
|
112 |
-
// Start time
|
113 |
-
//$start = microtime( true );
|
114 |
-
// Loop x files at a time
|
115 |
-
//$this->maxFilesPerRun = 300;
|
116 |
-
for ( $i = 0; $i < $this->maxFilesPerRun; $i++ ) {
|
117 |
-
|
118 |
-
// Reached timeout
|
119 |
-
// if( ( $timeout = apply_filters( 'wpstg_job_timeout', 10 ) ) ) {
|
120 |
-
// if( ( \microtime( true ) - $start ) > $timeout ) {
|
121 |
-
// // Prepare response and save current progress
|
122 |
-
// $this->prepareResponse( false, true );
|
123 |
-
// $this->saveOptions();
|
124 |
-
// return false;
|
125 |
-
// }
|
126 |
-
// }
|
127 |
-
// Increment copied files
|
128 |
-
// Do this anytime to make sure to not stuck in the same step / files
|
129 |
-
$this->options->copiedFiles++;
|
130 |
-
|
131 |
-
// End of file
|
132 |
-
if( $this->file->eof() ) {
|
133 |
-
break;
|
134 |
-
}
|
135 |
-
|
136 |
-
$file = $this->file->fgets();
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
$this->options->
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
* @
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
$
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
*
|
244 |
-
* @
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
*
|
257 |
-
*
|
258 |
-
* @
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
$
|
264 |
-
$
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
$
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
*
|
304 |
-
* @
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
*
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
*
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
}
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
*
|
416 |
-
* @param string $
|
417 |
-
* @return boolean
|
418 |
-
*/
|
419 |
-
private function
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
6 |
+
// No Direct Access
|
7 |
+
use WPStaging\Utils\Logger;
|
8 |
+
|
9 |
+
if( !defined( "WPINC" ) ) {
|
10 |
+
die;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class Files
|
15 |
+
* @package WPStaging\Backend\Modules\Jobs
|
16 |
+
*/
|
17 |
+
class Files extends JobExecutable {
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var \SplFileObject
|
21 |
+
*/
|
22 |
+
private $file;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var int
|
26 |
+
*/
|
27 |
+
private $maxFilesPerRun;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var string
|
31 |
+
*/
|
32 |
+
private $destination;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Initialization
|
36 |
+
*/
|
37 |
+
public function initialize() {
|
38 |
+
|
39 |
+
$this->destination = $this->options->destinationDir;
|
40 |
+
|
41 |
+
$filePath = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
|
42 |
+
|
43 |
+
if( is_file( $filePath ) ) {
|
44 |
+
$this->file = new \SplFileObject( $filePath, 'r' );
|
45 |
+
}
|
46 |
+
|
47 |
+
// Informational logs
|
48 |
+
if( 0 == $this->options->currentStep ) {
|
49 |
+
$this->log( "Copying files..." );
|
50 |
+
}
|
51 |
+
|
52 |
+
$this->settings->batchSize = $this->settings->batchSize * 1000000;
|
53 |
+
$this->maxFilesPerRun = $this->settings->fileLimit;
|
54 |
+
//$this->maxFilesPerRun = ($this->settings->cpuLoad === 'low') ? 50 : 1;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
59 |
+
* @return void
|
60 |
+
*/
|
61 |
+
protected function calculateTotalSteps() {
|
62 |
+
$this->options->totalSteps = ceil( $this->options->totalFiles / $this->maxFilesPerRun );
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Execute the Current Step
|
67 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
68 |
+
* @return bool
|
69 |
+
*/
|
70 |
+
protected function execute() {
|
71 |
+
// Finished
|
72 |
+
if( $this->isFinished() ) {
|
73 |
+
$this->log( "Copying files finished" );
|
74 |
+
$this->prepareResponse( true, false );
|
75 |
+
return false;
|
76 |
+
}
|
77 |
+
|
78 |
+
// Get files and copy'em
|
79 |
+
if( !$this->getFilesAndCopy() ) {
|
80 |
+
$this->prepareResponse( false, false );
|
81 |
+
return false;
|
82 |
+
}
|
83 |
+
|
84 |
+
// Prepare and return response
|
85 |
+
$this->prepareResponse();
|
86 |
+
|
87 |
+
// Not finished
|
88 |
+
return true;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Get files and copy
|
93 |
+
* @return bool
|
94 |
+
*/
|
95 |
+
private function getFilesAndCopy() {
|
96 |
+
// Over limits threshold
|
97 |
+
if( $this->isOverThreshold() ) {
|
98 |
+
// Prepare response and save current progress
|
99 |
+
$this->prepareResponse( false, false );
|
100 |
+
$this->saveOptions();
|
101 |
+
return false;
|
102 |
+
}
|
103 |
+
|
104 |
+
// Go to last copied line and than to next one
|
105 |
+
//if ($this->options->copiedFiles != 0) {
|
106 |
+
if( isset( $this->options->copiedFiles ) && $this->options->copiedFiles != 0 ) {
|
107 |
+
$this->file->seek( $this->options->copiedFiles - 1 );
|
108 |
+
}
|
109 |
+
|
110 |
+
$this->file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );
|
111 |
+
|
112 |
+
// Start time
|
113 |
+
//$start = microtime( true );
|
114 |
+
// Loop x files at a time
|
115 |
+
//$this->maxFilesPerRun = 300;
|
116 |
+
for ( $i = 0; $i < $this->maxFilesPerRun; $i++ ) {
|
117 |
+
|
118 |
+
// Reached timeout
|
119 |
+
// if( ( $timeout = apply_filters( 'wpstg_job_timeout', 10 ) ) ) {
|
120 |
+
// if( ( \microtime( true ) - $start ) > $timeout ) {
|
121 |
+
// // Prepare response and save current progress
|
122 |
+
// $this->prepareResponse( false, true );
|
123 |
+
// $this->saveOptions();
|
124 |
+
// return false;
|
125 |
+
// }
|
126 |
+
// }
|
127 |
+
// Increment copied files
|
128 |
+
// Do this anytime to make sure to not stuck in the same step / files
|
129 |
+
$this->options->copiedFiles++;
|
130 |
+
|
131 |
+
// End of file
|
132 |
+
if( $this->file->eof() ) {
|
133 |
+
break;
|
134 |
+
}
|
135 |
+
|
136 |
+
$file = $this->file->fgets();
|
137 |
+
|
138 |
+
|
139 |
+
$this->copyFile( $file );
|
140 |
+
}
|
141 |
+
|
142 |
+
|
143 |
+
|
144 |
+
$totalFiles = $this->options->copiedFiles;
|
145 |
+
// Log this only every 50 entries to keep the log small and to not block the rendering browser
|
146 |
+
if( $this->options->copiedFiles % 50 == 0 ) {
|
147 |
+
$this->log( "Total {$totalFiles} files processed" );
|
148 |
+
}
|
149 |
+
|
150 |
+
return true;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Checks Whether There is Any Job to Execute or Not
|
155 |
+
* @return bool
|
156 |
+
*/
|
157 |
+
private function isFinished() {
|
158 |
+
return (
|
159 |
+
!isset( $this->options->isRunning ) ||
|
160 |
+
$this->options->currentStep > $this->options->totalSteps ||
|
161 |
+
$this->options->copiedFiles >= $this->options->totalFiles
|
162 |
+
);
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* @param string $file
|
167 |
+
* @return bool
|
168 |
+
*/
|
169 |
+
private function copyFile( $file ) {
|
170 |
+
|
171 |
+
$file = trim( \WPStaging\WPStaging::getWPpath() . $file );
|
172 |
+
|
173 |
+
$file = wpstg_replace_windows_directory_separator($file);
|
174 |
+
|
175 |
+
$directory = dirname( $file );
|
176 |
+
|
177 |
+
// Directory is excluded
|
178 |
+
if( $this->isDirectoryExcluded( $directory ) ) {
|
179 |
+
$this->debugLog( "Skipping directory by rule: {$file}", Logger::TYPE_INFO );
|
180 |
+
return false;
|
181 |
+
}
|
182 |
+
|
183 |
+
// File is excluded
|
184 |
+
if( $this->isFileExcluded( $file ) ) {
|
185 |
+
$this->debugLog( "Skipping file by rule: {$file}", Logger::TYPE_INFO );
|
186 |
+
return false;
|
187 |
+
}
|
188 |
+
// Path + File is excluded
|
189 |
+
if( $this->isFileExcludedFullPath( $file ) ) {
|
190 |
+
$this->debugLog( "Skipping file by rule: {$file}", Logger::TYPE_INFO );
|
191 |
+
return false;
|
192 |
+
}
|
193 |
+
|
194 |
+
// Invalid file, skipping it as if succeeded
|
195 |
+
if( !is_file( $file ) ) {
|
196 |
+
$this->log( "File doesn't exist {$file}", Logger::TYPE_WARNING );
|
197 |
+
return true;
|
198 |
+
}
|
199 |
+
// Invalid file, skipping it as if succeeded
|
200 |
+
if( !is_readable( $file ) ) {
|
201 |
+
$this->log( "Can't read file {$file}", Logger::TYPE_WARNING );
|
202 |
+
return true;
|
203 |
+
}
|
204 |
+
|
205 |
+
|
206 |
+
// Get file size
|
207 |
+
$fileSize = filesize( $file );
|
208 |
+
|
209 |
+
// File is over maximum allowed file size (8MB)
|
210 |
+
if( $fileSize >= $this->settings->maxFileSize * 1000000 ) {
|
211 |
+
$this->log( "Skipping big file: {$file}", Logger::TYPE_INFO );
|
212 |
+
return false;
|
213 |
+
}
|
214 |
+
|
215 |
+
// Failed to get destination
|
216 |
+
if( false === ($destination = $this->getDestination( $file )) ) {
|
217 |
+
$this->log( "Can't get the destination of {$file}", Logger::TYPE_WARNING );
|
218 |
+
return false;
|
219 |
+
}
|
220 |
+
|
221 |
+
// File is over batch size
|
222 |
+
if( $fileSize >= $this->settings->batchSize ) {
|
223 |
+
$this->log( "Trying to copy big file: {$file} -> {$destination}", Logger::TYPE_INFO );
|
224 |
+
return $this->copyBig( $file, $destination, $this->settings->batchSize );
|
225 |
+
}
|
226 |
+
|
227 |
+
// Attempt to copy
|
228 |
+
if( !@copy( $file, $destination ) ) {
|
229 |
+
$errors = error_get_last();
|
230 |
+
$this->log( "Files: Failed to copy file to destination. Error: {$errors['message']} {$file} -> {$destination}", Logger::TYPE_ERROR );
|
231 |
+
return false;
|
232 |
+
}
|
233 |
+
|
234 |
+
// Set file permissions
|
235 |
+
@chmod( $destination, wpstg_get_permissions_for_file() );
|
236 |
+
|
237 |
+
$this->setDirPermissions( $destination );
|
238 |
+
|
239 |
+
return true;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Set directory permissions
|
244 |
+
* @param type $file
|
245 |
+
* @return boolean
|
246 |
+
*/
|
247 |
+
private function setDirPermissions( $file ) {
|
248 |
+
$dir = dirname( $file );
|
249 |
+
if( is_dir( $dir ) ) {
|
250 |
+
@chmod( $dir, wpstg_get_permissions_for_directory() );
|
251 |
+
}
|
252 |
+
return false;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Gets destination file and checks if the directory exists, if it does not attempts to create it.
|
257 |
+
* If creating destination directory fails, it returns false, gives destination full path otherwise
|
258 |
+
* @param string $file
|
259 |
+
* @return bool|string
|
260 |
+
*/
|
261 |
+
private function getDestination( $file ) {
|
262 |
+
//$file = $this->getMultisiteUploadFolder( $file );
|
263 |
+
$file = wpstg_replace_windows_directory_separator($file);
|
264 |
+
$rootPath = wpstg_replace_windows_directory_separator(\WPStaging\WPStaging::getWPpath());
|
265 |
+
$relativePath = str_replace( $rootPath, null, $file );
|
266 |
+
$destinationPath = $this->destination . $relativePath;
|
267 |
+
$destinationDirectory = dirname( $destinationPath );
|
268 |
+
|
269 |
+
if( !is_dir( $destinationDirectory ) && !@mkdir( $destinationDirectory, wpstg_get_permissions_for_directory(), true ) ) {
|
270 |
+
$this->log( "Files: Can not create directory {$destinationDirectory}", Logger::TYPE_ERROR );
|
271 |
+
return false;
|
272 |
+
}
|
273 |
+
|
274 |
+
return $this->sanitizeDirectorySeparator( $destinationPath );
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Replace relative path of file if its located in multisite upload folder
|
279 |
+
* wp-content/uploads/sites/SITEID or old wordpress structure wp-content/blogs.dir/SITEID/files
|
280 |
+
* @return boolean
|
281 |
+
*/
|
282 |
+
private function getMultisiteUploadFolder( $file ) {
|
283 |
+
// Check first which method is used
|
284 |
+
$uploads = wp_upload_dir();
|
285 |
+
$basedir = $uploads['basedir'];
|
286 |
+
|
287 |
+
if( false === strpos( $basedir, 'blogs.dir' ) ) {
|
288 |
+
// Since WP 3.5
|
289 |
+
$search = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id();
|
290 |
+
$replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
|
291 |
+
$uploadsFolder = str_replace( $search, $replace, $file );
|
292 |
+
} else {
|
293 |
+
// old blog structure
|
294 |
+
$search = 'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files';
|
295 |
+
$replace = 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
|
296 |
+
$uploadsFolder = str_replace( $search, $replace, $file );
|
297 |
+
}
|
298 |
+
|
299 |
+
return $uploadsFolder;
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Copy bigger files than $this->settings->batchSize
|
304 |
+
* @param string $src
|
305 |
+
* @param string $dst
|
306 |
+
* @param int $buffersize
|
307 |
+
* @return boolean
|
308 |
+
*/
|
309 |
+
private function copyBig( $src, $dst, $buffersize ) {
|
310 |
+
$src = fopen( $src, 'r' );
|
311 |
+
$dest = fopen( $dst, 'w' );
|
312 |
+
|
313 |
+
// Try first method:
|
314 |
+
while ( !feof( $src ) ) {
|
315 |
+
if( false === fwrite( $dest, fread( $src, $buffersize ) ) ) {
|
316 |
+
$error = true;
|
317 |
+
}
|
318 |
+
}
|
319 |
+
// Try second method if first one failed
|
320 |
+
if( isset( $error ) && ($error === true) ) {
|
321 |
+
while ( !feof( $src ) ) {
|
322 |
+
if( false === stream_copy_to_stream( $src, $dest, 1024 ) ) {
|
323 |
+
$this->log( "Can not copy file; {$src} -> {$dest}" );
|
324 |
+
fclose( $src );
|
325 |
+
fclose( $dest );
|
326 |
+
return false;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
}
|
330 |
+
// Close any open handler
|
331 |
+
fclose( $src );
|
332 |
+
fclose( $dest );
|
333 |
+
return true;
|
334 |
+
}
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Check if certain file is excluded from copying process
|
338 |
+
*
|
339 |
+
* @param string $file filename including ending without full path
|
340 |
+
* @return boolean
|
341 |
+
*/
|
342 |
+
private function isFileExcluded( $file ) {
|
343 |
+
|
344 |
+
$excludedFiles = ( array ) $this->options->excludedFiles;
|
345 |
+
|
346 |
+
$basenameFile = basename( $file );
|
347 |
+
|
348 |
+
|
349 |
+
// Remove .htaccess and web.config from array excludedFiles if staging site is copied to a subdomain
|
350 |
+
//if( $this->isCustomDirectory() ) {
|
351 |
+
if( false === $this->isIdenticalHostname() ) {
|
352 |
+
$excludedFiles = \array_diff( $excludedFiles, array("web.config", ".htaccess") );
|
353 |
+
}
|
354 |
+
|
355 |
+
|
356 |
+
// If file name exists
|
357 |
+
if( in_array( $basenameFile, $excludedFiles ) ) {
|
358 |
+
return true;
|
359 |
+
}
|
360 |
+
|
361 |
+
// Check for wildcards
|
362 |
+
foreach ($excludedFiles as $pattern){
|
363 |
+
if(fnmatch($pattern, $basenameFile)){
|
364 |
+
return true;
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
// Do not copy wp-config.php if the clone gets updated. This is for security purposes,
|
369 |
+
// because if the updating process fails, the staging site would not be accessable any longer
|
370 |
+
if( isset( $this->options->mainJob ) && $this->options->mainJob == "updating" && stripos( strrev( $file ), strrev( "wp-config.php" ) ) === 0 ) {
|
371 |
+
return true;
|
372 |
+
}
|
373 |
+
|
374 |
+
|
375 |
+
return false;
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Check if custom target directory is used
|
380 |
+
* @return boolean
|
381 |
+
*/
|
382 |
+
// private function isCustomDirectory() {
|
383 |
+
//
|
384 |
+
// if( empty( $this->options->cloneDir ) ) {
|
385 |
+
// return false;
|
386 |
+
// }
|
387 |
+
// return true;
|
388 |
+
// }
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Check if production and staging hostname are identical
|
392 |
+
* If they are not identical we assume website is cloned to a subdomain and not into a subfolder
|
393 |
+
* @return boolean
|
394 |
+
*/
|
395 |
+
private function isIdenticalHostname() {
|
396 |
+
// hostname of production site without scheme
|
397 |
+
$siteurl = get_site_url();
|
398 |
+
$url = parse_url( $siteurl );
|
399 |
+
$productionHostname = $url['host'];
|
400 |
+
|
401 |
+
// hostname of staging site without scheme
|
402 |
+
$cloneUrl = empty( $this->options->cloneHostname ) ? $url : parse_url( $this->options->cloneHostname );
|
403 |
+
$targetHostname = $cloneUrl['host'];
|
404 |
+
|
405 |
+
// Check if target hostname beginns with the production hostname
|
406 |
+
// Only compare the hostname without path
|
407 |
+
if( wpstg_starts_with( $productionHostname, $targetHostname ) ) {
|
408 |
+
return true;
|
409 |
+
}
|
410 |
+
return false;
|
411 |
+
}
|
412 |
+
|
413 |
+
/**
|
414 |
+
* Check if certain file is excluded from copying process
|
415 |
+
*
|
416 |
+
* @param string $file filename including ending + (part) path e.g wp-content/db.php
|
417 |
+
* @return boolean
|
418 |
+
*/
|
419 |
+
private function isFileExcludedFullPath( $file ) {
|
420 |
+
// If path + file exists
|
421 |
+
foreach ( $this->options->excludedFilesFullPath as $excludedFile ) {
|
422 |
+
if( false !== strpos( $file, $excludedFile ) ) {
|
423 |
+
return true;
|
424 |
+
}
|
425 |
+
}
|
426 |
+
|
427 |
+
return false;
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Replace backward slash with forward slash directory separator
|
432 |
+
* Windows Compatibility Fix
|
433 |
+
*
|
434 |
+
* @param string $path Path
|
435 |
+
* @return string
|
436 |
+
*/
|
437 |
+
private function sanitizeDirectorySeparator( $path ) {
|
438 |
+
return preg_replace( '/[\\\\]+/', '/', $path );
|
439 |
+
}
|
440 |
+
|
441 |
+
/**
|
442 |
+
* Check if directory is excluded from copying
|
443 |
+
* @param string $directory
|
444 |
+
* @return bool
|
445 |
+
*/
|
446 |
+
private function isDirectoryExcluded( $directory ) {
|
447 |
+
// Make sure that wp-staging-pro directory / plugin is never excluded
|
448 |
+
if( false !== strpos( $directory, 'wp-staging' ) || false !== strpos( $directory, 'wp-staging-pro' ) ) {
|
449 |
+
return false;
|
450 |
+
}
|
451 |
+
|
452 |
+
$directory = trailingslashit( $this->sanitizeDirectorySeparator( $directory ) );
|
453 |
+
|
454 |
+
foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
|
455 |
+
$excludedDirectory = trailingslashit( $this->sanitizeDirectorySeparator( $excludedDirectory ) );
|
456 |
+
if( strpos( $directory, $excludedDirectory ) === 0 && !$this->isExtraDirectory( $directory ) ) {
|
457 |
+
return true;
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
return false;
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Check if directory is an extra directory and should be copied
|
466 |
+
* @param string $directory
|
467 |
+
* @return boolean
|
468 |
+
*/
|
469 |
+
private function isExtraDirectory( $directory ) {
|
470 |
+
$directory = $this->sanitizeDirectorySeparator( $directory );
|
471 |
+
|
472 |
+
foreach ( $this->options->extraDirectories as $extraDirectory ) {
|
473 |
+
if( strpos( $directory, $this->sanitizeDirectorySeparator( $extraDirectory ) ) === 0 ) {
|
474 |
+
return true;
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
return false;
|
479 |
+
}
|
480 |
+
|
481 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/Finish.php
CHANGED
@@ -1,139 +1,141 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
use WPStaging\WPStaging;
|
6 |
-
use WPStaging\Backend\Modules\Jobs\Job;
|
7 |
-
use WPStaging\Utils\Multisite;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Class Finish
|
11 |
-
* @package WPStaging\Backend\Modules\Jobs
|
12 |
-
*/
|
13 |
-
class Finish extends Job {
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Clone Key
|
17 |
-
* @var string
|
18 |
-
*/
|
19 |
-
private $clone = '';
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Start Module
|
23 |
-
* @return object
|
24 |
-
*/
|
25 |
-
public function start() {
|
26 |
-
// sanitize the clone name before saving
|
27 |
-
$this->clone = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
|
28 |
-
|
29 |
-
// Delete Cache Files
|
30 |
-
$this->deleteCacheFiles();
|
31 |
-
|
32 |
-
// Prepare clone records & save scanned directories for delete job later
|
33 |
-
$this->prepareCloneDataRecords();
|
34 |
-
|
35 |
-
$this->options->isRunning = false;
|
36 |
-
|
37 |
-
$multisite = new Multisite;
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
$return = array(
|
42 |
-
"directoryName" => $this->options->cloneDirectoryName,
|
43 |
-
"path" => $this->options->destinationDir,
|
44 |
-
"url" => $this->getDestinationUrl(),
|
45 |
-
"number" => $this->options->cloneNumber,
|
46 |
-
"version" => \WPStaging\WPStaging::VERSION,
|
47 |
-
"status" => 'finished',
|
48 |
-
"prefix" => $this->options->prefix,
|
49 |
-
"last_msg" => $this->logger->getLastLogMsg(),
|
50 |
-
"job" => $this->options->currentJob,
|
51 |
-
"percentage" => 100
|
52 |
-
);
|
53 |
-
|
54 |
-
//$this->flush();
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
$this->
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
$this->options->existingClones[$this->options->clone]['
|
85 |
-
|
86 |
-
$this->
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
//
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
"
|
101 |
-
"
|
102 |
-
"
|
103 |
-
"
|
104 |
-
"
|
105 |
-
"
|
106 |
-
"
|
107 |
-
"
|
108 |
-
"
|
109 |
-
"
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
return
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
}
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
use WPStaging\WPStaging;
|
6 |
+
use WPStaging\Backend\Modules\Jobs\Job;
|
7 |
+
use WPStaging\Utils\Multisite;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class Finish
|
11 |
+
* @package WPStaging\Backend\Modules\Jobs
|
12 |
+
*/
|
13 |
+
class Finish extends Job {
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Clone Key
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $clone = '';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Start Module
|
23 |
+
* @return object
|
24 |
+
*/
|
25 |
+
public function start() {
|
26 |
+
// sanitize the clone name before saving
|
27 |
+
$this->clone = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
|
28 |
+
|
29 |
+
// Delete Cache Files
|
30 |
+
$this->deleteCacheFiles();
|
31 |
+
|
32 |
+
// Prepare clone records & save scanned directories for delete job later
|
33 |
+
$this->prepareCloneDataRecords();
|
34 |
+
|
35 |
+
$this->options->isRunning = false;
|
36 |
+
|
37 |
+
$multisite = new Multisite;
|
38 |
+
|
39 |
+
|
40 |
+
|
41 |
+
$return = array(
|
42 |
+
"directoryName" => $this->options->cloneDirectoryName,
|
43 |
+
"path" => trailingslashit( $this->options->destinationDir ),
|
44 |
+
"url" => $this->getDestinationUrl(),
|
45 |
+
"number" => $this->options->cloneNumber,
|
46 |
+
"version" => \WPStaging\WPStaging::VERSION,
|
47 |
+
"status" => 'finished',
|
48 |
+
"prefix" => $this->options->prefix,
|
49 |
+
"last_msg" => $this->logger->getLastLogMsg(),
|
50 |
+
"job" => $this->options->currentJob,
|
51 |
+
"percentage" => 100
|
52 |
+
);
|
53 |
+
|
54 |
+
//$this->flush();
|
55 |
+
do_action( 'wpstg_cloning_complete', $this->options );
|
56 |
+
|
57 |
+
|
58 |
+
return ( object ) $return;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Delete Cache Files
|
63 |
+
*/
|
64 |
+
protected function deleteCacheFiles() {
|
65 |
+
$this->log( "Finish: Deleting clone job's cache files..." );
|
66 |
+
|
67 |
+
// Clean cache files
|
68 |
+
$this->cache->delete( "clone_options" );
|
69 |
+
$this->cache->delete( "files_to_copy" );
|
70 |
+
|
71 |
+
$this->log( "Finish: Clone job's cache files have been deleted!" );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Prepare clone records
|
76 |
+
* @return bool
|
77 |
+
*/
|
78 |
+
protected function prepareCloneDataRecords() {
|
79 |
+
// Check if clones still exist
|
80 |
+
$this->log( "Finish: Verifying existing clones..." );
|
81 |
+
|
82 |
+
// Clone data already exists
|
83 |
+
if( isset( $this->options->existingClones[$this->options->clone] ) ) {
|
84 |
+
$this->options->existingClones[$this->options->clone]['datetime'] = time();
|
85 |
+
$this->options->existingClones[$this->options->clone]['url'] = $this->getDestinationUrl();
|
86 |
+
$this->options->existingClones[$this->options->clone]['status'] = 'finished';
|
87 |
+
$this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
|
88 |
+
update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
|
89 |
+
$this->log( "Finish: The job finished!" );
|
90 |
+
return true;
|
91 |
+
}
|
92 |
+
|
93 |
+
// Save new clone data
|
94 |
+
$this->log( "Finish: {$this->options->clone}'s clone job's data is not in database, generating data" );
|
95 |
+
|
96 |
+
// sanitize the clone name before saving
|
97 |
+
//$clone = preg_replace("#\W+#", '-', strtolower($this->options->clone));
|
98 |
+
|
99 |
+
$this->options->existingClones[$this->clone] = array(
|
100 |
+
"directoryName" => $this->options->cloneDirectoryName,
|
101 |
+
"path" => trailingslashit( $this->options->destinationDir ),
|
102 |
+
"url" => $this->getDestinationUrl(),
|
103 |
+
"number" => $this->options->cloneNumber,
|
104 |
+
"version" => \WPStaging\WPStaging::VERSION,
|
105 |
+
"status" => "finished",
|
106 |
+
"prefix" => $this->options->prefix,
|
107 |
+
"datetime" => time(),
|
108 |
+
"databaseUser" => $this->options->databaseUser,
|
109 |
+
"databasePassword" => $this->options->databasePassword,
|
110 |
+
"databaseDatabase" => $this->options->databaseDatabase,
|
111 |
+
"databaseServer" => $this->options->databaseServer,
|
112 |
+
"databasePrefix" => $this->options->databasePrefix,
|
113 |
+
);
|
114 |
+
|
115 |
+
if( false === update_option( "wpstg_existing_clones_beta", $this->options->existingClones ) ) {
|
116 |
+
$this->log( "Finish: Failed to save {$this->options->clone}'s clone job data to database'" );
|
117 |
+
return false;
|
118 |
+
}
|
119 |
+
|
120 |
+
return true;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Get destination Hostname depending on wheather WP has been installed in sub dir or not
|
125 |
+
* @return type
|
126 |
+
*/
|
127 |
+
private function getDestinationUrl() {
|
128 |
+
|
129 |
+
if( !empty( $this->options->cloneHostname ) ) {
|
130 |
+
return $this->options->cloneHostname;
|
131 |
+
}
|
132 |
+
|
133 |
+
//return trailingslashit( $this->multisiteHomeDomain ) . $this->options->cloneDirectoryName;
|
134 |
+
// Get the path to the main multisite without appending and trailingslash e.g. wordpress
|
135 |
+
$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : '/';
|
136 |
+
return rtrim( $this->multisiteHomeDomain, '/\\' ) . $multisitePath . $this->options->cloneDirectoryName;
|
137 |
+
//$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? str_replace( '/', '', PATH_CURRENT_SITE ) : '';
|
138 |
+
//return trailingslashit( $this->multisiteHomeDomain ) . $multisitePath . '/' . $this->options->cloneDirectoryName;
|
139 |
+
}
|
140 |
+
|
141 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/SearchReplace.php
CHANGED
@@ -1,816 +1,839 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Strings;
|
12 |
-
use WPStaging\Utils\Helper;
|
13 |
-
use WPStaging\Utils\Multisite;
|
14 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Class Database
|
18 |
-
* @package WPStaging\Backend\Modules\Jobs
|
19 |
-
*/
|
20 |
-
class SearchReplace extends JobExecutable {
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @var int
|
24 |
-
*/
|
25 |
-
private $total = 0;
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @var \WPDB
|
29 |
-
*/
|
30 |
-
public $db;
|
31 |
-
|
32 |
-
/**
|
33 |
-
*
|
34 |
-
* @var Obj
|
35 |
-
*/
|
36 |
-
private $strings;
|
37 |
-
|
38 |
-
/**
|
39 |
-
*
|
40 |
-
* @var string
|
41 |
-
*/
|
42 |
-
private $destinationHostname;
|
43 |
-
|
44 |
-
/**
|
45 |
-
*
|
46 |
-
* @var string
|
47 |
-
*/
|
48 |
-
private $sourceHostname;
|
49 |
-
|
50 |
-
/**
|
51 |
-
*
|
52 |
-
* @var string
|
53 |
-
*/
|
54 |
-
//private $targetDir;
|
55 |
-
|
56 |
-
/**
|
57 |
-
* The prefix of the new database tables which are used for the live site after updating tables
|
58 |
-
* @var string
|
59 |
-
*/
|
60 |
-
public $tmpPrefix;
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Initialize
|
64 |
-
*/
|
65 |
-
public function initialize() {
|
66 |
-
$this->total = count( $this->options->tables );
|
67 |
-
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
68 |
-
$this->tmpPrefix = $this->options->prefix;
|
69 |
-
$this->strings = new Strings();
|
70 |
-
$this->sourceHostname = $this->getSourceHostname();
|
71 |
-
$this->destinationHostname = $this->getDestinationHostname();
|
72 |
-
}
|
73 |
-
|
74 |
-
public function start() {
|
75 |
-
// Skip job. Nothing to do
|
76 |
-
if( $this->options->totalSteps === 0 ) {
|
77 |
-
$this->prepareResponse( true, false );
|
78 |
-
}
|
79 |
-
|
80 |
-
$this->run();
|
81 |
-
|
82 |
-
// Save option, progress
|
83 |
-
$this->saveOptions();
|
84 |
-
|
85 |
-
return ( object ) $this->response;
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
90 |
-
* @return void
|
91 |
-
*/
|
92 |
-
protected function calculateTotalSteps() {
|
93 |
-
$this->options->totalSteps = $this->total;
|
94 |
-
}
|
95 |
-
|
96 |
-
/**
|
97 |
-
* Execute the Current Step
|
98 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
99 |
-
* @return bool
|
100 |
-
*/
|
101 |
-
protected function execute() {
|
102 |
-
// Over limits threshold
|
103 |
-
if( $this->isOverThreshold() ) {
|
104 |
-
// Prepare response and save current progress
|
105 |
-
$this->prepareResponse( false, false );
|
106 |
-
$this->saveOptions();
|
107 |
-
return false;
|
108 |
-
}
|
109 |
-
|
110 |
-
// No more steps, finished
|
111 |
-
if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
|
112 |
-
$this->prepareResponse( true, false );
|
113 |
-
return false;
|
114 |
-
}
|
115 |
-
|
116 |
-
// Table is excluded
|
117 |
-
if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
|
118 |
-
$this->prepareResponse();
|
119 |
-
return true;
|
120 |
-
}
|
121 |
-
|
122 |
-
// Search & Replace
|
123 |
-
if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
|
124 |
-
// Prepare Response
|
125 |
-
$this->prepareResponse( false, false );
|
126 |
-
|
127 |
-
// Not finished
|
128 |
-
return true;
|
129 |
-
}
|
130 |
-
|
131 |
-
|
132 |
-
// Prepare Response
|
133 |
-
$this->prepareResponse();
|
134 |
-
|
135 |
-
// Not finished
|
136 |
-
return true;
|
137 |
-
}
|
138 |
-
|
139 |
-
/**
|
140 |
-
* Stop Execution immediately
|
141 |
-
* return mixed bool | json
|
142 |
-
*/
|
143 |
-
private function stopExecution() {
|
144 |
-
if( $this->db->prefix == $this->tmpPrefix ) {
|
145 |
-
$this->returnException( 'Fatal Error 9: Prefix ' . $this->db->prefix . ' is used for the live site hence it can not be used for the staging site as well. Please ask support@wp-staging.com how to resolve this.' );
|
146 |
-
}
|
147 |
-
return false;
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Copy Tables
|
152 |
-
* @param string $tableName
|
153 |
-
* @return bool
|
154 |
-
*/
|
155 |
-
private function updateTable( $tableName ) {
|
156 |
-
$strings = new Strings();
|
157 |
-
$table = $strings->str_replace_first( $this->db->prefix, '', $tableName );
|
158 |
-
$newTableName = $this->tmpPrefix . $table;
|
159 |
-
|
160 |
-
// Save current job
|
161 |
-
$this->setJob( $newTableName );
|
162 |
-
|
163 |
-
// Beginning of the job
|
164 |
-
if( !$this->startJob( $newTableName, $tableName ) ) {
|
165 |
-
return true;
|
166 |
-
}
|
167 |
-
// Copy data
|
168 |
-
$this->startReplace( $newTableName );
|
169 |
-
|
170 |
-
// Finish the step
|
171 |
-
return $this->finishStep();
|
172 |
-
}
|
173 |
-
|
174 |
-
/**
|
175 |
-
* Get source Hostname depending on wheather WP has been installed in sub dir or not
|
176 |
-
* @return type
|
177 |
-
*/
|
178 |
-
public function getSourceHostname() {
|
179 |
-
|
180 |
-
if( $this->isSubDir() ) {
|
181 |
-
return trailingslashit( $this->multisiteHomeUrlWithoutScheme ) . '/' . $this->getSubDir();
|
182 |
-
}
|
183 |
-
return $this->multisiteHomeUrlWithoutScheme;
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* Get destination Hostname depending on wheather WP has been installed in sub dir or not
|
188 |
-
* @return type
|
189 |
-
*/
|
190 |
-
public function getDestinationHostname() {
|
191 |
-
|
192 |
-
if( !empty( $this->options->cloneHostname ) ) {
|
193 |
-
return $this->strings->getUrlWithoutScheme( $this->options->cloneHostname );
|
194 |
-
}
|
195 |
-
|
196 |
-
if( $this->isSubDir() ) {
|
197 |
-
return trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName;
|
198 |
-
}
|
199 |
-
|
200 |
-
// Get the path to the main multisite without appending and trailingslash e.g. wordpress
|
201 |
-
$multisitePath = defined( 'PATH_CURRENT_SITE') ? PATH_CURRENT_SITE : '/';
|
202 |
-
$url
|
203 |
-
//$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? str_replace( '/', '', PATH_CURRENT_SITE ) : '';
|
204 |
-
//$url = trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $multisitePath . '/' . $this->options->cloneDirectoryName;
|
205 |
-
return $url;
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* Get the install sub directory if WP is installed in sub directory
|
210 |
-
* @return string
|
211 |
-
*/
|
212 |
-
private function getSubDir() {
|
213 |
-
$home = get_option( 'home' );
|
214 |
-
$siteurl = get_option( 'siteurl' );
|
215 |
-
|
216 |
-
if( empty( $home ) || empty( $siteurl ) ) {
|
217 |
-
return '';
|
218 |
-
}
|
219 |
-
|
220 |
-
$dir = str_replace( $home, '', $siteurl );
|
221 |
-
return str_replace( '/', '', $dir );
|
222 |
-
}
|
223 |
-
|
224 |
-
/**
|
225 |
-
* Start search replace job
|
226 |
-
* @param string $new
|
227 |
-
* @param string $old
|
228 |
-
*/
|
229 |
-
private function startReplace( $table ) {
|
230 |
-
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
231 |
-
$this->log(
|
232 |
-
"DB
|
233 |
-
);
|
234 |
-
|
235 |
-
// Search & Replace
|
236 |
-
$this->searchReplace( $table, $rows, array() );
|
237 |
-
|
238 |
-
// Set new offset
|
239 |
-
$this->options->job->start += $this->settings->querySRLimit;
|
240 |
-
}
|
241 |
-
|
242 |
-
/**
|
243 |
-
* Returns the number of pages in a table.
|
244 |
-
* @access public
|
245 |
-
* @return int
|
246 |
-
*/
|
247 |
-
private function get_pages_in_table( $table ) {
|
248 |
-
|
249 |
-
// Table does not exist
|
250 |
-
$result = $this->db->query( "SHOW TABLES LIKE '{$table}'" );
|
251 |
-
if( !$result || 0 === $result ) {
|
252 |
-
return 0;
|
253 |
-
}
|
254 |
-
|
255 |
-
$table = esc_sql( $table );
|
256 |
-
$rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
|
257 |
-
$pages = ceil( $rows / $this->settings->querySRLimit );
|
258 |
-
return absint( $pages );
|
259 |
-
}
|
260 |
-
|
261 |
-
/**
|
262 |
-
* Gets the columns in a table.
|
263 |
-
* @access public
|
264 |
-
* @param string $table The table to check.
|
265 |
-
* @return array
|
266 |
-
*/
|
267 |
-
private function get_columns( $table ) {
|
268 |
-
$primary_key = null;
|
269 |
-
$columns = array();
|
270 |
-
$fields = $this->db->get_results( 'DESCRIBE ' . $table );
|
271 |
-
if( is_array( $fields ) ) {
|
272 |
-
foreach ( $fields as $column ) {
|
273 |
-
$columns[] = $column->Field;
|
274 |
-
if( $column->Key == 'PRI' ) {
|
275 |
-
$primary_key = $column->Field;
|
276 |
-
}
|
277 |
-
}
|
278 |
-
}
|
279 |
-
return array($primary_key, $columns);
|
280 |
-
}
|
281 |
-
|
282 |
-
/**
|
283 |
-
* Return absolute destination path
|
284 |
-
* @return string
|
285 |
-
*/
|
286 |
-
private function getAbsDestination() {
|
287 |
-
if( empty( $this->options->cloneDir ) ) {
|
288 |
-
return \WPStaging\WPStaging::getWPpath();
|
289 |
-
}
|
290 |
-
return trailingslashit( $this->options->cloneDir );
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
295 |
-
*
|
296 |
-
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
297 |
-
* and to be compatible with batch processing.
|
298 |
-
*
|
299 |
-
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
300 |
-
*
|
301 |
-
* @access public
|
302 |
-
* @param string $table The table to run the replacement on.
|
303 |
-
* @param int $page The page/block to begin the query on.
|
304 |
-
* @param array $args An associative array containing arguments for this run.
|
305 |
-
* @return array
|
306 |
-
*/
|
307 |
-
private function searchReplace( $table, $page, $args ) {
|
308 |
-
|
309 |
-
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
310 |
-
$this->log( "DB
|
311 |
-
return true;
|
312 |
-
}
|
313 |
-
|
314 |
-
// Load up the default settings for this chunk.
|
315 |
-
$table
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
'
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
'
|
332 |
-
$this->
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
);
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
$args['
|
344 |
-
|
345 |
-
$args['
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
//
|
357 |
-
//
|
358 |
-
//
|
359 |
-
//
|
360 |
-
//
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
$
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
'
|
374 |
-
'
|
375 |
-
'
|
376 |
-
'
|
377 |
-
'
|
378 |
-
'
|
379 |
-
'
|
380 |
-
|
381 |
-
|
382 |
-
);
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
$
|
389 |
-
$
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
//
|
431 |
-
//
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
//
|
438 |
-
//
|
439 |
-
//
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
'
|
444 |
-
'
|
445 |
-
'
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
//
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
unset( $
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
//
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
unset( $
|
500 |
-
unset( $
|
501 |
-
unset( $
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
*
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
$
|
517 |
-
$
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
'wp-content' . DIRECTORY_SEPARATOR . '
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
*
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
*
|
544 |
-
*
|
545 |
-
*
|
546 |
-
*
|
547 |
-
*
|
548 |
-
*
|
549 |
-
*
|
550 |
-
* @
|
551 |
-
* @param string $
|
552 |
-
* @param
|
553 |
-
* @param
|
554 |
-
* @param boolean
|
555 |
-
*
|
556 |
-
*
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
$
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
}
|
587 |
-
$
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
} else {
|
594 |
-
if( is_string( $data ) ) {
|
595 |
-
if( !empty( $from ) && !empty( $to ) ) {
|
596 |
-
$data = $this->str_replace( $from, $to, $data, $case_insensitive );
|
597 |
-
}
|
598 |
-
}
|
599 |
-
}
|
600 |
-
|
601 |
-
if( $serialized ) {
|
602 |
-
return serialize( $data );
|
603 |
-
}
|
604 |
-
} catch ( Exception $error ) {
|
605 |
-
|
606 |
-
}
|
607 |
-
|
608 |
-
return $data;
|
609 |
-
}
|
610 |
-
|
611 |
-
/**
|
612 |
-
* Check if the object is a valid one and not __PHP_Incomplete_Class_Name
|
613 |
-
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
614 |
-
* @return boolean
|
615 |
-
*/
|
616 |
-
// private function isValidObject( $data ) {
|
617 |
-
// if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
618 |
-
// return false;
|
619 |
-
// }
|
620 |
-
//
|
621 |
-
// $invalid_class_props = get_object_vars( $data );
|
622 |
-
//
|
623 |
-
// if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
624 |
-
// // Assume it must be an valid object
|
625 |
-
// return true;
|
626 |
-
// }
|
627 |
-
//
|
628 |
-
// $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
629 |
-
//
|
630 |
-
// if( !empty( $invalid_object_class ) ) {
|
631 |
-
// return false;
|
632 |
-
// }
|
633 |
-
//
|
634 |
-
// // Assume it must be an valid object
|
635 |
-
// return true;
|
636 |
-
// }
|
637 |
-
|
638 |
-
/**
|
639 |
-
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
640 |
-
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
|
641 |
-
* @access public
|
642 |
-
* @param string $input The string to escape.
|
643 |
-
* @return string
|
644 |
-
*/
|
645 |
-
private function mysql_escape_mimic( $input ) {
|
646 |
-
if( is_array( $input ) ) {
|
647 |
-
return array_map( __METHOD__, $input );
|
648 |
-
}
|
649 |
-
if( !empty( $input ) && is_string( $input ) ) {
|
650 |
-
return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
|
651 |
-
}
|
652 |
-
|
653 |
-
return $input;
|
654 |
-
}
|
655 |
-
|
656 |
-
/**
|
657 |
-
* Return unserialized object or array
|
658 |
-
*
|
659 |
-
* @param string $serialized_string Serialized string.
|
660 |
-
* @param string $method The name of the caller method.
|
661 |
-
*
|
662 |
-
* @return mixed, false on failure
|
663 |
-
*/
|
664 |
-
private static function unserialize( $serialized_string ) {
|
665 |
-
if( !is_serialized( $serialized_string ) ) {
|
666 |
-
return false;
|
667 |
-
}
|
668 |
-
|
669 |
-
$serialized_string = trim( $serialized_string );
|
670 |
-
$unserialized_string = @unserialize( $serialized_string );
|
671 |
-
|
672 |
-
return $unserialized_string;
|
673 |
-
}
|
674 |
-
|
675 |
-
/**
|
676 |
-
* Wrapper for str_replace
|
677 |
-
*
|
678 |
-
* @param string $from
|
679 |
-
* @param string $to
|
680 |
-
* @param string $data
|
681 |
-
* @param string|bool $case_insensitive
|
682 |
-
*
|
683 |
-
* @return string
|
684 |
-
*/
|
685 |
-
private function str_replace( $from, $to, $data, $case_insensitive = false ) {
|
686 |
-
|
687 |
-
// Add filter
|
688 |
-
$excludes = apply_filters( 'wpstg_clone_searchreplace_excl', array() );
|
689 |
-
|
690 |
-
// Build pattern
|
691 |
-
$regexExclude = '';
|
692 |
-
foreach ( $excludes as $exclude ) {
|
693 |
-
$regexExclude .= $exclude . '(*SKIP)(FAIL)|';
|
694 |
-
}
|
695 |
-
|
696 |
-
if( 'on' === $case_insensitive ) {
|
697 |
-
//$data = str_ireplace( $from, $to, $data );
|
698 |
-
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#i', $to, $data );
|
699 |
-
} else {
|
700 |
-
//$data = str_replace( $from, $to, $data );
|
701 |
-
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#', $to, $data );
|
702 |
-
}
|
703 |
-
|
704 |
-
return $data;
|
705 |
-
}
|
706 |
-
|
707 |
-
/**
|
708 |
-
* Set the job
|
709 |
-
* @param string $table
|
710 |
-
*/
|
711 |
-
private function setJob( $table ) {
|
712 |
-
if( !empty( $this->options->job->current ) ) {
|
713 |
-
return;
|
714 |
-
}
|
715 |
-
|
716 |
-
$this->options->job->current = $table;
|
717 |
-
$this->options->job->start = 0;
|
718 |
-
}
|
719 |
-
|
720 |
-
/**
|
721 |
-
* Start Job
|
722 |
-
* @param string $new
|
723 |
-
* @param string $old
|
724 |
-
* @return bool
|
725 |
-
*/
|
726 |
-
private function startJob( $new, $old ) {
|
727 |
-
|
728 |
-
$this->options->job->total = 0;
|
729 |
-
|
730 |
-
if( 0 != $this->options->job->start ) {
|
731 |
-
return true;
|
732 |
-
}
|
733 |
-
|
734 |
-
// Table does not exist
|
735 |
-
$result = $this->db->query( "SHOW TABLES LIKE '{$old}'" );
|
736 |
-
if( !$result || 0 === $result ) {
|
737 |
-
return false;
|
738 |
-
}
|
739 |
-
|
740 |
-
$this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
|
741 |
-
|
742 |
-
if( 0 == $this->options->job->total ) {
|
743 |
-
$this->finishStep();
|
744 |
-
return false;
|
745 |
-
}
|
746 |
-
|
747 |
-
return true;
|
748 |
-
}
|
749 |
-
|
750 |
-
/**
|
751 |
-
*
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
$
|
761 |
-
|
762 |
-
|
763 |
-
$
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
return
|
814 |
-
|
815 |
-
|
816 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Strings;
|
12 |
+
use WPStaging\Utils\Helper;
|
13 |
+
use WPStaging\Utils\Multisite;
|
14 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Class Database
|
18 |
+
* @package WPStaging\Backend\Modules\Jobs
|
19 |
+
*/
|
20 |
+
class SearchReplace extends JobExecutable {
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var int
|
24 |
+
*/
|
25 |
+
private $total = 0;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var \WPDB
|
29 |
+
*/
|
30 |
+
public $db;
|
31 |
+
|
32 |
+
/**
|
33 |
+
*
|
34 |
+
* @var Obj
|
35 |
+
*/
|
36 |
+
private $strings;
|
37 |
+
|
38 |
+
/**
|
39 |
+
*
|
40 |
+
* @var string
|
41 |
+
*/
|
42 |
+
private $destinationHostname;
|
43 |
+
|
44 |
+
/**
|
45 |
+
*
|
46 |
+
* @var string
|
47 |
+
*/
|
48 |
+
private $sourceHostname;
|
49 |
+
|
50 |
+
/**
|
51 |
+
*
|
52 |
+
* @var string
|
53 |
+
*/
|
54 |
+
//private $targetDir;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* The prefix of the new database tables which are used for the live site after updating tables
|
58 |
+
* @var string
|
59 |
+
*/
|
60 |
+
public $tmpPrefix;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Initialize
|
64 |
+
*/
|
65 |
+
public function initialize() {
|
66 |
+
$this->total = count( $this->options->tables );
|
67 |
+
$this->db = WPStaging::getInstance()->get( "wpdb" );
|
68 |
+
$this->tmpPrefix = $this->options->prefix;
|
69 |
+
$this->strings = new Strings();
|
70 |
+
$this->sourceHostname = $this->getSourceHostname();
|
71 |
+
$this->destinationHostname = $this->getDestinationHostname();
|
72 |
+
}
|
73 |
+
|
74 |
+
public function start() {
|
75 |
+
// Skip job. Nothing to do
|
76 |
+
if( $this->options->totalSteps === 0 ) {
|
77 |
+
$this->prepareResponse( true, false );
|
78 |
+
}
|
79 |
+
|
80 |
+
$this->run();
|
81 |
+
|
82 |
+
// Save option, progress
|
83 |
+
$this->saveOptions();
|
84 |
+
|
85 |
+
return ( object ) $this->response;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
90 |
+
* @return void
|
91 |
+
*/
|
92 |
+
protected function calculateTotalSteps() {
|
93 |
+
$this->options->totalSteps = $this->total;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Execute the Current Step
|
98 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
99 |
+
* @return bool
|
100 |
+
*/
|
101 |
+
protected function execute() {
|
102 |
+
// Over limits threshold
|
103 |
+
if( $this->isOverThreshold() ) {
|
104 |
+
// Prepare response and save current progress
|
105 |
+
$this->prepareResponse( false, false );
|
106 |
+
$this->saveOptions();
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
|
110 |
+
// No more steps, finished
|
111 |
+
if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
|
112 |
+
$this->prepareResponse( true, false );
|
113 |
+
return false;
|
114 |
+
}
|
115 |
+
|
116 |
+
// Table is excluded
|
117 |
+
if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
|
118 |
+
$this->prepareResponse();
|
119 |
+
return true;
|
120 |
+
}
|
121 |
+
|
122 |
+
// Search & Replace
|
123 |
+
if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
|
124 |
+
// Prepare Response
|
125 |
+
$this->prepareResponse( false, false );
|
126 |
+
|
127 |
+
// Not finished
|
128 |
+
return true;
|
129 |
+
}
|
130 |
+
|
131 |
+
|
132 |
+
// Prepare Response
|
133 |
+
$this->prepareResponse();
|
134 |
+
|
135 |
+
// Not finished
|
136 |
+
return true;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Stop Execution immediately
|
141 |
+
* return mixed bool | json
|
142 |
+
*/
|
143 |
+
private function stopExecution() {
|
144 |
+
if( $this->db->prefix == $this->tmpPrefix ) {
|
145 |
+
$this->returnException( 'Fatal Error 9: Prefix ' . $this->db->prefix . ' is used for the live site hence it can not be used for the staging site as well. Please ask support@wp-staging.com how to resolve this.' );
|
146 |
+
}
|
147 |
+
return false;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Copy Tables
|
152 |
+
* @param string $tableName
|
153 |
+
* @return bool
|
154 |
+
*/
|
155 |
+
private function updateTable( $tableName ) {
|
156 |
+
$strings = new Strings();
|
157 |
+
$table = $strings->str_replace_first( $this->db->prefix, '', $tableName );
|
158 |
+
$newTableName = $this->tmpPrefix . $table;
|
159 |
+
|
160 |
+
// Save current job
|
161 |
+
$this->setJob( $newTableName );
|
162 |
+
|
163 |
+
// Beginning of the job
|
164 |
+
if( !$this->startJob( $newTableName, $tableName ) ) {
|
165 |
+
return true;
|
166 |
+
}
|
167 |
+
// Copy data
|
168 |
+
$this->startReplace( $newTableName );
|
169 |
+
|
170 |
+
// Finish the step
|
171 |
+
return $this->finishStep();
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Get source Hostname depending on wheather WP has been installed in sub dir or not
|
176 |
+
* @return type
|
177 |
+
*/
|
178 |
+
public function getSourceHostname() {
|
179 |
+
|
180 |
+
if( $this->isSubDir() ) {
|
181 |
+
return trailingslashit( $this->multisiteHomeUrlWithoutScheme ) . '/' . $this->getSubDir();
|
182 |
+
}
|
183 |
+
return $this->multisiteHomeUrlWithoutScheme;
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Get destination Hostname depending on wheather WP has been installed in sub dir or not
|
188 |
+
* @return type
|
189 |
+
*/
|
190 |
+
public function getDestinationHostname() {
|
191 |
+
|
192 |
+
if( !empty( $this->options->cloneHostname ) ) {
|
193 |
+
return $this->strings->getUrlWithoutScheme( $this->options->cloneHostname );
|
194 |
+
}
|
195 |
+
|
196 |
+
if( $this->isSubDir() ) {
|
197 |
+
return trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName;
|
198 |
+
}
|
199 |
+
|
200 |
+
// Get the path to the main multisite without appending and trailingslash e.g. wordpress
|
201 |
+
$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? PATH_CURRENT_SITE : '/';
|
202 |
+
$url = rtrim( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ), '/\\' ) . $multisitePath . $this->options->cloneDirectoryName;
|
203 |
+
//$multisitePath = defined( 'PATH_CURRENT_SITE' ) ? str_replace( '/', '', PATH_CURRENT_SITE ) : '';
|
204 |
+
//$url = trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $multisitePath . '/' . $this->options->cloneDirectoryName;
|
205 |
+
return $url;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Get the install sub directory if WP is installed in sub directory
|
210 |
+
* @return string
|
211 |
+
*/
|
212 |
+
private function getSubDir() {
|
213 |
+
$home = get_option( 'home' );
|
214 |
+
$siteurl = get_option( 'siteurl' );
|
215 |
+
|
216 |
+
if( empty( $home ) || empty( $siteurl ) ) {
|
217 |
+
return '';
|
218 |
+
}
|
219 |
+
|
220 |
+
$dir = str_replace( $home, '', $siteurl );
|
221 |
+
return str_replace( '/', '', $dir );
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Start search replace job
|
226 |
+
* @param string $new
|
227 |
+
* @param string $old
|
228 |
+
*/
|
229 |
+
private function startReplace( $table ) {
|
230 |
+
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
231 |
+
$this->log(
|
232 |
+
"DB Search & Replace: Table {$table} {$this->options->job->start} to {$rows} records"
|
233 |
+
);
|
234 |
+
|
235 |
+
// Search & Replace
|
236 |
+
$this->searchReplace( $table, $rows, array() );
|
237 |
+
|
238 |
+
// Set new offset
|
239 |
+
$this->options->job->start += $this->settings->querySRLimit;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Returns the number of pages in a table.
|
244 |
+
* @access public
|
245 |
+
* @return int
|
246 |
+
*/
|
247 |
+
// private function get_pages_in_table( $table ) {
|
248 |
+
//
|
249 |
+
// // Table does not exist
|
250 |
+
// $result = $this->db->query( "SHOW TABLES LIKE '{$table}'" );
|
251 |
+
// if( !$result || 0 === $result ) {
|
252 |
+
// return 0;
|
253 |
+
// }
|
254 |
+
//
|
255 |
+
// $table = esc_sql( $table );
|
256 |
+
// $rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
|
257 |
+
// $pages = ceil( $rows / $this->settings->querySRLimit );
|
258 |
+
// return absint( $pages );
|
259 |
+
// }
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Gets the columns in a table.
|
263 |
+
* @access public
|
264 |
+
* @param string $table The table to check.
|
265 |
+
* @return array
|
266 |
+
*/
|
267 |
+
private function get_columns( $table ) {
|
268 |
+
$primary_key = null;
|
269 |
+
$columns = array();
|
270 |
+
$fields = $this->db->get_results( 'DESCRIBE ' . $table );
|
271 |
+
if( is_array( $fields ) ) {
|
272 |
+
foreach ( $fields as $column ) {
|
273 |
+
$columns[] = $column->Field;
|
274 |
+
if( $column->Key == 'PRI' ) {
|
275 |
+
$primary_key = $column->Field;
|
276 |
+
}
|
277 |
+
}
|
278 |
+
}
|
279 |
+
return array($primary_key, $columns);
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Return absolute destination path
|
284 |
+
* @return string
|
285 |
+
*/
|
286 |
+
private function getAbsDestination() {
|
287 |
+
if( empty( $this->options->cloneDir ) ) {
|
288 |
+
return \WPStaging\WPStaging::getWPpath();
|
289 |
+
}
|
290 |
+
return trailingslashit( $this->options->cloneDir );
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
295 |
+
*
|
296 |
+
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
297 |
+
* and to be compatible with batch processing.
|
298 |
+
*
|
299 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
300 |
+
*
|
301 |
+
* @access public
|
302 |
+
* @param string $table The table to run the replacement on.
|
303 |
+
* @param int $page The page/block to begin the query on.
|
304 |
+
* @param array $args An associative array containing arguments for this run.
|
305 |
+
* @return array
|
306 |
+
*/
|
307 |
+
private function searchReplace( $table, $page, $args ) {
|
308 |
+
|
309 |
+
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
310 |
+
$this->log( "DB Search & Replace: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
|
311 |
+
return true;
|
312 |
+
}
|
313 |
+
|
314 |
+
// Load up the default settings for this chunk.
|
315 |
+
$table = esc_sql( $table );
|
316 |
+
//$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
317 |
+
//$pages = $this->get_pages_in_table( $table );
|
318 |
+
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
319 |
+
$args['search_for'] = array(
|
320 |
+
'//' . $this->getSourceHostname(),
|
321 |
+
ABSPATH,
|
322 |
+
'\/\/' . str_replace( '/', '\/', $this->getSourceHostname() ), // Used by revslider and several visual editors
|
323 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->getSourceHostname() ), // HTML entitity for WP Backery Page Builder Plugin
|
324 |
+
//$this->getImagePathLive()
|
325 |
+
);
|
326 |
+
|
327 |
+
|
328 |
+
$args['replace_with'] = array(
|
329 |
+
'//' . $this->getDestinationHostname(),
|
330 |
+
$this->options->destinationDir,
|
331 |
+
'\/\/' . str_replace( '/', '\/', $this->getDestinationHostname() ), // Used by revslider and several visual editors
|
332 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->getDestinationHostname() ), // HTML entitity for WP Backery Page Builder Plugin
|
333 |
+
//$this->getImagePathStaging()
|
334 |
+
);
|
335 |
+
|
336 |
+
$this->debugLog( "DB Search & Replace: Search: {$args['search_for'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
337 |
+
$this->debugLog( "DB Search & Replace: Replace: {$args['replace_with'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
338 |
+
|
339 |
+
|
340 |
+
|
341 |
+
$args['replace_guids'] = 'off';
|
342 |
+
$args['dry_run'] = 'off';
|
343 |
+
$args['case_insensitive'] = false;
|
344 |
+
//$args['replace_mails'] = 'off';
|
345 |
+
$args['skip_transients'] = 'on';
|
346 |
+
|
347 |
+
|
348 |
+
// Allow filtering of search & replace parameters
|
349 |
+
$args = apply_filters( 'wpstg_clone_searchreplace_params', $args );
|
350 |
+
|
351 |
+
// Get a list of columns in this table.
|
352 |
+
list( $primary_key, $columns ) = $this->get_columns( $table );
|
353 |
+
|
354 |
+
// Bail out early if there isn't a primary key.
|
355 |
+
// We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
|
356 |
+
// @todo test this carefully. If it causes (performance) issues we need to activate it again!
|
357 |
+
// @since 2.4.4
|
358 |
+
// if( null === $primary_key ) {
|
359 |
+
// return false;
|
360 |
+
// }
|
361 |
+
|
362 |
+
$current_row = 0;
|
363 |
+
$start = $this->options->job->start;
|
364 |
+
$end = $this->settings->querySRLimit;
|
365 |
+
|
366 |
+
// Grab the content of the table.
|
367 |
+
$data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
368 |
+
|
369 |
+
// Filter certain rows option_name in wpstg_options
|
370 |
+
$filter = array(
|
371 |
+
'Admin_custome_login_Slidshow',
|
372 |
+
'Admin_custome_login_Social',
|
373 |
+
'Admin_custome_login_logo',
|
374 |
+
'Admin_custome_login_text',
|
375 |
+
'Admin_custome_login_login',
|
376 |
+
'Admin_custome_login_top',
|
377 |
+
'Admin_custome_login_dashboard',
|
378 |
+
'Admin_custome_login_Version',
|
379 |
+
'upload_path'
|
380 |
+
);
|
381 |
+
|
382 |
+
$filter = apply_filters( 'wpstg_clone_searchreplace_excl_rows', $filter );
|
383 |
+
|
384 |
+
// Loop through the data.
|
385 |
+
foreach ( $data as $row ) {
|
386 |
+
$current_row++;
|
387 |
+
$update_sql = array();
|
388 |
+
$where_sql = array();
|
389 |
+
$upd = false;
|
390 |
+
|
391 |
+
// Skip rows below
|
392 |
+
if( isset( $row['option_name'] ) && in_array( $row['option_name'], $filter ) ) {
|
393 |
+
continue;
|
394 |
+
}
|
395 |
+
|
396 |
+
// Skip rows with transients (They can store huge data and we need to save memory)
|
397 |
+
if( isset( $row['option_name'] ) && 'on' === $args['skip_transients'] && false !== strpos( $row['option_name'], '_transient' ) ) {
|
398 |
+
continue;
|
399 |
+
}
|
400 |
+
// Skip rows with more than 5MB to save memory
|
401 |
+
if( isset( $row['option_value'] ) && strlen( $row['option_value'] ) >= 5000000 ) {
|
402 |
+
continue;
|
403 |
+
}
|
404 |
+
|
405 |
+
|
406 |
+
foreach ( $columns as $column ) {
|
407 |
+
|
408 |
+
$dataRow = $row[$column];
|
409 |
+
|
410 |
+
// Skip rows larger than 5MB
|
411 |
+
$size = strlen( $dataRow );
|
412 |
+
if( $size >= 5000000 ) {
|
413 |
+
continue;
|
414 |
+
}
|
415 |
+
|
416 |
+
// Skip Primary key
|
417 |
+
if( $column == $primary_key ) {
|
418 |
+
$where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
419 |
+
continue;
|
420 |
+
}
|
421 |
+
|
422 |
+
// Skip GUIDs by default.
|
423 |
+
if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
|
424 |
+
continue;
|
425 |
+
}
|
426 |
+
|
427 |
+
// Skip mail addresses
|
428 |
+
// if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->multisiteDomainWithoutScheme ) ) {
|
429 |
+
// continue;
|
430 |
+
// }
|
431 |
+
// Check options table
|
432 |
+
if( $this->options->prefix . 'options' === $table ) {
|
433 |
+
|
434 |
+
// Skip certain options
|
435 |
+
// if( isset( $should_skip ) && true === $should_skip ) {
|
436 |
+
// $should_skip = false;
|
437 |
+
// continue;
|
438 |
+
// }
|
439 |
+
// Skip this row
|
440 |
+
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
441 |
+
'wpstg_existing_clones' === $dataRow ||
|
442 |
+
'wpstg_settings' === $dataRow ||
|
443 |
+
'wpstg_license_status' === $dataRow ||
|
444 |
+
'siteurl' === $dataRow ||
|
445 |
+
'home' === $dataRow
|
446 |
+
) {
|
447 |
+
//$should_skip = true;
|
448 |
+
continue;
|
449 |
+
}
|
450 |
+
}
|
451 |
+
|
452 |
+
// Check the path delimiter for / or \/ and remove one of those which prevents from resulting in wrong syntax like domain.com/staging\/.
|
453 |
+
// 1. local.wordpress.test -> local.wordpress.test/staging
|
454 |
+
// 2. local.wordpress.test\/ -> local.wordpress.test\/staging\/
|
455 |
+
$tmp = $args;
|
456 |
+
if( false === strpos( $dataRow, $tmp['search_for'][0] ) ) {
|
457 |
+
array_shift( $tmp['search_for'] ); // rtrim( $this->homeUrl, '/' ),
|
458 |
+
array_shift( $tmp['replace_with'] ); // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
|
459 |
+
} else {
|
460 |
+
unset( $tmp['search_for'][1] );
|
461 |
+
unset( $tmp['replace_with'][1] );
|
462 |
+
// recount array
|
463 |
+
$tmp['search_for'] = array_values( $tmp['search_for'] );
|
464 |
+
$tmp['replace_with'] = array_values( $tmp['replace_with'] );
|
465 |
+
}
|
466 |
+
|
467 |
+
// Run a search replace on the data row and respect the serialisation.
|
468 |
+
$i = 0;
|
469 |
+
foreach ( $tmp['search_for'] as $replace ) {
|
470 |
+
$dataRow = $this->recursive_unserialize_replace( $tmp['search_for'][$i], $tmp['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
|
471 |
+
$i++;
|
472 |
+
}
|
473 |
+
unset( $replace );
|
474 |
+
unset( $i );
|
475 |
+
unset( $tmp );
|
476 |
+
|
477 |
+
// Something was changed
|
478 |
+
if( $row[$column] != $dataRow ) {
|
479 |
+
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
480 |
+
$upd = true;
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
// Determine what to do with updates.
|
485 |
+
if( $args['dry_run'] === 'on' ) {
|
486 |
+
// Don't do anything if a dry run
|
487 |
+
} elseif( $upd && !empty( $where_sql ) ) {
|
488 |
+
// If there are changes to make, run the query.
|
489 |
+
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
490 |
+
$result = $this->db->query( $sql );
|
491 |
+
|
492 |
+
if( !$result ) {
|
493 |
+
$this->log( "Error updating row {$current_row} SQL: {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR );
|
494 |
+
}
|
495 |
+
}
|
496 |
+
} // end row loop
|
497 |
+
unset( $row );
|
498 |
+
unset( $update_sql );
|
499 |
+
unset( $where_sql );
|
500 |
+
unset( $sql );
|
501 |
+
unset( $current_row );
|
502 |
+
|
503 |
+
|
504 |
+
// DB Flush
|
505 |
+
$this->db->flush();
|
506 |
+
return true;
|
507 |
+
}
|
508 |
+
|
509 |
+
/**
|
510 |
+
* Get path to multisite image folder e.g. wp-content/blogs.dir/ID/files or wp-content/uploads/sites/ID
|
511 |
+
* @return string
|
512 |
+
*/
|
513 |
+
private function getImagePathLive() {
|
514 |
+
// Check first which structure is used
|
515 |
+
$uploads = wp_upload_dir();
|
516 |
+
$basedir = $uploads['basedir'];
|
517 |
+
$blogId = get_current_blog_id();
|
518 |
+
|
519 |
+
if( false === strpos( $basedir, 'blogs.dir' ) ) {
|
520 |
+
// Since WP 3.5
|
521 |
+
$path = $blogId > 1 ?
|
522 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR :
|
523 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
524 |
+
} else {
|
525 |
+
// old blog structure
|
526 |
+
$path = $blogId > 1 ?
|
527 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR :
|
528 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
529 |
+
}
|
530 |
+
return $path;
|
531 |
+
}
|
532 |
+
|
533 |
+
/**
|
534 |
+
* Get path to staging site image path wp-content/uploads
|
535 |
+
* @return string
|
536 |
+
*/
|
537 |
+
private function getImagePathStaging() {
|
538 |
+
return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* Adapted from interconnect/it's search/replace script.
|
543 |
+
*
|
544 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
545 |
+
*
|
546 |
+
* Take a serialised array and unserialise it replacing elements as needed and
|
547 |
+
* unserialising any subordinate arrays and performing the replace on those too.
|
548 |
+
*
|
549 |
+
* @access private
|
550 |
+
* @param string $from String we're looking to replace.
|
551 |
+
* @param string $to What we want it to be replaced with
|
552 |
+
* @param array $data Used to pass any subordinate arrays back to in.
|
553 |
+
* @param boolean $serialized Does the array passed via $data need serialising.
|
554 |
+
* @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
|
555 |
+
*
|
556 |
+
* @return string|array The original array with all elements replaced as needed.
|
557 |
+
*/
|
558 |
+
private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
|
559 |
+
try {
|
560 |
+
// PDO instances can not be serialized or unserialized
|
561 |
+
if( is_serialized( $data ) && strpos( $data, 'O:3:"PDO":0:' ) !== false ) {
|
562 |
+
return $data;
|
563 |
+
}
|
564 |
+
// Some unserialized data cannot be re-serialized eg. SimpleXMLElements
|
565 |
+
if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
566 |
+
$data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
|
567 |
+
} elseif( is_array( $data ) ) {
|
568 |
+
$tmp = array();
|
569 |
+
foreach ( $data as $key => $value ) {
|
570 |
+
$tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
571 |
+
}
|
572 |
+
|
573 |
+
$data = $tmp;
|
574 |
+
unset( $tmp );
|
575 |
+
} elseif( is_object( $data ) ) {
|
576 |
+
$props = get_object_vars( $data );
|
577 |
+
|
578 |
+
// Do a search & replace
|
579 |
+
if( empty( $props['__PHP_Incomplete_Class_Name'] ) ) {
|
580 |
+
$tmp = $data;
|
581 |
+
foreach ( $props as $key => $value ) {
|
582 |
+
if( $key === '' || ord( $key[0] ) === 0 ) {
|
583 |
+
continue;
|
584 |
+
}
|
585 |
+
$tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
586 |
+
}
|
587 |
+
$data = $tmp;
|
588 |
+
$tmp = '';
|
589 |
+
$props = '';
|
590 |
+
unset( $tmp );
|
591 |
+
unset( $props );
|
592 |
+
}
|
593 |
+
} else {
|
594 |
+
if( is_string( $data ) ) {
|
595 |
+
if( !empty( $from ) && !empty( $to ) ) {
|
596 |
+
$data = $this->str_replace( $from, $to, $data, $case_insensitive );
|
597 |
+
}
|
598 |
+
}
|
599 |
+
}
|
600 |
+
|
601 |
+
if( $serialized ) {
|
602 |
+
return serialize( $data );
|
603 |
+
}
|
604 |
+
} catch ( Exception $error ) {
|
605 |
+
|
606 |
+
}
|
607 |
+
|
608 |
+
return $data;
|
609 |
+
}
|
610 |
+
|
611 |
+
/**
|
612 |
+
* Check if the object is a valid one and not __PHP_Incomplete_Class_Name
|
613 |
+
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
614 |
+
* @return boolean
|
615 |
+
*/
|
616 |
+
// private function isValidObject( $data ) {
|
617 |
+
// if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
618 |
+
// return false;
|
619 |
+
// }
|
620 |
+
//
|
621 |
+
// $invalid_class_props = get_object_vars( $data );
|
622 |
+
//
|
623 |
+
// if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
624 |
+
// // Assume it must be an valid object
|
625 |
+
// return true;
|
626 |
+
// }
|
627 |
+
//
|
628 |
+
// $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
629 |
+
//
|
630 |
+
// if( !empty( $invalid_object_class ) ) {
|
631 |
+
// return false;
|
632 |
+
// }
|
633 |
+
//
|
634 |
+
// // Assume it must be an valid object
|
635 |
+
// return true;
|
636 |
+
// }
|
637 |
+
|
638 |
+
/**
|
639 |
+
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
640 |
+
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
|
641 |
+
* @access public
|
642 |
+
* @param string $input The string to escape.
|
643 |
+
* @return string
|
644 |
+
*/
|
645 |
+
private function mysql_escape_mimic( $input ) {
|
646 |
+
if( is_array( $input ) ) {
|
647 |
+
return array_map( __METHOD__, $input );
|
648 |
+
}
|
649 |
+
if( !empty( $input ) && is_string( $input ) ) {
|
650 |
+
return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
|
651 |
+
}
|
652 |
+
|
653 |
+
return $input;
|
654 |
+
}
|
655 |
+
|
656 |
+
/**
|
657 |
+
* Return unserialized object or array
|
658 |
+
*
|
659 |
+
* @param string $serialized_string Serialized string.
|
660 |
+
* @param string $method The name of the caller method.
|
661 |
+
*
|
662 |
+
* @return mixed, false on failure
|
663 |
+
*/
|
664 |
+
private static function unserialize( $serialized_string ) {
|
665 |
+
if( !is_serialized( $serialized_string ) ) {
|
666 |
+
return false;
|
667 |
+
}
|
668 |
+
|
669 |
+
$serialized_string = trim( $serialized_string );
|
670 |
+
$unserialized_string = @unserialize( $serialized_string );
|
671 |
+
|
672 |
+
return $unserialized_string;
|
673 |
+
}
|
674 |
+
|
675 |
+
/**
|
676 |
+
* Wrapper for str_replace
|
677 |
+
*
|
678 |
+
* @param string $from
|
679 |
+
* @param string $to
|
680 |
+
* @param string $data
|
681 |
+
* @param string|bool $case_insensitive
|
682 |
+
*
|
683 |
+
* @return string
|
684 |
+
*/
|
685 |
+
private function str_replace( $from, $to, $data, $case_insensitive = false ) {
|
686 |
+
|
687 |
+
// Add filter
|
688 |
+
$excludes = apply_filters( 'wpstg_clone_searchreplace_excl', array() );
|
689 |
+
|
690 |
+
// Build pattern
|
691 |
+
$regexExclude = '';
|
692 |
+
foreach ( $excludes as $exclude ) {
|
693 |
+
$regexExclude .= $exclude . '(*SKIP)(FAIL)|';
|
694 |
+
}
|
695 |
+
|
696 |
+
if( 'on' === $case_insensitive ) {
|
697 |
+
//$data = str_ireplace( $from, $to, $data );
|
698 |
+
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#i', $to, $data );
|
699 |
+
} else {
|
700 |
+
//$data = str_replace( $from, $to, $data );
|
701 |
+
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#', $to, $data );
|
702 |
+
}
|
703 |
+
|
704 |
+
return $data;
|
705 |
+
}
|
706 |
+
|
707 |
+
/**
|
708 |
+
* Set the job
|
709 |
+
* @param string $table
|
710 |
+
*/
|
711 |
+
private function setJob( $table ) {
|
712 |
+
if( !empty( $this->options->job->current ) ) {
|
713 |
+
return;
|
714 |
+
}
|
715 |
+
|
716 |
+
$this->options->job->current = $table;
|
717 |
+
$this->options->job->start = 0;
|
718 |
+
}
|
719 |
+
|
720 |
+
/**
|
721 |
+
* Start Job
|
722 |
+
* @param string $new
|
723 |
+
* @param string $old
|
724 |
+
* @return bool
|
725 |
+
*/
|
726 |
+
private function startJob( $new, $old ) {
|
727 |
+
|
728 |
+
$this->options->job->total = 0;
|
729 |
+
|
730 |
+
if( 0 != $this->options->job->start ) {
|
731 |
+
return true;
|
732 |
+
}
|
733 |
+
|
734 |
+
// Table does not exist
|
735 |
+
$result = $this->db->query( "SHOW TABLES LIKE '{$old}'" );
|
736 |
+
if( !$result || 0 === $result ) {
|
737 |
+
return false;
|
738 |
+
}
|
739 |
+
|
740 |
+
$this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
|
741 |
+
|
742 |
+
if( 0 == $this->options->job->total ) {
|
743 |
+
$this->finishStep();
|
744 |
+
return false;
|
745 |
+
}
|
746 |
+
|
747 |
+
return true;
|
748 |
+
}
|
749 |
+
|
750 |
+
/**
|
751 |
+
* Is table excluded from search replace processing?
|
752 |
+
* @param string $table
|
753 |
+
* @return boolean
|
754 |
+
*/
|
755 |
+
private function isExcludedTable( $table ) {
|
756 |
+
|
757 |
+
$customTables = apply_filters( 'wpstg_clone_searchreplace_tables_exclude', array() );
|
758 |
+
$defaultTables = array('blogs');
|
759 |
+
|
760 |
+
$tables = array_merge( $customTables, $defaultTables );
|
761 |
+
|
762 |
+
$excludedTables = array();
|
763 |
+
foreach ( $tables as $key => $value ) {
|
764 |
+
$excludedTables[] = $this->options->prefix . $value;
|
765 |
+
}
|
766 |
+
|
767 |
+
if( in_array( $table, $excludedTables ) ) {
|
768 |
+
return true;
|
769 |
+
}
|
770 |
+
return false;
|
771 |
+
}
|
772 |
+
|
773 |
+
/**
|
774 |
+
* Finish the step
|
775 |
+
*/
|
776 |
+
private function finishStep() {
|
777 |
+
// This job is not finished yet
|
778 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
779 |
+
return false;
|
780 |
+
}
|
781 |
+
|
782 |
+
// Add it to cloned tables listing
|
783 |
+
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
784 |
+
|
785 |
+
// Reset job
|
786 |
+
$this->options->job = new \stdClass();
|
787 |
+
|
788 |
+
return true;
|
789 |
+
}
|
790 |
+
|
791 |
+
/**
|
792 |
+
* Drop table if necessary
|
793 |
+
* @param string $new
|
794 |
+
*/
|
795 |
+
private function dropTable( $new ) {
|
796 |
+
$old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
|
797 |
+
|
798 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
799 |
+
return;
|
800 |
+
}
|
801 |
+
|
802 |
+
$this->log( "DB Search & Replace: {$new} already exists, dropping it first" );
|
803 |
+
$this->db->query( "DROP TABLE {$new}" );
|
804 |
+
}
|
805 |
+
|
806 |
+
/**
|
807 |
+
* Check if table needs to be dropped
|
808 |
+
* @param string $new
|
809 |
+
* @param string $old
|
810 |
+
* @return bool
|
811 |
+
*/
|
812 |
+
private function shouldDropTable( $new, $old ) {
|
813 |
+
return (
|
814 |
+
$old == $new &&
|
815 |
+
(
|
816 |
+
!isset( $this->options->job->current ) ||
|
817 |
+
!isset( $this->options->job->start ) ||
|
818 |
+
0 == $this->options->job->start
|
819 |
+
)
|
820 |
+
);
|
821 |
+
}
|
822 |
+
|
823 |
+
/**
|
824 |
+
* Check if WP is installed in subdir
|
825 |
+
* @return boolean
|
826 |
+
*/
|
827 |
+
private function isSubDir() {
|
828 |
+
// Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
|
829 |
+
// This is happening much more often than you would expect
|
830 |
+
$siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
|
831 |
+
$home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
|
832 |
+
|
833 |
+
if( $home !== $siteurl ) {
|
834 |
+
return true;
|
835 |
+
}
|
836 |
+
return false;
|
837 |
+
}
|
838 |
+
|
839 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/SearchReplaceExternal.php
CHANGED
@@ -1,840 +1,833 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
-
|
5 |
-
// No Direct Access
|
6 |
-
if( !defined( "WPINC" ) ) {
|
7 |
-
die;
|
8 |
-
}
|
9 |
-
|
10 |
-
use WPStaging\WPStaging;
|
11 |
-
use WPStaging\Utils\Strings;
|
12 |
-
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Class Database
|
16 |
-
* @package WPStaging\Backend\Modules\Jobs
|
17 |
-
*/
|
18 |
-
class SearchReplaceExternal extends JobExecutable {
|
19 |
-
|
20 |
-
/**
|
21 |
-
* @var int
|
22 |
-
*/
|
23 |
-
private $total = 0;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Staging Site DB
|
27 |
-
* @var \WPDB
|
28 |
-
*/
|
29 |
-
private $stagingDb;
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Production Site DB
|
33 |
-
* @var \WPDB
|
34 |
-
*/
|
35 |
-
private $productionDb;
|
36 |
-
|
37 |
-
/**
|
38 |
-
*
|
39 |
-
* @var string
|
40 |
-
*/
|
41 |
-
private $sourceHostname;
|
42 |
-
|
43 |
-
/**
|
44 |
-
*
|
45 |
-
* @var string
|
46 |
-
*/
|
47 |
-
private $destinationHostname;
|
48 |
-
|
49 |
-
/**
|
50 |
-
*
|
51 |
-
* @var Obj
|
52 |
-
*/
|
53 |
-
private $strings;
|
54 |
-
|
55 |
-
/**
|
56 |
-
* The prefix of the new database tables which are used for the live site after updating tables
|
57 |
-
* @var string
|
58 |
-
*/
|
59 |
-
public $tmpPrefix;
|
60 |
-
|
61 |
-
/**
|
62 |
-
* Initialize
|
63 |
-
*/
|
64 |
-
public function initialize() {
|
65 |
-
$this->total = count( $this->options->tables );
|
66 |
-
$this->stagingDb = $this->getStagingDB();
|
67 |
-
$this->productionDb = WPStaging::getInstance()->get( "wpdb" );
|
68 |
-
$this->tmpPrefix = $this->options->prefix;
|
69 |
-
$this->strings = new Strings();
|
70 |
-
$this->sourceHostname = $this->getSourceHostname();
|
71 |
-
$this->destinationHostname = $this->getDestinationHostname();
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Get database object to interact with
|
76 |
-
*/
|
77 |
-
private function getStagingDB() {
|
78 |
-
return new \wpdb( $this->options->databaseUser, $this->options->databasePassword, $this->options->databaseDatabase, $this->options->databaseServer );
|
79 |
-
}
|
80 |
-
|
81 |
-
public function start() {
|
82 |
-
// Skip job. Nothing to do
|
83 |
-
if( $this->options->totalSteps === 0 ) {
|
84 |
-
$this->prepareResponse( true, false );
|
85 |
-
}
|
86 |
-
|
87 |
-
$this->run();
|
88 |
-
|
89 |
-
// Save option, progress
|
90 |
-
$this->saveOptions();
|
91 |
-
|
92 |
-
return ( object ) $this->response;
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
97 |
-
* @return void
|
98 |
-
*/
|
99 |
-
protected function calculateTotalSteps() {
|
100 |
-
$this->options->totalSteps = $this->total;
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Execute the Current Step
|
105 |
-
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
106 |
-
* @return bool
|
107 |
-
*/
|
108 |
-
protected function execute() {
|
109 |
-
// Over limits threshold
|
110 |
-
if( $this->isOverThreshold() ) {
|
111 |
-
// Prepare response and save current progress
|
112 |
-
$this->prepareResponse( false, false );
|
113 |
-
$this->saveOptions();
|
114 |
-
return false;
|
115 |
-
}
|
116 |
-
|
117 |
-
// No more steps, finished
|
118 |
-
if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
|
119 |
-
$this->prepareResponse( true, false );
|
120 |
-
return false;
|
121 |
-
}
|
122 |
-
|
123 |
-
// Table is excluded
|
124 |
-
if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
|
125 |
-
$this->prepareResponse();
|
126 |
-
return true;
|
127 |
-
}
|
128 |
-
|
129 |
-
// Search & Replace
|
130 |
-
if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
|
131 |
-
// Prepare Response
|
132 |
-
$this->prepareResponse( false, false );
|
133 |
-
|
134 |
-
// Not finished
|
135 |
-
return true;
|
136 |
-
}
|
137 |
-
|
138 |
-
|
139 |
-
// Prepare Response
|
140 |
-
$this->prepareResponse();
|
141 |
-
|
142 |
-
// Not finished
|
143 |
-
return true;
|
144 |
-
}
|
145 |
-
|
146 |
-
/**
|
147 |
-
* Stop Execution immediately
|
148 |
-
* return mixed bool | json
|
149 |
-
*/
|
150 |
-
private function stopExecution() {
|
151 |
-
// if( $this->stagingDb->prefix == $this->tmpPrefix ) {
|
152 |
-
// $this->returnException( 'Fatal Error 9: Prefix ' . $this->stagingDb->prefix . ' is used for the live site hence it can not be used for the staging site as well. Please ask support@wp-staging.com how to resolve this.' );
|
153 |
-
// }
|
154 |
-
return false;
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Copy Tables
|
159 |
-
* @param string $tableName
|
160 |
-
* @return bool
|
161 |
-
*/
|
162 |
-
private function updateTable( $tableName ) {
|
163 |
-
$strings = new Strings();
|
164 |
-
$table = $strings->str_replace_first( $this->productionDb->prefix, '', $tableName );
|
165 |
-
$newTableName = $this->tmpPrefix . $table;
|
166 |
-
|
167 |
-
// Save current job
|
168 |
-
$this->setJob( $newTableName );
|
169 |
-
|
170 |
-
// Beginning of the job
|
171 |
-
if( !$this->startJob( $newTableName, $tableName ) ) {
|
172 |
-
return true;
|
173 |
-
}
|
174 |
-
// Copy data
|
175 |
-
$this->startReplace( $newTableName );
|
176 |
-
|
177 |
-
// Finish the step
|
178 |
-
return $this->finishStep();
|
179 |
-
}
|
180 |
-
|
181 |
-
/**
|
182 |
-
* Get source Hostname depending on wheather WP has been installed in sub dir or not
|
183 |
-
* @return type
|
184 |
-
*/
|
185 |
-
private function getSourceHostname() {
|
186 |
-
|
187 |
-
if( $this->isSubDir() ) {
|
188 |
-
return trailingslashit( $this->multisiteHomeUrlWithoutScheme ) . '/' . $this->getSubDir();
|
189 |
-
}
|
190 |
-
return $this->multisiteHomeUrlWithoutScheme;
|
191 |
-
}
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Get destination Hostname depending on wheather WP has been installed in sub dir or not
|
195 |
-
* Retun host name without scheme
|
196 |
-
* @return type
|
197 |
-
*/
|
198 |
-
private function getDestinationHostname() {
|
199 |
-
|
200 |
-
if( !empty( $this->options->cloneHostname ) ) {
|
201 |
-
return $this->strings->getUrlWithoutScheme( $this->options->cloneHostname );
|
202 |
-
}
|
203 |
-
|
204 |
-
if( $this->isSubDir() ) {
|
205 |
-
return trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName;
|
206 |
-
}
|
207 |
-
return trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $this->options->cloneDirectoryName;
|
208 |
-
}
|
209 |
-
|
210 |
-
/**
|
211 |
-
* Get the install sub directory if WP is installed in sub directory
|
212 |
-
* @return string
|
213 |
-
*/
|
214 |
-
private function getSubDir() {
|
215 |
-
$home = get_option( 'home' );
|
216 |
-
$siteurl = get_option( 'siteurl' );
|
217 |
-
|
218 |
-
if( empty( $home ) || empty( $siteurl ) ) {
|
219 |
-
return '';
|
220 |
-
}
|
221 |
-
|
222 |
-
$dir = str_replace( $home, '', $siteurl );
|
223 |
-
return str_replace( '/', '', $dir );
|
224 |
-
}
|
225 |
-
|
226 |
-
/**
|
227 |
-
* Start search replace job
|
228 |
-
* @param string $new
|
229 |
-
* @param string $old
|
230 |
-
*/
|
231 |
-
private function startReplace( $table ) {
|
232 |
-
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
233 |
-
$this->log(
|
234 |
-
"DB
|
235 |
-
);
|
236 |
-
|
237 |
-
// Search & Replace
|
238 |
-
$this->searchReplace( $table, $rows, array() );
|
239 |
-
|
240 |
-
// Set new offset
|
241 |
-
$this->options->job->start += $this->settings->querySRLimit;
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Returns the number of pages in a table.
|
246 |
-
* @access public
|
247 |
-
* @return int
|
248 |
-
*/
|
249 |
-
private function get_pages_in_table( $table ) {
|
250 |
-
|
251 |
-
// Table does not exist
|
252 |
-
$table = str_replace( $this->options->prefix . '.', null, $table );
|
253 |
-
$result = $this->productionDb->query( "SHOW TABLES LIKE '{$table}'" );
|
254 |
-
if( !$result || 0 === $result ) {
|
255 |
-
return 0;
|
256 |
-
}
|
257 |
-
|
258 |
-
$table = esc_sql( $table );
|
259 |
-
$rows = $this->productionDb->get_var( "SELECT COUNT(*) FROM $table" );
|
260 |
-
$pages = ceil( $rows / $this->settings->querySRLimit );
|
261 |
-
return absint( $pages );
|
262 |
-
}
|
263 |
-
|
264 |
-
/**
|
265 |
-
* Gets the columns in a table.
|
266 |
-
* @access public
|
267 |
-
* @param string $table The table to check.
|
268 |
-
* @return array
|
269 |
-
*/
|
270 |
-
private function get_columns( $table ) {
|
271 |
-
$primary_key = null;
|
272 |
-
$columns = array();
|
273 |
-
$fields = $this->stagingDb->get_results( 'DESCRIBE ' . $table );
|
274 |
-
if( is_array( $fields ) ) {
|
275 |
-
foreach ( $fields as $column ) {
|
276 |
-
$columns[] = $column->Field;
|
277 |
-
if( $column->Key == 'PRI' ) {
|
278 |
-
$primary_key = $column->Field;
|
279 |
-
}
|
280 |
-
}
|
281 |
-
}
|
282 |
-
return array($primary_key, $columns);
|
283 |
-
}
|
284 |
-
|
285 |
-
/**
|
286 |
-
*
|
287 |
-
*
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
*
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
$
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
$args['
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
$
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
//
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
//
|
358 |
-
|
359 |
-
|
360 |
-
//
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
$
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
$
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
//
|
424 |
-
if(
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
//
|
430 |
-
//
|
431 |
-
//
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
)
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
//
|
470 |
-
$
|
471 |
-
|
472 |
-
$
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
*
|
536 |
-
* @
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
*
|
544 |
-
*
|
545 |
-
* @
|
546 |
-
*
|
547 |
-
*
|
548 |
-
*
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
$
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
//
|
616 |
-
//
|
617 |
-
// return
|
618 |
-
// }
|
619 |
-
//
|
620 |
-
// $
|
621 |
-
//
|
622 |
-
// if
|
623 |
-
//
|
624 |
-
//
|
625 |
-
//
|
626 |
-
//
|
627 |
-
//
|
628 |
-
//
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
*
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
$
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
return
|
739 |
-
}
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
$
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
if( $home !== $siteurl ) {
|
835 |
-
return true;
|
836 |
-
}
|
837 |
-
return false;
|
838 |
-
}
|
839 |
-
|
840 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace WPStaging\Backend\Modules\Jobs\Multisite;
|
4 |
+
|
5 |
+
// No Direct Access
|
6 |
+
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
+
}
|
9 |
+
|
10 |
+
use WPStaging\WPStaging;
|
11 |
+
use WPStaging\Utils\Strings;
|
12 |
+
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Class Database
|
16 |
+
* @package WPStaging\Backend\Modules\Jobs
|
17 |
+
*/
|
18 |
+
class SearchReplaceExternal extends JobExecutable {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
private $total = 0;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Staging Site DB
|
27 |
+
* @var \WPDB
|
28 |
+
*/
|
29 |
+
private $stagingDb;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Production Site DB
|
33 |
+
* @var \WPDB
|
34 |
+
*/
|
35 |
+
private $productionDb;
|
36 |
+
|
37 |
+
/**
|
38 |
+
*
|
39 |
+
* @var string
|
40 |
+
*/
|
41 |
+
private $sourceHostname;
|
42 |
+
|
43 |
+
/**
|
44 |
+
*
|
45 |
+
* @var string
|
46 |
+
*/
|
47 |
+
private $destinationHostname;
|
48 |
+
|
49 |
+
/**
|
50 |
+
*
|
51 |
+
* @var Obj
|
52 |
+
*/
|
53 |
+
private $strings;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* The prefix of the new database tables which are used for the live site after updating tables
|
57 |
+
* @var string
|
58 |
+
*/
|
59 |
+
public $tmpPrefix;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Initialize
|
63 |
+
*/
|
64 |
+
public function initialize() {
|
65 |
+
$this->total = count( $this->options->tables );
|
66 |
+
$this->stagingDb = $this->getStagingDB();
|
67 |
+
$this->productionDb = WPStaging::getInstance()->get( "wpdb" );
|
68 |
+
$this->tmpPrefix = $this->options->prefix;
|
69 |
+
$this->strings = new Strings();
|
70 |
+
$this->sourceHostname = $this->getSourceHostname();
|
71 |
+
$this->destinationHostname = $this->getDestinationHostname();
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Get database object to interact with
|
76 |
+
*/
|
77 |
+
private function getStagingDB() {
|
78 |
+
return new \wpdb( $this->options->databaseUser, $this->options->databasePassword, $this->options->databaseDatabase, $this->options->databaseServer );
|
79 |
+
}
|
80 |
+
|
81 |
+
public function start() {
|
82 |
+
// Skip job. Nothing to do
|
83 |
+
if( $this->options->totalSteps === 0 ) {
|
84 |
+
$this->prepareResponse( true, false );
|
85 |
+
}
|
86 |
+
|
87 |
+
$this->run();
|
88 |
+
|
89 |
+
// Save option, progress
|
90 |
+
$this->saveOptions();
|
91 |
+
|
92 |
+
return ( object ) $this->response;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
97 |
+
* @return void
|
98 |
+
*/
|
99 |
+
protected function calculateTotalSteps() {
|
100 |
+
$this->options->totalSteps = $this->total;
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Execute the Current Step
|
105 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
106 |
+
* @return bool
|
107 |
+
*/
|
108 |
+
protected function execute() {
|
109 |
+
// Over limits threshold
|
110 |
+
if( $this->isOverThreshold() ) {
|
111 |
+
// Prepare response and save current progress
|
112 |
+
$this->prepareResponse( false, false );
|
113 |
+
$this->saveOptions();
|
114 |
+
return false;
|
115 |
+
}
|
116 |
+
|
117 |
+
// No more steps, finished
|
118 |
+
if( $this->options->currentStep > $this->total || !isset( $this->options->tables[$this->options->currentStep] ) ) {
|
119 |
+
$this->prepareResponse( true, false );
|
120 |
+
return false;
|
121 |
+
}
|
122 |
+
|
123 |
+
// Table is excluded
|
124 |
+
if( in_array( $this->options->tables[$this->options->currentStep], $this->options->excludedTables ) ) {
|
125 |
+
$this->prepareResponse();
|
126 |
+
return true;
|
127 |
+
}
|
128 |
+
|
129 |
+
// Search & Replace
|
130 |
+
if( !$this->stopExecution() && !$this->updateTable( $this->options->tables[$this->options->currentStep] ) ) {
|
131 |
+
// Prepare Response
|
132 |
+
$this->prepareResponse( false, false );
|
133 |
+
|
134 |
+
// Not finished
|
135 |
+
return true;
|
136 |
+
}
|
137 |
+
|
138 |
+
|
139 |
+
// Prepare Response
|
140 |
+
$this->prepareResponse();
|
141 |
+
|
142 |
+
// Not finished
|
143 |
+
return true;
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Stop Execution immediately
|
148 |
+
* return mixed bool | json
|
149 |
+
*/
|
150 |
+
private function stopExecution() {
|
151 |
+
// if( $this->stagingDb->prefix == $this->tmpPrefix ) {
|
152 |
+
// $this->returnException( 'Fatal Error 9: Prefix ' . $this->stagingDb->prefix . ' is used for the live site hence it can not be used for the staging site as well. Please ask support@wp-staging.com how to resolve this.' );
|
153 |
+
// }
|
154 |
+
return false;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Copy Tables
|
159 |
+
* @param string $tableName
|
160 |
+
* @return bool
|
161 |
+
*/
|
162 |
+
private function updateTable( $tableName ) {
|
163 |
+
$strings = new Strings();
|
164 |
+
$table = $strings->str_replace_first( $this->productionDb->prefix, '', $tableName );
|
165 |
+
$newTableName = $this->tmpPrefix . $table;
|
166 |
+
|
167 |
+
// Save current job
|
168 |
+
$this->setJob( $newTableName );
|
169 |
+
|
170 |
+
// Beginning of the job
|
171 |
+
if( !$this->startJob( $newTableName, $tableName ) ) {
|
172 |
+
return true;
|
173 |
+
}
|
174 |
+
// Copy data
|
175 |
+
$this->startReplace( $newTableName );
|
176 |
+
|
177 |
+
// Finish the step
|
178 |
+
return $this->finishStep();
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Get source Hostname depending on wheather WP has been installed in sub dir or not
|
183 |
+
* @return type
|
184 |
+
*/
|
185 |
+
private function getSourceHostname() {
|
186 |
+
|
187 |
+
if( $this->isSubDir() ) {
|
188 |
+
return trailingslashit( $this->multisiteHomeUrlWithoutScheme ) . '/' . $this->getSubDir();
|
189 |
+
}
|
190 |
+
return $this->multisiteHomeUrlWithoutScheme;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* Get destination Hostname depending on wheather WP has been installed in sub dir or not
|
195 |
+
* Retun host name without scheme
|
196 |
+
* @return type
|
197 |
+
*/
|
198 |
+
private function getDestinationHostname() {
|
199 |
+
|
200 |
+
if( !empty( $this->options->cloneHostname ) ) {
|
201 |
+
return $this->strings->getUrlWithoutScheme( $this->options->cloneHostname );
|
202 |
+
}
|
203 |
+
|
204 |
+
if( $this->isSubDir() ) {
|
205 |
+
return trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName;
|
206 |
+
}
|
207 |
+
return trailingslashit( $this->strings->getUrlWithoutScheme( $this->multisiteDomainWithoutScheme ) ) . $this->options->cloneDirectoryName;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Get the install sub directory if WP is installed in sub directory
|
212 |
+
* @return string
|
213 |
+
*/
|
214 |
+
private function getSubDir() {
|
215 |
+
$home = get_option( 'home' );
|
216 |
+
$siteurl = get_option( 'siteurl' );
|
217 |
+
|
218 |
+
if( empty( $home ) || empty( $siteurl ) ) {
|
219 |
+
return '';
|
220 |
+
}
|
221 |
+
|
222 |
+
$dir = str_replace( $home, '', $siteurl );
|
223 |
+
return str_replace( '/', '', $dir );
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Start search replace job
|
228 |
+
* @param string $new
|
229 |
+
* @param string $old
|
230 |
+
*/
|
231 |
+
private function startReplace( $table ) {
|
232 |
+
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
233 |
+
$this->log(
|
234 |
+
"DB Search & Replace: Table {$table} {$this->options->job->start} to {$rows} records"
|
235 |
+
);
|
236 |
+
|
237 |
+
// Search & Replace
|
238 |
+
$this->searchReplace( $table, $rows, array() );
|
239 |
+
|
240 |
+
// Set new offset
|
241 |
+
$this->options->job->start += $this->settings->querySRLimit;
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Returns the number of pages in a table.
|
246 |
+
* @access public
|
247 |
+
* @return int
|
248 |
+
*/
|
249 |
+
// private function get_pages_in_table( $table ) {
|
250 |
+
//
|
251 |
+
// // Table does not exist
|
252 |
+
// $table = str_replace( $this->options->prefix . '.', null, $table );
|
253 |
+
// $result = $this->productionDb->query( "SHOW TABLES LIKE '{$table}'" );
|
254 |
+
// if( !$result || 0 === $result ) {
|
255 |
+
// return 0;
|
256 |
+
// }
|
257 |
+
//
|
258 |
+
// $table = esc_sql( $table );
|
259 |
+
// $rows = $this->productionDb->get_var( "SELECT COUNT(*) FROM $table" );
|
260 |
+
// $pages = ceil( $rows / $this->settings->querySRLimit );
|
261 |
+
// return absint( $pages );
|
262 |
+
// }
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Gets the columns in a table.
|
266 |
+
* @access public
|
267 |
+
* @param string $table The table to check.
|
268 |
+
* @return array
|
269 |
+
*/
|
270 |
+
private function get_columns( $table ) {
|
271 |
+
$primary_key = null;
|
272 |
+
$columns = array();
|
273 |
+
$fields = $this->stagingDb->get_results( 'DESCRIBE ' . $table );
|
274 |
+
if( is_array( $fields ) ) {
|
275 |
+
foreach ( $fields as $column ) {
|
276 |
+
$columns[] = $column->Field;
|
277 |
+
if( $column->Key == 'PRI' ) {
|
278 |
+
$primary_key = $column->Field;
|
279 |
+
}
|
280 |
+
}
|
281 |
+
}
|
282 |
+
return array($primary_key, $columns);
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
287 |
+
*
|
288 |
+
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
289 |
+
* and to be compatible with batch processing.
|
290 |
+
*
|
291 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
292 |
+
*
|
293 |
+
* @access public
|
294 |
+
* @param string $table The table to run the replacement on.
|
295 |
+
* @param int $page The page/block to begin the query on.
|
296 |
+
* @param array $args An associative array containing arguments for this run.
|
297 |
+
* @return array
|
298 |
+
*/
|
299 |
+
private function searchReplace( $table, $page, $args ) {
|
300 |
+
|
301 |
+
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
302 |
+
$this->log( "DB Search & Replace: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
|
303 |
+
return true;
|
304 |
+
}
|
305 |
+
|
306 |
+
// Load up the default settings for this chunk.
|
307 |
+
$table = esc_sql( $table );
|
308 |
+
//$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
309 |
+
//$pages = $this->get_pages_in_table( $table );
|
310 |
+
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
311 |
+
$args['search_for'] = array(
|
312 |
+
'//' . $this->sourceHostname,
|
313 |
+
ABSPATH,
|
314 |
+
'\/\/' . str_replace( '/', '\/', $this->sourceHostname ), // Used by revslider and several visual editors
|
315 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->sourceHostname ), // HTML entitity for WP Backery Page Builder Plugin
|
316 |
+
//$this->getImagePathLive()
|
317 |
+
);
|
318 |
+
|
319 |
+
|
320 |
+
$args['replace_with'] = array(
|
321 |
+
'//' . $this->destinationHostname,
|
322 |
+
$this->options->destinationDir,
|
323 |
+
'\/\/' . str_replace( '/', '\/', $this->destinationHostname ), // Used by revslider and several visual editors
|
324 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->destinationHostname ), // HTML entitity for WP Backery Page Builder Plugin
|
325 |
+
//$this->getImagePathStaging()
|
326 |
+
);
|
327 |
+
|
328 |
+
$this->debugLog( "DB Search & Replace: Search: {$args['search_for'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
329 |
+
$this->debugLog( "DB Search & Replace: Replace: {$args['replace_with'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
330 |
+
|
331 |
+
|
332 |
+
$args['replace_guids'] = 'off';
|
333 |
+
$args['dry_run'] = 'off';
|
334 |
+
$args['case_insensitive'] = false;
|
335 |
+
//$args['replace_mails'] = 'off';
|
336 |
+
$args['skip_transients'] = 'on';
|
337 |
+
|
338 |
+
|
339 |
+
// Allow filtering of search & replace parameters
|
340 |
+
$args = apply_filters( 'wpstg_clone_searchreplace_params', $args );
|
341 |
+
|
342 |
+
// Get a list of columns in this table.
|
343 |
+
list( $primary_key, $columns ) = $this->get_columns( $table );
|
344 |
+
|
345 |
+
// Bail out early if there isn't a primary key.
|
346 |
+
// We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
|
347 |
+
// @todo test this carefully. If it causes (performance) issues we need to activate it again!
|
348 |
+
// @since 2.4.4
|
349 |
+
// if( null === $primary_key ) {
|
350 |
+
// return false;
|
351 |
+
// }
|
352 |
+
|
353 |
+
$current_row = 0;
|
354 |
+
$start = $this->options->job->start;
|
355 |
+
$end = $this->settings->querySRLimit;
|
356 |
+
|
357 |
+
// Grab the content of the table.
|
358 |
+
$data = $this->stagingDb->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
359 |
+
|
360 |
+
// Filter certain rows (of other plugins)
|
361 |
+
$filter = array(
|
362 |
+
'Admin_custome_login_Slidshow',
|
363 |
+
'Admin_custome_login_Social',
|
364 |
+
'Admin_custome_login_logo',
|
365 |
+
'Admin_custome_login_text',
|
366 |
+
'Admin_custome_login_login',
|
367 |
+
'Admin_custome_login_top',
|
368 |
+
'Admin_custome_login_dashboard',
|
369 |
+
'Admin_custome_login_Version',
|
370 |
+
'upload_path'
|
371 |
+
);
|
372 |
+
|
373 |
+
$filter = apply_filters( 'wpstg_clone_searchreplace_excl_rows', $filter );
|
374 |
+
|
375 |
+
// Loop through the data.
|
376 |
+
foreach ( $data as $row ) {
|
377 |
+
$current_row++;
|
378 |
+
$update_sql = array();
|
379 |
+
$where_sql = array();
|
380 |
+
$upd = false;
|
381 |
+
|
382 |
+
// Skip rows below
|
383 |
+
if( isset( $row['option_name'] ) && in_array( $row['option_name'], $filter ) ) {
|
384 |
+
continue;
|
385 |
+
}
|
386 |
+
|
387 |
+
// Skip rows with transients (They can store huge data and we need to save memory)
|
388 |
+
if( isset( $row['option_name'] ) && 'on' === $args['skip_transients'] && false !== strpos( $row['option_name'], '_transient' ) ) {
|
389 |
+
continue;
|
390 |
+
}
|
391 |
+
// Skip rows with more than 5MB to save memory
|
392 |
+
if( isset( $row['option_value'] ) && strlen( $row['option_value'] ) >= 5000000 ) {
|
393 |
+
continue;
|
394 |
+
}
|
395 |
+
|
396 |
+
|
397 |
+
foreach ( $columns as $column ) {
|
398 |
+
|
399 |
+
$dataRow = $row[$column];
|
400 |
+
|
401 |
+
|
402 |
+
// Skip rows larger than 5MB
|
403 |
+
$size = strlen( $dataRow );
|
404 |
+
if( $size >= 5000000 ) {
|
405 |
+
continue;
|
406 |
+
}
|
407 |
+
|
408 |
+
// Skip Primary key
|
409 |
+
if( $column == $primary_key ) {
|
410 |
+
$where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
411 |
+
continue;
|
412 |
+
}
|
413 |
+
|
414 |
+
// Skip GUIDs by default.
|
415 |
+
if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
|
416 |
+
continue;
|
417 |
+
}
|
418 |
+
|
419 |
+
// Skip mail addresses
|
420 |
+
// if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->multisiteDomainWithoutScheme ) ) {
|
421 |
+
// continue;
|
422 |
+
// }
|
423 |
+
// Check options table
|
424 |
+
if( $this->options->prefix . 'options' === $table ) {
|
425 |
+
|
426 |
+
// Skip certain options
|
427 |
+
// if( isset( $should_skip ) && true === $should_skip ) {
|
428 |
+
// $should_skip = false;
|
429 |
+
// continue;
|
430 |
+
// }
|
431 |
+
// Skip this row
|
432 |
+
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
433 |
+
'wpstg_existing_clones' === $dataRow ||
|
434 |
+
'wpstg_settings' === $dataRow ||
|
435 |
+
'wpstg_license_status' === $dataRow ||
|
436 |
+
'siteurl' === $dataRow ||
|
437 |
+
'home' === $dataRow
|
438 |
+
) {
|
439 |
+
//$should_skip = true;
|
440 |
+
continue;
|
441 |
+
}
|
442 |
+
}
|
443 |
+
|
444 |
+
// Check the path delimiter for / or \/ and remove one of those which prevents from resulting in wrong syntax like domain.com/staging\/.
|
445 |
+
// 1. local.wordpress.test -> local.wordpress.test/staging
|
446 |
+
// 2. local.wordpress.test\/ -> local.wordpress.test\/staging\/
|
447 |
+
$tmp = $args;
|
448 |
+
if( false === strpos( $dataRow, $tmp['search_for'][0] ) ) {
|
449 |
+
array_shift( $tmp['search_for'] ); // rtrim( $this->homeUrl, '/' ),
|
450 |
+
array_shift( $tmp['replace_with'] ); // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
|
451 |
+
} else {
|
452 |
+
unset( $tmp['search_for'][1] );
|
453 |
+
unset( $tmp['replace_with'][1] );
|
454 |
+
// recount array
|
455 |
+
$tmp['search_for'] = array_values( $tmp['search_for'] );
|
456 |
+
$tmp['replace_with'] = array_values( $tmp['replace_with'] );
|
457 |
+
}
|
458 |
+
|
459 |
+
// Run a search replace on the data row and respect the serialisation.
|
460 |
+
$i = 0;
|
461 |
+
foreach ( $tmp['search_for'] as $replace ) {
|
462 |
+
$dataRow = $this->recursive_unserialize_replace( $tmp['search_for'][$i], $tmp['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
|
463 |
+
$i++;
|
464 |
+
}
|
465 |
+
unset( $replace );
|
466 |
+
unset( $i );
|
467 |
+
unset( $tmp );
|
468 |
+
|
469 |
+
// Something was changed
|
470 |
+
if( $row[$column] != $dataRow ) {
|
471 |
+
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
472 |
+
$upd = true;
|
473 |
+
}
|
474 |
+
}
|
475 |
+
|
476 |
+
// Determine what to do with updates.
|
477 |
+
if( $args['dry_run'] === 'on' ) {
|
478 |
+
// Don't do anything if a dry run
|
479 |
+
} elseif( $upd && !empty( $where_sql ) ) {
|
480 |
+
// If there are changes to make, run the query.
|
481 |
+
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
482 |
+
$result = $this->stagingDb->query( $sql );
|
483 |
+
|
484 |
+
if( !$result ) {
|
485 |
+
$this->log( "Error updating row {$current_row} SQL: {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR );
|
486 |
+
}
|
487 |
+
}
|
488 |
+
} // end row loop
|
489 |
+
unset( $row );
|
490 |
+
unset( $update_sql );
|
491 |
+
unset( $where_sql );
|
492 |
+
unset( $sql );
|
493 |
+
unset( $current_row );
|
494 |
+
|
495 |
+
|
496 |
+
// DB Flush
|
497 |
+
$this->stagingDb->flush();
|
498 |
+
return true;
|
499 |
+
}
|
500 |
+
|
501 |
+
/**
|
502 |
+
* Get path to multisite image folder e.g. wp-content/blogs.dir/ID/files or wp-content/uploads/sites/ID
|
503 |
+
* @return string
|
504 |
+
*/
|
505 |
+
private function getImagePathLive() {
|
506 |
+
// Check first which structure is used
|
507 |
+
$uploads = wp_upload_dir();
|
508 |
+
$basedir = $uploads['basedir'];
|
509 |
+
$blogId = get_current_blog_id();
|
510 |
+
|
511 |
+
if( false === strpos( $basedir, 'blogs.dir' ) ) {
|
512 |
+
// Since WP 3.5
|
513 |
+
$path = $blogId > 1 ?
|
514 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR :
|
515 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
516 |
+
} else {
|
517 |
+
// old blog structure
|
518 |
+
$path = $blogId > 1 ?
|
519 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR :
|
520 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
521 |
+
}
|
522 |
+
return $path;
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Get path to staging site image path wp-content/uploads
|
527 |
+
* @return string
|
528 |
+
*/
|
529 |
+
private function getImagePathStaging() {
|
530 |
+
return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
531 |
+
}
|
532 |
+
|
533 |
+
/**
|
534 |
+
* Adapted from interconnect/it's search/replace script.
|
535 |
+
*
|
536 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
537 |
+
*
|
538 |
+
* Take a serialised array and unserialise it replacing elements as needed and
|
539 |
+
* unserialising any subordinate arrays and performing the replace on those too.
|
540 |
+
*
|
541 |
+
* @access private
|
542 |
+
* @param string $from String we're looking to replace.
|
543 |
+
* @param string $to What we want it to be replaced with
|
544 |
+
* @param array $data Used to pass any subordinate arrays back to in.
|
545 |
+
* @param boolean $serialized Does the array passed via $data need serialising.
|
546 |
+
* @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
|
547 |
+
*
|
548 |
+
* @return string|array The original array with all elements replaced as needed.
|
549 |
+
*/
|
550 |
+
private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
|
551 |
+
try {
|
552 |
+
// PDO instances can not be serialized or unserialized
|
553 |
+
if( is_serialized( $data ) && strpos( $data, 'O:3:"PDO":0:' ) !== false ) {
|
554 |
+
return $data;
|
555 |
+
}
|
556 |
+
// Some unserialized data cannot be re-serialized eg. SimpleXMLElements
|
557 |
+
if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
558 |
+
$data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
|
559 |
+
} elseif( is_array( $data ) ) {
|
560 |
+
$tmp = array();
|
561 |
+
foreach ( $data as $key => $value ) {
|
562 |
+
$tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
563 |
+
}
|
564 |
+
|
565 |
+
$data = $tmp;
|
566 |
+
unset( $tmp );
|
567 |
+
} elseif( is_object( $data ) ) {
|
568 |
+
$props = get_object_vars( $data );
|
569 |
+
|
570 |
+
// Do a search & replace
|
571 |
+
if( empty( $props['__PHP_Incomplete_Class_Name'] ) ) {
|
572 |
+
$tmp = $data;
|
573 |
+
foreach ( $props as $key => $value ) {
|
574 |
+
if( $key === '' || ord( $key[0] ) === 0 ) {
|
575 |
+
continue;
|
576 |
+
}
|
577 |
+
$tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
578 |
+
}
|
579 |
+
$data = $tmp;
|
580 |
+
$tmp = '';
|
581 |
+
$props = '';
|
582 |
+
unset( $tmp );
|
583 |
+
unset( $props );
|
584 |
+
}
|
585 |
+
} else {
|
586 |
+
if( is_string( $data ) ) {
|
587 |
+
if( !empty( $from ) && !empty( $to ) ) {
|
588 |
+
$data = $this->str_replace( $from, $to, $data, $case_insensitive );
|
589 |
+
}
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
if( $serialized ) {
|
594 |
+
return serialize( $data );
|
595 |
+
}
|
596 |
+
} catch ( Exception $error ) {
|
597 |
+
|
598 |
+
}
|
599 |
+
|
600 |
+
return $data;
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* Check if the object is a valid one and not __PHP_Incomplete_Class_Name
|
605 |
+
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
606 |
+
* @return boolean
|
607 |
+
*/
|
608 |
+
// private function isValidObject( $data ) {
|
609 |
+
// if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
610 |
+
// return false;
|
611 |
+
// }
|
612 |
+
//
|
613 |
+
// $invalid_class_props = get_object_vars( $data );
|
614 |
+
//
|
615 |
+
// if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
616 |
+
// // Assume it must be an valid object
|
617 |
+
// return true;
|
618 |
+
// }
|
619 |
+
//
|
620 |
+
// $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
621 |
+
//
|
622 |
+
// if( !empty( $invalid_object_class ) ) {
|
623 |
+
// return false;
|
624 |
+
// }
|
625 |
+
//
|
626 |
+
// // Assume it must be an valid object
|
627 |
+
// return true;
|
628 |
+
// }
|
629 |
+
|
630 |
+
/**
|
631 |
+
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
632 |
+
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
|
633 |
+
* @access public
|
634 |
+
* @param string $input The string to escape.
|
635 |
+
* @return string
|
636 |
+
*/
|
637 |
+
private function mysql_escape_mimic( $input ) {
|
638 |
+
if( is_array( $input ) ) {
|
639 |
+
return array_map( __METHOD__, $input );
|
640 |
+
}
|
641 |
+
if( !empty( $input ) && is_string( $input ) ) {
|
642 |
+
return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
|
643 |
+
}
|
644 |
+
|
645 |
+
return $input;
|
646 |
+
}
|
647 |
+
|
648 |
+
/**
|
649 |
+
* Return unserialized object or array
|
650 |
+
*
|
651 |
+
* @param string $serialized_string Serialized string.
|
652 |
+
* @param string $method The name of the caller method.
|
653 |
+
*
|
654 |
+
* @return mixed, false on failure
|
655 |
+
*/
|
656 |
+
private static function unserialize( $serialized_string ) {
|
657 |
+
if( !is_serialized( $serialized_string ) ) {
|
658 |
+
return false;
|
659 |
+
}
|
660 |
+
|
661 |
+
$serialized_string = trim( $serialized_string );
|
662 |
+
$unserialized_string = @unserialize( $serialized_string );
|
663 |
+
|
664 |
+
return $unserialized_string;
|
665 |
+
}
|
666 |
+
|
667 |
+
/**
|
668 |
+
* Wrapper for str_replace
|
669 |
+
*
|
670 |
+
* @param string $from
|
671 |
+
* @param string $to
|
672 |
+
* @param string $data
|
673 |
+
* @param string|bool $case_insensitive
|
674 |
+
*
|
675 |
+
* @return string
|
676 |
+
*/
|
677 |
+
private function str_replace( $from, $to, $data, $case_insensitive = false ) {
|
678 |
+
|
679 |
+
// Add filter
|
680 |
+
$excludes = apply_filters( 'wpstg_clone_searchreplace_excl', array() );
|
681 |
+
|
682 |
+
// Build pattern
|
683 |
+
$regexExclude = '';
|
684 |
+
foreach ( $excludes as $exclude ) {
|
685 |
+
$regexExclude .= $exclude . '(*SKIP)(FAIL)|';
|
686 |
+
}
|
687 |
+
|
688 |
+
if( 'on' === $case_insensitive ) {
|
689 |
+
//$data = str_ireplace( $from, $to, $data );
|
690 |
+
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#i', $to, $data );
|
691 |
+
} else {
|
692 |
+
//$data = str_replace( $from, $to, $data );
|
693 |
+
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#', $to, $data );
|
694 |
+
}
|
695 |
+
|
696 |
+
return $data;
|
697 |
+
}
|
698 |
+
|
699 |
+
/**
|
700 |
+
* Set the job
|
701 |
+
* @param string $table
|
702 |
+
*/
|
703 |
+
private function setJob( $table ) {
|
704 |
+
if( !empty( $this->options->job->current ) ) {
|
705 |
+
return;
|
706 |
+
}
|
707 |
+
|
708 |
+
$this->options->job->current = $table;
|
709 |
+
$this->options->job->start = 0;
|
710 |
+
}
|
711 |
+
|
712 |
+
/**
|
713 |
+
* Start Job
|
714 |
+
* @param string $new
|
715 |
+
* @param string $old
|
716 |
+
* @return bool
|
717 |
+
*/
|
718 |
+
private function startJob( $new, $old ) {
|
719 |
+
|
720 |
+
if( $this->isExcludedTable( $new ) ) {
|
721 |
+
return false;
|
722 |
+
}
|
723 |
+
|
724 |
+
// Table does not exist
|
725 |
+
$result = $this->productionDb->query( "SHOW TABLES LIKE '{$old}'" );
|
726 |
+
if( !$result || 0 === $result ) {
|
727 |
+
return false;
|
728 |
+
}
|
729 |
+
|
730 |
+
if( 0 != $this->options->job->start ) {
|
731 |
+
return true;
|
732 |
+
}
|
733 |
+
|
734 |
+
$this->options->job->total = ( int ) $this->productionDb->get_var( "SELECT COUNT(1) FROM {$old}" );
|
735 |
+
|
736 |
+
if( 0 == $this->options->job->total ) {
|
737 |
+
$this->finishStep();
|
738 |
+
return false;
|
739 |
+
}
|
740 |
+
|
741 |
+
return true;
|
742 |
+
}
|
743 |
+
|
744 |
+
/**
|
745 |
+
* Is table excluded from search replace processing?
|
746 |
+
* @param string $table
|
747 |
+
* @return boolean
|
748 |
+
*/
|
749 |
+
private function isExcludedTable( $table ) {
|
750 |
+
|
751 |
+
$customTables = apply_filters( 'wpstg_clone_searchreplace_tables_exclude', array() );
|
752 |
+
$defaultTables = array('blogs');
|
753 |
+
|
754 |
+
$tables = array_merge( $customTables, $defaultTables );
|
755 |
+
|
756 |
+
$excludedTables = array();
|
757 |
+
foreach ( $tables as $key => $value ) {
|
758 |
+
$excludedTables[] = $this->options->prefix . $value;
|
759 |
+
}
|
760 |
+
|
761 |
+
if( in_array( $table, $excludedTables ) ) {
|
762 |
+
return true;
|
763 |
+
}
|
764 |
+
return false;
|
765 |
+
}
|
766 |
+
|
767 |
+
/**
|
768 |
+
* Finish the step
|
769 |
+
*/
|
770 |
+
private function finishStep() {
|
771 |
+
// This job is not finished yet
|
772 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
773 |
+
return false;
|
774 |
+
}
|
775 |
+
|
776 |
+
// Add it to cloned tables listing
|
777 |
+
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
778 |
+
|
779 |
+
// Reset job
|
780 |
+
$this->options->job = new \stdClass();
|
781 |
+
|
782 |
+
return true;
|
783 |
+
}
|
784 |
+
|
785 |
+
/**
|
786 |
+
* Drop table if necessary
|
787 |
+
* @param string $new
|
788 |
+
*/
|
789 |
+
private function dropTable( $new ) {
|
790 |
+
$old = $this->stagingDb->get_var( $this->stagingDb->prepare( "SHOW TABLES LIKE %s", $new ) );
|
791 |
+
|
792 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
793 |
+
return;
|
794 |
+
}
|
795 |
+
|
796 |
+
$this->log( "DB Search & Replace: {$new} already exists, dropping it first" );
|
797 |
+
$this->stagingDb->query( "DROP TABLE {$new}" );
|
798 |
+
}
|
799 |
+
|
800 |
+
/**
|
801 |
+
* Check if table needs to be dropped
|
802 |
+
* @param string $new
|
803 |
+
* @param string $old
|
804 |
+
* @return bool
|
805 |
+
*/
|
806 |
+
private function shouldDropTable( $new, $old ) {
|
807 |
+
return (
|
808 |
+
$old == $new &&
|
809 |
+
(
|
810 |
+
!isset( $this->options->job->current ) ||
|
811 |
+
!isset( $this->options->job->start ) ||
|
812 |
+
0 == $this->options->job->start
|
813 |
+
)
|
814 |
+
);
|
815 |
+
}
|
816 |
+
|
817 |
+
/**
|
818 |
+
* Check if WP is installed in subdir
|
819 |
+
* @return boolean
|
820 |
+
*/
|
821 |
+
private function isSubDir() {
|
822 |
+
// Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
|
823 |
+
// This is happening much more often than you would expect
|
824 |
+
$siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
|
825 |
+
$home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
|
826 |
+
|
827 |
+
if( $home !== $siteurl ) {
|
828 |
+
return true;
|
829 |
+
}
|
830 |
+
return false;
|
831 |
+
}
|
832 |
+
|
833 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/Backend/Modules/Jobs/Scan.php
CHANGED
@@ -161,15 +161,15 @@ class Scan extends Job {
|
|
161 |
$dataSize = isset( $data["size"] ) ? $data["size"] : '';
|
162 |
|
163 |
|
164 |
-
//
|
165 |
-
//
|
166 |
$isDisabled = ($name !== 'wp-admin' &&
|
167 |
$name !== 'wp-includes' &&
|
168 |
$name !== 'wp-content' &&
|
169 |
$name !== 'sites') &&
|
170 |
-
false === strpos( strrev( $dataPath ), strrev( "wp-admin" ) ) &&
|
171 |
-
false === strpos( strrev( $dataPath ), strrev( "wp-includes" ) ) &&
|
172 |
-
false === strpos( strrev( $dataPath ), strrev( "wp-content" ) ) ? true : false;
|
173 |
|
174 |
// Extra class to differentiate between wp core and non core folders
|
175 |
$class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
|
@@ -190,6 +190,7 @@ class Scan extends Job {
|
|
190 |
$output .= "'>{$name}";
|
191 |
$output .= "</a>";
|
192 |
$output .= "<span class='wpstg-size-info'>{$this->formatSize( $dataSize )}</span>";
|
|
|
193 |
|
194 |
if( !empty( $directory ) ) {
|
195 |
$output .= "<div class='wpstg-dir wpstg-subdir'>";
|
@@ -303,10 +304,11 @@ class Scan extends Job {
|
|
303 |
// Gather Themes
|
304 |
$this->getSubDirectories( WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes" );
|
305 |
|
306 |
-
// Gather Default Uploads Folder
|
307 |
-
//$this->getSubDirectories(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads");
|
308 |
// Gather Custom Uploads Folder if there is one
|
309 |
$this->getSubDirectories( $this->getUploadDir() );
|
|
|
|
|
|
|
310 |
}
|
311 |
|
312 |
/**
|
@@ -318,6 +320,16 @@ class Scan extends Job {
|
|
318 |
return false;
|
319 |
}
|
320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
$directories = new \DirectoryIterator( $path );
|
322 |
|
323 |
foreach ( $directories as $directory ) {
|
@@ -420,12 +432,63 @@ class Scan extends Job {
|
|
420 |
}
|
421 |
|
422 |
/**
|
423 |
-
* Get absolute WP uploads path e.g.
|
|
|
|
|
424 |
* @return string
|
425 |
*/
|
426 |
protected function getUploadDir() {
|
427 |
-
$uploads = wp_upload_dir();
|
428 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
429 |
}
|
430 |
|
431 |
}
|
161 |
$dataSize = isset( $data["size"] ) ? $data["size"] : '';
|
162 |
|
163 |
|
164 |
+
// Select all wp core folders and their sub dirs.
|
165 |
+
// Unselect all other folders (default setting)
|
166 |
$isDisabled = ($name !== 'wp-admin' &&
|
167 |
$name !== 'wp-includes' &&
|
168 |
$name !== 'wp-content' &&
|
169 |
$name !== 'sites') &&
|
170 |
+
false === strpos( strrev( wpstg_replace_windows_directory_separator( $dataPath ) ), strrev( wpstg_replace_windows_directory_separator( ABSPATH . "wp-admin" ) ) ) &&
|
171 |
+
false === strpos( strrev( wpstg_replace_windows_directory_separator( $dataPath ) ), strrev( wpstg_replace_windows_directory_separator( ABSPATH . "wp-includes" ) ) ) &&
|
172 |
+
false === strpos( strrev( wpstg_replace_windows_directory_separator( $dataPath ) ), strrev( wpstg_replace_windows_directory_separator( ABSPATH . "wp-content" ) ) ) ? true : false;
|
173 |
|
174 |
// Extra class to differentiate between wp core and non core folders
|
175 |
$class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
|
190 |
$output .= "'>{$name}";
|
191 |
$output .= "</a>";
|
192 |
$output .= "<span class='wpstg-size-info'>{$this->formatSize( $dataSize )}</span>";
|
193 |
+
$output .= isset( $this->settings->debugMode ) ? "<span class='wpstg-size-info'> {$dataPath}</span>" : "";
|
194 |
|
195 |
if( !empty( $directory ) ) {
|
196 |
$output .= "<div class='wpstg-dir wpstg-subdir'>";
|
304 |
// Gather Themes
|
305 |
$this->getSubDirectories( WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "themes" );
|
306 |
|
|
|
|
|
307 |
// Gather Custom Uploads Folder if there is one
|
308 |
$this->getSubDirectories( $this->getUploadDir() );
|
309 |
+
|
310 |
+
// Gather /sites/ or /blogs.dir/ folder if there is one (for multisites)
|
311 |
+
$this->getSubDirectories( $this->getMuUploadSitesDir() );
|
312 |
}
|
313 |
|
314 |
/**
|
320 |
return false;
|
321 |
}
|
322 |
|
323 |
+
if( !is_dir( $path ) ) {
|
324 |
+
return false;
|
325 |
+
}
|
326 |
+
|
327 |
+
// IMPORTANT: If this is not used and a folder belongs to another user
|
328 |
+
// DirectoryIterator() will throw a fatal error which can not be catched with is_readable()
|
329 |
+
if( !opendir( $path ) ) {
|
330 |
+
return false;
|
331 |
+
}
|
332 |
+
|
333 |
$directories = new \DirectoryIterator( $path );
|
334 |
|
335 |
foreach ( $directories as $directory ) {
|
432 |
}
|
433 |
|
434 |
/**
|
435 |
+
* Get absolute WP uploads path e.g.
|
436 |
+
* Multisites: /var/www/htdocs/example.com/wp-content/uploads/sites/1 or /var/www/htdocs/example.com/wp-content/blogs.dir/1/files
|
437 |
+
* Single sites: /var/www/htdocs/example.com/wp-content/uploads
|
438 |
* @return string
|
439 |
*/
|
440 |
protected function getUploadDir() {
|
441 |
+
$uploads = wp_upload_dir( null, false );
|
442 |
+
|
443 |
+
$baseDir = wpstg_replace_windows_directory_separator( $uploads['basedir'] );
|
444 |
+
|
445 |
+
// If multisite (and if not the main site in a post-MU network)
|
446 |
+
if( is_multisite() && !( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
|
447 |
+
// blogs.dir is used on WP 3.5 and lower
|
448 |
+
if( false !== strpos( $baseDir, 'blogs.dir' ) ) {
|
449 |
+
// remove this piece from the basedir: /blogs.dir/2/files
|
450 |
+
$uploadDir = wpstg_replace_first_match( '/blogs.dir/' . get_current_blog_id() . '/files', null, $baseDir );
|
451 |
+
$dir = wpstg_replace_windows_directory_separator( $uploadDir . '/blogs.dir' );
|
452 |
+
} else {
|
453 |
+
// remove this piece from the basedir: /sites/2
|
454 |
+
$uploadDir = wpstg_replace_first_match( '/sites/' . get_current_blog_id(), null, $baseDir );
|
455 |
+
$dir = wpstg_replace_windows_directory_separator( $uploadDir . '/sites' );
|
456 |
+
}
|
457 |
+
|
458 |
+
|
459 |
+
return $dir;
|
460 |
+
}
|
461 |
+
return $baseDir;
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Get absolute WP uploads path e.g.
|
466 |
+
* Multisites: /var/www/htdocs/example.com/wp-content/uploads/sites/1 or /var/www/htdocs/example.com/wp-content/blogs.dir/1/files
|
467 |
+
* Single sites: /var/www/htdocs/example.com/wp-content/uploads
|
468 |
+
* @return string
|
469 |
+
*/
|
470 |
+
protected function getMuUploadSitesDir() {
|
471 |
+
$uploads = wp_upload_dir( null, false );
|
472 |
+
|
473 |
+
$baseDir = wpstg_replace_windows_directory_separator( $uploads['basedir'] );
|
474 |
+
|
475 |
+
// If multisite (and if not the main site in a post-MU network)
|
476 |
+
if( is_multisite() && !( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
|
477 |
+
// blogs.dir is used on WP 3.5 and lower
|
478 |
+
if( false !== strpos( $baseDir, 'blogs.dir' ) ) {
|
479 |
+
// remove this piece from the basedir: /blogs.dir/2/files
|
480 |
+
$uploadDir = wpstg_replace_first_match( '/blogs.dir/' . get_current_blog_id() . '/files', null, $baseDir );
|
481 |
+
$dir = wpstg_replace_windows_directory_separator( $uploadDir . '/blogs.dir' );
|
482 |
+
} else {
|
483 |
+
// remove this piece from the basedir: /sites/2
|
484 |
+
$uploadDir = wpstg_replace_first_match( '/sites/' . get_current_blog_id(), null, $baseDir );
|
485 |
+
$dir = wpstg_replace_windows_directory_separator( $uploadDir . '/sites' );
|
486 |
+
}
|
487 |
+
|
488 |
+
|
489 |
+
return $dir;
|
490 |
+
}
|
491 |
+
return $baseDir;
|
492 |
}
|
493 |
|
494 |
}
|
apps/Backend/Modules/Jobs/SearchReplace.php
CHANGED
@@ -220,7 +220,7 @@ class SearchReplace extends JobExecutable {
|
|
220 |
private function startReplace( $table ) {
|
221 |
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
222 |
$this->log(
|
223 |
-
"DB
|
224 |
);
|
225 |
|
226 |
// Search & Replace
|
@@ -288,7 +288,7 @@ class SearchReplace extends JobExecutable {
|
|
288 |
private function searchReplace( $table, $page, $args ) {
|
289 |
|
290 |
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
291 |
-
$this->log( "DB
|
292 |
return true;
|
293 |
}
|
294 |
|
@@ -302,16 +302,21 @@ class SearchReplace extends JobExecutable {
|
|
302 |
'%2F%2F' . str_replace( '/', '%2F', $this->sourceHostname ), // HTML entitity for WP Backery Page Builder Plugin
|
303 |
'\/\/' . str_replace( '/', '\/', $this->sourceHostname ), // Escaped \/ used by revslider and several visual editors
|
304 |
'//' . $this->sourceHostname,
|
305 |
-
rtrim( ABSPATH, '/' ),
|
|
|
306 |
);
|
307 |
|
308 |
$args['replace_with'] = array(
|
309 |
'%2F%2F' . str_replace( '/', '%2F', $this->destinationHostname ),
|
310 |
'\/\/' . str_replace( '/', '\/', $this->destinationHostname ),
|
311 |
'//' . $this->destinationHostname,
|
312 |
-
rtrim( $this->options->destinationDir, '/' ),
|
|
|
313 |
);
|
314 |
|
|
|
|
|
|
|
315 |
|
316 |
$args['replace_guids'] = 'off';
|
317 |
$args['dry_run'] = 'off';
|
@@ -772,7 +777,7 @@ class SearchReplace extends JobExecutable {
|
|
772 |
return;
|
773 |
}
|
774 |
|
775 |
-
$this->log( "DB
|
776 |
$this->db->query( "DROP TABLE {$new}" );
|
777 |
}
|
778 |
|
220 |
private function startReplace( $table ) {
|
221 |
$rows = $this->options->job->start + $this->settings->querySRLimit;
|
222 |
$this->log(
|
223 |
+
"DB Search & Replace: Table {$table} {$this->options->job->start} to {$rows} records"
|
224 |
);
|
225 |
|
226 |
// Search & Replace
|
288 |
private function searchReplace( $table, $page, $args ) {
|
289 |
|
290 |
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
291 |
+
$this->log( "DB Search & Replace: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
|
292 |
return true;
|
293 |
}
|
294 |
|
302 |
'%2F%2F' . str_replace( '/', '%2F', $this->sourceHostname ), // HTML entitity for WP Backery Page Builder Plugin
|
303 |
'\/\/' . str_replace( '/', '\/', $this->sourceHostname ), // Escaped \/ used by revslider and several visual editors
|
304 |
'//' . $this->sourceHostname,
|
305 |
+
//rtrim( ABSPATH, '/' ), // This lead to errors if ABSPATH is /www/ because this would break external links like https://www.domain.com to https://www/replace-string/domain.com
|
306 |
+
ABSPATH
|
307 |
);
|
308 |
|
309 |
$args['replace_with'] = array(
|
310 |
'%2F%2F' . str_replace( '/', '%2F', $this->destinationHostname ),
|
311 |
'\/\/' . str_replace( '/', '\/', $this->destinationHostname ),
|
312 |
'//' . $this->destinationHostname,
|
313 |
+
//rtrim( $this->options->destinationDir, '/' ),
|
314 |
+
$this->options->destinationDir
|
315 |
);
|
316 |
|
317 |
+
$this->debugLog( "DB Search & Replace: Search: {$args['search_for'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
318 |
+
$this->debugLog( "DB Search & Replace: Replace: {$args['replace_with'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
319 |
+
|
320 |
|
321 |
$args['replace_guids'] = 'off';
|
322 |
$args['dry_run'] = 'off';
|
777 |
return;
|
778 |
}
|
779 |
|
780 |
+
$this->log( "DB Search & Replace: {$new} already exists, dropping it first" );
|
781 |
$this->db->query( "DROP TABLE {$new}" );
|
782 |
}
|
783 |
|
apps/Core/Utils/Cache.php
CHANGED
@@ -4,7 +4,7 @@ namespace WPStaging\Utils;
|
|
4 |
|
5 |
// No Direct Access
|
6 |
if( !defined( "WPINC" ) ) {
|
7 |
-
|
8 |
}
|
9 |
|
10 |
use WPStaging\WPStaging;
|
@@ -15,187 +15,188 @@ use WPStaging\WPStaging;
|
|
15 |
*/
|
16 |
class Cache {
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
|
|
200 |
|
201 |
}
|
4 |
|
5 |
// No Direct Access
|
6 |
if( !defined( "WPINC" ) ) {
|
7 |
+
die;
|
8 |
}
|
9 |
|
10 |
use WPStaging\WPStaging;
|
15 |
*/
|
16 |
class Cache {
|
17 |
|
18 |
+
/**
|
19 |
+
* Cache directory (full path)
|
20 |
+
* @var string
|
21 |
+
*/
|
22 |
+
private $cacheDir;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Cache file extension
|
26 |
+
* @var string
|
27 |
+
*/
|
28 |
+
private $cacheExtension = "cache";
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Lifetime of cache files in seconds
|
32 |
+
* @var int
|
33 |
+
*/
|
34 |
+
private $lifetime = 2592000; // 30 days
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Cache constructor.
|
38 |
+
* @param null|int $lifetime
|
39 |
+
* @param null|string $cacheDir
|
40 |
+
* @param null|string $cacheExtension
|
41 |
+
* @throws \Exception
|
42 |
+
*/
|
43 |
+
|
44 |
+
public function __construct( $lifetime = null, $cacheDir = null, $cacheExtension = null ) {
|
45 |
+
// Set lifetime
|
46 |
+
$lifetime = ( int ) $lifetime;
|
47 |
+
if( $lifetime > 0 ) {
|
48 |
+
$this->lifetime = $lifetime;
|
49 |
+
}
|
50 |
+
|
51 |
+
// Set cache directory
|
52 |
+
if( !empty( $cacheDir ) && is_dir( $cacheDir ) ) {
|
53 |
+
$this->cacheDir = $cacheDir;
|
54 |
+
}
|
55 |
+
// Set default
|
56 |
+
else {
|
57 |
+
|
58 |
+
$this->cacheDir = \WPStaging\WPStaging::getContentDir();
|
59 |
+
}
|
60 |
+
|
61 |
+
// Set cache extension
|
62 |
+
if( !empty( $cacheExtension ) ) {
|
63 |
+
$this->cacheExtension = $cacheExtension;
|
64 |
+
}
|
65 |
+
|
66 |
+
// If cache directory doesn't exists, create it
|
67 |
+
if( !is_dir( $this->cacheDir ) && !@mkdir( $this->cacheDir, 0775, true ) ) {
|
68 |
+
throw new \Exception( "Failed to create cache directory " . $this->cacheDir . '! Make sure folder permission is 755 and owner is correct. Should be www-data or similar.' );
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Get cache
|
74 |
+
* @param string $cacheFileName
|
75 |
+
* @param mixed $defaultValue
|
76 |
+
* @param null|int $lifetime
|
77 |
+
* @return mixed|null
|
78 |
+
*/
|
79 |
+
public function get( $cacheFileName, $defaultValue = null, $lifetime = null ) {
|
80 |
+
// Check if file is valid
|
81 |
+
if( false === ($cacheFile = $this->isValid( $cacheFileName, true, $lifetime )) ) {
|
82 |
+
return $defaultValue;
|
83 |
+
}
|
84 |
+
|
85 |
+
return @unserialize( file_get_contents( $cacheFile ) );
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Saves value to given cache file
|
90 |
+
* @param string $cacheFileName
|
91 |
+
* @param mixed $value
|
92 |
+
* @return bool
|
93 |
+
* @throws \Exception
|
94 |
+
*/
|
95 |
+
public function save( $cacheFileName, $value ) {
|
96 |
+
$cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
|
97 |
+
|
98 |
+
// Attempt to delete cache file if it exists
|
99 |
+
if( is_file( $cacheFile ) && !@unlink( $cacheFile ) ) {
|
100 |
+
$this->returnException( "Can't delete existing cache file" );
|
101 |
+
throw new \Exception( "Can't delete existing cache file" );
|
102 |
+
}
|
103 |
+
|
104 |
+
try {
|
105 |
+
|
106 |
+
// Save it to file
|
107 |
+
if( !wpstg_put_contents( $cacheFile, @serialize( $value ) ) ) {
|
108 |
+
$this->returnException( " Can't save data to: " . $cacheFile . " Disk quota exceeded or not enough free disk space left" );
|
109 |
+
return false;
|
110 |
+
}
|
111 |
+
} catch ( Exception $e ) {
|
112 |
+
$this->returnException( " Can't save data to: " . $cacheFile . " Error: " . $e );
|
113 |
+
return false;
|
114 |
+
}
|
115 |
+
return true;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Checks if file is valid or not
|
120 |
+
* @param $cacheFileName
|
121 |
+
* @param bool $deleteFileIfInvalid
|
122 |
+
* @param null|int $lifetime
|
123 |
+
* @return string|bool
|
124 |
+
* @throws \Exception
|
125 |
+
*/
|
126 |
+
public function isValid( $cacheFileName, $deleteFileIfInvalid = false, $lifetime = null ) {
|
127 |
+
// Lifetime
|
128 |
+
$lifetime = ( int ) $lifetime;
|
129 |
+
if( -1 > $lifetime || 0 == $lifetime ) {
|
130 |
+
$lifetime = $this->lifetime;
|
131 |
+
}
|
132 |
+
|
133 |
+
// Get full path of the given cache file
|
134 |
+
$cacheFile = $this->cacheDir . $cacheFileName . '.' . $this->cacheExtension;
|
135 |
+
|
136 |
+
// File doesn't exist
|
137 |
+
if( !is_file( $cacheFile ) ) {
|
138 |
+
return false;
|
139 |
+
}
|
140 |
+
|
141 |
+
// As long as file exists, don't check lifetime
|
142 |
+
if( -1 == $lifetime ) {
|
143 |
+
return $cacheFile;
|
144 |
+
}
|
145 |
+
|
146 |
+
// Time is up, file is invalid
|
147 |
+
if( time() - filemtime( $cacheFile ) >= $lifetime ) {
|
148 |
+
|
149 |
+
// Attempt to delete the file
|
150 |
+
if( $deleteFileIfInvalid === true && !@unlink( $cacheFile ) ) {
|
151 |
+
throw new \Exception( "Attempting to delete invalid cache file has failed!" );
|
152 |
+
}
|
153 |
+
|
154 |
+
// No need to delete the file, return
|
155 |
+
return false;
|
156 |
+
}
|
157 |
+
|
158 |
+
return $cacheFile;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Delete a cache file
|
163 |
+
* @param string $cacheFileName
|
164 |
+
* @return bool
|
165 |
+
* @throws \Exception
|
166 |
+
*/
|
167 |
+
public function delete( $cacheFileName ) {
|
168 |
+
if( false !== ($cacheFile = $this->isValid( $cacheFileName, true )) && false === @unlink( $cacheFile ) ) {
|
169 |
+
throw new \Exception( "Couldn't delete cache: {$cacheFileName}. Full Path: {$cacheFile}" );
|
170 |
+
}
|
171 |
+
|
172 |
+
return true;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* @return string
|
177 |
+
*/
|
178 |
+
public function getCacheDir() {
|
179 |
+
return $this->cacheDir;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* @return string
|
184 |
+
*/
|
185 |
+
public function getCacheExtension() {
|
186 |
+
return $this->cacheExtension;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Throw a errror message via json and stop further execution
|
191 |
+
* @param string $message
|
192 |
+
*/
|
193 |
+
protected function returnException( $message = '' ) {
|
194 |
+
wp_die( json_encode( array(
|
195 |
+
'job' => isset( $this->options->currentJob ) ? $this->options->currentJob : '',
|
196 |
+
'status' => false,
|
197 |
+
'message' => $message,
|
198 |
+
'error' => true
|
199 |
+
) ) );
|
200 |
+
}
|
201 |
|
202 |
}
|
apps/Core/Utils/Logger.php
CHANGED
@@ -101,6 +101,7 @@ class Logger
|
|
101 |
*/
|
102 |
public function add($message, $type = self::TYPE_ERROR)
|
103 |
{
|
|
|
104 |
$this->messages[] = array(
|
105 |
"type" => $type,
|
106 |
"date" => date("Y/m/d H:i:s"),
|
@@ -145,7 +146,8 @@ class Logger
|
|
145 |
return true;
|
146 |
}
|
147 |
|
148 |
-
return (@file_put_contents($this->getLogFile(), $messageString, FILE_APPEND | LOCK_EX));
|
|
|
149 |
}
|
150 |
|
151 |
/**
|
101 |
*/
|
102 |
public function add($message, $type = self::TYPE_ERROR)
|
103 |
{
|
104 |
+
|
105 |
$this->messages[] = array(
|
106 |
"type" => $type,
|
107 |
"date" => date("Y/m/d H:i:s"),
|
146 |
return true;
|
147 |
}
|
148 |
|
149 |
+
//return (@file_put_contents($this->getLogFile(), $messageString, FILE_APPEND | LOCK_EX));
|
150 |
+
return (@file_put_contents($this->getLogFile(), $messageString, FILE_APPEND));
|
151 |
}
|
152 |
|
153 |
/**
|
apps/Core/Utils/functions.php
CHANGED
@@ -166,6 +166,12 @@ function wpstg_urldecode( $data ) {
|
|
166 |
* @return bool
|
167 |
*/
|
168 |
function wpstg_is_stagingsite() {
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
if( "true" === get_option( "wpstg_is_staging_site" ) ) {
|
170 |
return true;
|
171 |
}
|
@@ -254,3 +260,199 @@ function wpstg_get_abs_upload_dir() {
|
|
254 |
|
255 |
return $uploadsAbsPath;
|
256 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
166 |
* @return bool
|
167 |
*/
|
168 |
function wpstg_is_stagingsite() {
|
169 |
+
|
170 |
+
if( file_exists( ABSPATH . '.wp-staging-cloneable' ) ) {
|
171 |
+
return false;
|
172 |
+
}
|
173 |
+
|
174 |
+
|
175 |
if( "true" === get_option( "wpstg_is_staging_site" ) ) {
|
176 |
return true;
|
177 |
}
|
260 |
|
261 |
return $uploadsAbsPath;
|
262 |
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Get hostname of production site including scheme
|
266 |
+
* @return string
|
267 |
+
*/
|
268 |
+
function wpstg_get_production_hostname() {
|
269 |
+
|
270 |
+
$connection = get_option( 'wpstg_connection' );
|
271 |
+
|
272 |
+
// Get the stored hostname
|
273 |
+
if( !empty( $connection['prodHostname'] ) ) {
|
274 |
+
return $connection['prodHostname'];
|
275 |
+
}
|
276 |
+
|
277 |
+
// Default. Try to get the hostname from the main domain (Workaround for WP Staging Pro older < 2.9.1)
|
278 |
+
$siteurl = get_site_url();
|
279 |
+
$result = parse_url( $siteurl );
|
280 |
+
return $result['scheme'] . "://" . $result['host'];
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Check if string starts with specific string
|
285 |
+
* @param string $haystack
|
286 |
+
* @param string $needle
|
287 |
+
* @return bool
|
288 |
+
*/
|
289 |
+
function wpstg_starts_with( $haystack, $needle ) {
|
290 |
+
$length = strlen( $needle );
|
291 |
+
return (substr( $haystack, 0, $length ) === $needle);
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Check if folder is empty
|
296 |
+
* @param type $dir
|
297 |
+
* @return boolean
|
298 |
+
*/
|
299 |
+
function wpstg_is_empty_dir( $dir ) {
|
300 |
+
if( !is_dir( $dir ) ) {
|
301 |
+
return true;
|
302 |
+
}
|
303 |
+
$iterator = new \FilesystemIterator( $dir );
|
304 |
+
if( $iterator->valid() ) {
|
305 |
+
return false;
|
306 |
+
}
|
307 |
+
return true;
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* Get absolute WP uploads path e.g.
|
312 |
+
* Multisites: /var/www/htdocs/example.com/wp-content/uploads/sites/1 or /var/www/htdocs/example.com/wp-content/blogs.dir/1/files
|
313 |
+
* Single sites: /var/www/htdocs/example.com/wp-content/uploads
|
314 |
+
* @return string
|
315 |
+
*/
|
316 |
+
function wpstg_get_upload_dir() {
|
317 |
+
$uploads = wp_upload_dir( null, false );
|
318 |
+
|
319 |
+
$baseDir = wpstg_replace_windows_directory_separator( $uploads['basedir'] );
|
320 |
+
|
321 |
+
// If multisite (and if not the main site in a post-MU network)
|
322 |
+
if( is_multisite() && !( is_main_network() && is_main_site() && defined( 'MULTISITE' ) ) ) {
|
323 |
+
// blogs.dir is used on WP 3.5 and earlier
|
324 |
+
if( false !== strpos( $baseDir, 'blogs.dir' ) ) {
|
325 |
+
// remove this piece from the basedir: /blogs.dir/2/files
|
326 |
+
$uploadDir = wpstg_replace_first_match( '/blogs.dir/' . get_current_blog_id() . '/files', null, $baseDir );
|
327 |
+
$dir = wpstg_replace_windows_directory_separator( $uploadDir . '/blogs.dir' );
|
328 |
+
} else {
|
329 |
+
// remove this piece from the basedir: /sites/2
|
330 |
+
$uploadDir = wpstg_replace_first_match( '/sites/' . get_current_blog_id(), null, $baseDir );
|
331 |
+
$dir = wpstg_replace_windows_directory_separator( $uploadDir . '/sites' );
|
332 |
+
}
|
333 |
+
|
334 |
+
|
335 |
+
return $dir;
|
336 |
+
}
|
337 |
+
return false;
|
338 |
+
}
|
339 |
+
|
340 |
+
/**
|
341 |
+
* Write data to file
|
342 |
+
* An alternative function for file_put_contents which is disabled on some hosts
|
343 |
+
*
|
344 |
+
* @param string $file
|
345 |
+
* @param string $contents
|
346 |
+
* @param int | false $mode
|
347 |
+
* @return boolean
|
348 |
+
*/
|
349 |
+
function wpstg_put_contents( $file, $contents, $mode = false ) {
|
350 |
+
$fp = @fopen( $file, 'wb' );
|
351 |
+
|
352 |
+
if( !$fp ) {
|
353 |
+
return false;
|
354 |
+
}
|
355 |
+
|
356 |
+
mbstring_binary_safe_encoding();
|
357 |
+
|
358 |
+
$data_length = strlen( $contents );
|
359 |
+
|
360 |
+
$bytes_written = fwrite( $fp, $contents );
|
361 |
+
|
362 |
+
reset_mbstring_encoding();
|
363 |
+
|
364 |
+
fclose( $fp );
|
365 |
+
|
366 |
+
if( $data_length !== $bytes_written ) {
|
367 |
+
return false;
|
368 |
+
}
|
369 |
+
|
370 |
+
wpstg_chmod( $file, $mode );
|
371 |
+
|
372 |
+
return true;
|
373 |
+
}
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Change chmod of file or folder
|
377 |
+
* @param string $file path to file
|
378 |
+
* @param mixed $mode false or specific octal value like 0755
|
379 |
+
* @param type $recursive
|
380 |
+
* @return boolean
|
381 |
+
*/
|
382 |
+
function wpstg_chmod( $file, $mode = false ) {
|
383 |
+
if( !$mode ) {
|
384 |
+
if( @is_file( $file ) ) {
|
385 |
+
if( defined( 'FS_CHMOD_FILE' ) ) {
|
386 |
+
$mode = FS_CHMOD_FILE;
|
387 |
+
} else {
|
388 |
+
$mode = ( int ) 0644;
|
389 |
+
}
|
390 |
+
} elseif( @is_dir( $file ) ) {
|
391 |
+
if( defined( 'FS_CHMOD_FILE' ) ) {
|
392 |
+
$mode = FS_CHMOD_DIR;
|
393 |
+
} else {
|
394 |
+
$mode = ( int ) 0755;
|
395 |
+
}
|
396 |
+
} else {
|
397 |
+
return false;
|
398 |
+
}
|
399 |
+
}
|
400 |
+
|
401 |
+
if( !@is_dir( $file ) ) {
|
402 |
+
return @chmod( $file, $mode );
|
403 |
+
}
|
404 |
+
|
405 |
+
return true;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Create file if it does not exist
|
410 |
+
*
|
411 |
+
* @param string $path
|
412 |
+
* @param (int|false) $chmod The permissions as octal number (or false to skip chmod)
|
413 |
+
* @param (string|int) $chown A user name or number (or false to skip chown).
|
414 |
+
* @return boolean true on success, false on failure.
|
415 |
+
*/
|
416 |
+
function wpstg_mkdir( $path, $chmod = false, $chown = false ) {
|
417 |
+
// Safe mode fails with a trailing slash under certain PHP versions.
|
418 |
+
$path = untrailingslashit( $path );
|
419 |
+
if( empty( $path ) ) {
|
420 |
+
return false;
|
421 |
+
}
|
422 |
+
|
423 |
+
if( !$chmod ) {
|
424 |
+
$chmod = FS_CHMOD_DIR;
|
425 |
+
}
|
426 |
+
|
427 |
+
if( !@mkdir( $path ) ) {
|
428 |
+
return false;
|
429 |
+
}
|
430 |
+
wpstg_chmod( $path, $chmod );
|
431 |
+
|
432 |
+
if( $chown ) {
|
433 |
+
wpstg_chown( $path, $chown );
|
434 |
+
}
|
435 |
+
|
436 |
+
return true;
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* Changes the owner of a file or directory.
|
441 |
+
*
|
442 |
+
*
|
443 |
+
* @param string $file Path to the file or directory.
|
444 |
+
* @param string|int $owner A user name or number.
|
445 |
+
* @param bool $recursive Optional. If set to true, changes file owner recursively.
|
446 |
+
* Default false.
|
447 |
+
* @return bool True on success, false on failure.
|
448 |
+
*/
|
449 |
+
function wpstg_chown( $file, $owner ) {
|
450 |
+
if( !@file_exists( $file ) ) {
|
451 |
+
return false;
|
452 |
+
}
|
453 |
+
|
454 |
+
if( !@is_dir( $file ) ) {
|
455 |
+
return @chown( $file, $owner );
|
456 |
+
}
|
457 |
+
return true;
|
458 |
+
}
|
apps/Core/WPStaging.php
CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
|
|
29 |
/**
|
30 |
* Plugin version
|
31 |
*/
|
32 |
-
const VERSION = "2.6.
|
33 |
|
34 |
/**
|
35 |
* Plugin name
|
@@ -44,7 +44,7 @@ final class WPStaging {
|
|
44 |
/**
|
45 |
* Compatible WP Version
|
46 |
*/
|
47 |
-
const WP_COMPATIBLE = "5.2.
|
48 |
|
49 |
/**
|
50 |
* Slug: Either wp-staging or wp-staging-pro
|
29 |
/**
|
30 |
* Plugin version
|
31 |
*/
|
32 |
+
const VERSION = "2.6.3";
|
33 |
|
34 |
/**
|
35 |
* Plugin name
|
44 |
/**
|
45 |
* Compatible WP Version
|
46 |
*/
|
47 |
+
const WP_COMPATIBLE = "5.2.4";
|
48 |
|
49 |
/**
|
50 |
* Slug: Either wp-staging or wp-staging-pro
|
readme.txt
CHANGED
@@ -9,68 +9,70 @@ 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.
|
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.
|
16 |
|
17 |
== Description ==
|
18 |
|
19 |
-
<strong>This cloning and staging plugin is well tested and work in progress. <br>
|
20 |
-
If you find an issue, please open a [support ticket](https://wp-staging.com/support/ "support ticket").
|
21 |
-
</strong>
|
22 |
-
<br /><br />
|
23 |
-
<strong>Note: </strong> For pushing & migrating plugins and theme files to live site, check out the pro edition [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
|
24 |
-
<br /><br />
|
25 |
-
|
26 |
<h3>WP Staging for WordPress Migration & Cloning </h3>
|
27 |
-
This duplicator plugin allows you to create an staging or development
|
|
|
28 |
It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
|
29 |
<br /> <br />
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
<br /> <br />
|
34 |
-
All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
|
35 |
<br /><br />
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
|
42 |
[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
|
43 |
|
|
|
|
|
44 |
= Main Features =
|
45 |
|
46 |
-
*
|
47 |
-
*
|
48 |
-
*
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
*
|
53 |
-
*
|
54 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
-
|
57 |
|
58 |
= Additional Features WP Staging Pro Edition =
|
59 |
|
60 |
-
*
|
61 |
-
*
|
62 |
* Clone your website into a subdomain
|
63 |
* Specify certain user roles for accessing the staging site
|
64 |
-
* Copy all modifications
|
65 |
|
66 |
<strong>Change your workflow of updating themes and plugins data:</strong>
|
67 |
|
68 |
1. Use WP Staging to clone a production website for staging, testing or backup purposes
|
69 |
2. Create a backup of your website
|
70 |
-
3. Customize theme, configuration,
|
71 |
-
4. Test everything on your staging site
|
72 |
-
5.
|
73 |
-
|
74 |
|
75 |
<h3> Why should I use a staging website? </h3>
|
76 |
|
@@ -151,6 +153,17 @@ https://wp-staging.com
|
|
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
|
@@ -245,18 +258,16 @@ https://wp-staging.com
|
|
245 |
* Tweak: clean up search & replace method
|
246 |
* Tweak: Better FAQ
|
247 |
|
248 |
-
= 2.4.8 =
|
249 |
-
* Fix: Prevent throwing an error when table prefix of table usermeta cannot be changed
|
250 |
-
* Fix: WP Staging does not run with old WordPress version 3.2
|
251 |
-
* Fix: Do not show rating box on posts and pages editor
|
252 |
-
* New: Support WordPress 5.0.3
|
253 |
-
* New: Add FAQ to footer
|
254 |
-
|
255 |
-
= 2.4.7 =
|
256 |
-
* New: Support WordPress 5.0.2
|
257 |
|
258 |
-
|
259 |
-
* Fix: Can not login to staging site . Changed minimum user capability to 'manage_options' instead 'administrator'
|
260 |
|
|
|
|
|
261 |
|
262 |
-
|
|
|
|
|
|
|
|
|
|
|
|
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.3
|
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.
|
16 |
|
17 |
== Description ==
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
<h3>WP Staging for WordPress Migration & Cloning </h3>
|
20 |
+
This duplicator plugin allows you to create an exact copy of your entire website for staging or development purposes in seconds.
|
21 |
+
(Exact time depends on the size of your website)<br /><br />
|
22 |
It creates a clone of your website into a subfolder of your main WordPress installation including an entire copy of your database.
|
23 |
<br /> <br />
|
24 |
+
<strong>Note: </strong> For pushing & migrating plugins and theme files to live site, check out the pro edition [https://wp-staging.com/](https://wp-staging.com/ "WP Staging Pro")
|
25 |
+
<br /><br />
|
26 |
+
All the time-consumptive database and file copying operations are done in the background. The plugin even automatically does an entire search & replace of all serialized links and paths.
|
|
|
|
|
27 |
<br /><br />
|
28 |
+
This plugin works even on the smallest shared hosting servers.
|
29 |
+
<br /><br />
|
30 |
+
<br /><br />
|
31 |
+
WP Staging can help you to protect your website from being broken or unavailable because of installing untested plugin updates!
|
|
|
32 |
|
33 |
[youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
|
34 |
|
35 |
+
<p>Note: WordPress 5.x has been shipped with a new visual editor called Gutenberg. Use WP Staging to check if Gutenberg editor is working as intended on your website and that all used plugins are compatible with that new editor.</p>
|
36 |
+
|
37 |
= Main Features =
|
38 |
|
39 |
+
* No SASS service. All data belongs to you and stays on your server.
|
40 |
+
* Easy to use! Just install it and click on "CREATE NEW STAGING SITE".
|
41 |
+
* No server timeouts on huge websites or/and small hosting servers
|
42 |
+
* Staging website will be available from example.com/staging-site
|
43 |
+
* Very fast - Migration process takes only a few seconds or minutes, depending on the website's size and server I/O power.
|
44 |
+
* Only administrators can access the staging website. Login with the same credentials you use on your production site.
|
45 |
+
* SEO friendly: The staging website is unavailable to search engines due to a custom login prompt and no-index header.
|
46 |
+
* The admin bar on the staging website will be orange colored and shows clearly that you work on the staging site.
|
47 |
+
* All database tables will be duplicated and get a new prefix beginning with wpstg(int)_.
|
48 |
+
* As default the staging website will be copied into a subfolder.
|
49 |
+
* Extensive logging features
|
50 |
+
* Supports all main web servers including Apache, Nginx, and Microsoft IIS
|
51 |
+
* <strong>[Premium]: </strong>Choose a separate database and select a custom directory for cloning
|
52 |
+
* <strong>[Premium]: </strong>Make the staging website available from a subdomain like dev.example.com
|
53 |
+
* <strong>[Premium]: </strong>Push & migrate plugins and themes to production website.
|
54 |
+
* <strong>[Premium]: </strong>Define user roles that should receive access to the staging site only. For instance, clients or external developers.
|
55 |
+
* <strong>[Premium]: </strong>Migration and cloning of WordPress multisites
|
56 |
+
|
57 |
+
> Note: Some features are Premium. Which means you need WP Staging Pro to use those features. You can [get WP Staging Premium here](https://wp-staging.com)!
|
58 |
|
59 |
+
* New: Compatible with WordFence & All In One WP Security & Firewall
|
60 |
|
61 |
= Additional Features WP Staging Pro Edition =
|
62 |
|
63 |
+
* Cloning and migration of WordPress multisites
|
64 |
+
* Define a separate database and a custom directory for cloning
|
65 |
* Clone your website into a subdomain
|
66 |
* Specify certain user roles for accessing the staging site
|
67 |
+
* Copy all modifications from staging site to the production website
|
68 |
|
69 |
<strong>Change your workflow of updating themes and plugins data:</strong>
|
70 |
|
71 |
1. Use WP Staging to clone a production website for staging, testing or backup purposes
|
72 |
2. Create a backup of your website
|
73 |
+
3. Customize theme, configuration, update or install new plugins
|
74 |
+
4. Test everything on your staging site
|
75 |
+
5. If everything works on the staging site start the migration and copy all modifications to your production site!
|
|
|
76 |
|
77 |
<h3> Why should I use a staging website? </h3>
|
78 |
|
153 |
|
154 |
== Changelog ==
|
155 |
|
156 |
+
= 2.6.3 =
|
157 |
+
* New Support for WordPress 5.2.4
|
158 |
+
* New: Allow wildcards for excluding files
|
159 |
+
* New: Add hook "wpstg_clone_action_staging" to execute code on staging site after cloning
|
160 |
+
* Tweak: Improved support for custom uploads folder if user customized UPLOADS constant or upload_path in DB
|
161 |
+
* Fix: Better compatibility with Windows IIS server
|
162 |
+
* Fix: External links are broken after cloning if ABSPATH is equal to /www/
|
163 |
+
* Fix: use an alternative method for file_put_contents as it is not supported on all systems due to file permission issues
|
164 |
+
* Fix: Redundant and duplicated update comments in wp-config.php in staging site
|
165 |
+
|
166 |
+
|
167 |
= 2.6.2 =
|
168 |
* Fix: Do not show warning "Preparing Data Step3: Failed to update rewrite_rules in wpstg0_options"
|
169 |
* Fix: Change error "Table wpstgtmp_options does not exist" to warning
|
258 |
* Tweak: clean up search & replace method
|
259 |
* Tweak: Better FAQ
|
260 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
|
262 |
+
Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
|
|
|
263 |
|
264 |
+
== Upgrade Notice ==
|
265 |
+
* Install this version for supporting latest WordPress version
|
266 |
|
267 |
+
* New: Allow wildcards for excluding files
|
268 |
+
* New: Add hook "wpstg_clone_action_staging" to execute code on staging site after cloning
|
269 |
+
* Tweak: Improved support for custom uploads folder if user customized UPLOADS constant or upload_path in DB
|
270 |
+
* Fix: Better compatibility with Windows IIS server
|
271 |
+
* Fix: External links are broken after cloning if ABSPATH is equal to /www/
|
272 |
+
* Fix: use an alternative method for file_put_contents as it is not supported on all systems due to file permission issues
|
273 |
+
* Fix: Redundant and duplicated update comments in wp-config.php in staging site
|
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.
|
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.
|
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.3
|
11 |
* Text Domain: wp-staging
|
12 |
* Domain Path: /languages/
|
13 |
|
51 |
|
52 |
// Version
|
53 |
if( !defined( 'WPSTG_VERSION' ) ) {
|
54 |
+
define( 'WPSTG_VERSION', '2.6.3' );
|
55 |
}
|
56 |
|
57 |
// Must use version of the optimizer
|