Version Description
- New: Compatible up to WordPress 5.2.2
- New: Performance improvement for directory iterator using less server ressources
- New: Add filter wpstg_folder_permission to set a custom folder permission like 0755, allows to overwrite FS_CHMOD_DIR if it has been defined.
- Fix: Error conditions in class Data does not compare type strict (=
Download this release
Release Info
Developer | ReneHermi |
Plugin | WP Staging – DB & File Duplicator & Migration |
Version | 2.6.0 |
Comparing to | |
See all releases |
Code changes from version 2.5.8 to 2.6.0
- apps/Backend/Modules/Jobs/Cloning.php +2 -1
- apps/Backend/Modules/Jobs/Data.php +247 -32
- apps/Backend/Modules/Jobs/Database.php +3 -1
- apps/Backend/Modules/Jobs/Delete.php +51 -42
- apps/Backend/Modules/Jobs/Directories.php +30 -17
- apps/Backend/Modules/Jobs/Finish.php +3 -0
- apps/Backend/Modules/Jobs/Multisite/Data.php +1 -1
- apps/Backend/Modules/Jobs/Multisite/DataExternal.php +1 -1
- apps/Backend/Modules/Jobs/Multisite/Database.php +363 -362
- apps/Backend/Modules/Jobs/Multisite/Directories.php +13 -40
- apps/Backend/Modules/Jobs/Multisite/SearchReplace.php +816 -784
- apps/Backend/Modules/Jobs/Multisite/SearchReplaceExternal.php +840 -818
- apps/Backend/Modules/Jobs/Scan.php +11 -11
- apps/Backend/Modules/Jobs/SearchReplace.php +98 -71
- apps/Backend/Modules/Jobs/Updating.php +8 -7
- apps/Backend/Upgrade/Upgrade.php +22 -0
- apps/Backend/helpers/wp-config.php +57 -0
- apps/Backend/public/css/wpstg-admin.css +57 -10
- apps/Backend/public/js/wpstg-admin.js +7 -6
- apps/Backend/views/clone/ajax/delete-confirmation.php +14 -9
- apps/Backend/views/clone/ajax/scan.php +7 -6
- apps/Backend/views/clone/ajax/single-overview.php +31 -26
- apps/Backend/views/clone/ajax/start.php +1 -1
- apps/Backend/views/clone/index.php +6 -4
- apps/Backend/views/clone/staging-site/index.php +4 -0
- apps/Core/Iterators/RecursiveFilterExclude.php +6 -4
- apps/Core/Utils/functions.php +36 -5
- apps/Core/WPStaging.php +3 -3
- readme.txt +23 -11
- wp-staging.php +2 -2
apps/Backend/Modules/Jobs/Cloning.php
CHANGED
@@ -33,7 +33,8 @@ class Cloning extends Job {
|
|
33 |
|
34 |
// Generate Options
|
35 |
// Clone
|
36 |
-
|
|
|
37 |
$this->options->cloneDirectoryName = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
|
38 |
$this->options->cloneNumber = 1;
|
39 |
$this->options->prefix = $this->setStagingPrefix();
|
33 |
|
34 |
// Generate Options
|
35 |
// Clone
|
36 |
+
//$this->options->clone = $_POST["cloneID"];
|
37 |
+
$this->options->clone = preg_replace( "#\W+#", '-', strtolower( $_POST["cloneID"] ) );
|
38 |
$this->options->cloneDirectoryName = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
|
39 |
$this->options->cloneNumber = 1;
|
40 |
$this->options->prefix = $this->setStagingPrefix();
|
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 |
/**
|
@@ -176,7 +176,7 @@ class Data extends JobExecutable {
|
|
176 |
*/
|
177 |
protected function isTable( $table ) {
|
178 |
if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
|
179 |
-
$this->log( "Table {$table} does not exist", Logger::TYPE_ERROR );
|
180 |
return false;
|
181 |
}
|
182 |
return true;
|
@@ -196,30 +196,141 @@ class Data extends JobExecutable {
|
|
196 |
|
197 |
$destination = $this->options->destinationDir . 'wp-config.php';
|
198 |
|
199 |
-
|
200 |
-
|
201 |
-
if( (!is_file( $source ) && !is_link( $source )) || is_file( $destination ) ) {
|
202 |
-
$this->log( "Preparing Data Step0: Skip it", Logger::TYPE_INFO );
|
203 |
return true;
|
204 |
}
|
205 |
|
206 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
if( is_link( $source ) ) {
|
208 |
-
$this->log( "Preparing Data
|
209 |
if( !@copy( readlink( $source ), $destination ) ) {
|
210 |
$errors = error_get_last();
|
211 |
-
$this->log( "Preparing Data
|
212 |
-
return
|
213 |
}
|
214 |
}
|
215 |
|
216 |
-
// Copy file
|
217 |
if( !@copy( $source, $destination ) ) {
|
218 |
$errors = error_get_last();
|
219 |
-
$this->log( "Preparing Data Step0: Failed to copy
|
220 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
}
|
222 |
-
$this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
|
223 |
return true;
|
224 |
}
|
225 |
|
@@ -251,11 +362,11 @@ class Data extends JobExecutable {
|
|
251 |
|
252 |
|
253 |
// All good
|
254 |
-
if(
|
255 |
-
$this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_WARNING );
|
256 |
return true;
|
257 |
}
|
258 |
|
|
|
259 |
return true;
|
260 |
}
|
261 |
|
@@ -299,12 +410,13 @@ class Data extends JobExecutable {
|
|
299 |
);
|
300 |
//}
|
301 |
// All good
|
302 |
-
if(
|
303 |
-
$this->log( "Preparing Data Step2:
|
304 |
return true;
|
305 |
}
|
306 |
-
|
307 |
-
|
|
|
308 |
}
|
309 |
|
310 |
/**
|
@@ -317,6 +429,7 @@ class Data extends JobExecutable {
|
|
317 |
|
318 |
// Skip - Table does not exist
|
319 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
|
|
320 |
return true;
|
321 |
}
|
322 |
|
@@ -333,11 +446,11 @@ class Data extends JobExecutable {
|
|
333 |
);
|
334 |
|
335 |
// All good
|
336 |
-
if(
|
337 |
-
$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
338 |
return true;
|
339 |
}
|
340 |
|
|
|
341 |
return true;
|
342 |
}
|
343 |
|
@@ -373,6 +486,8 @@ class Data extends JobExecutable {
|
|
373 |
return false;
|
374 |
}
|
375 |
|
|
|
|
|
376 |
return true;
|
377 |
}
|
378 |
|
@@ -449,7 +564,7 @@ class Data extends JobExecutable {
|
|
449 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
450 |
return false;
|
451 |
}
|
452 |
-
$this->Log( "Preparing Data
|
453 |
return true;
|
454 |
}
|
455 |
|
@@ -463,6 +578,7 @@ class Data extends JobExecutable {
|
|
463 |
|
464 |
// Skip - Table does not exist
|
465 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
|
|
466 |
return true;
|
467 |
}
|
468 |
|
@@ -595,7 +711,7 @@ class Data extends JobExecutable {
|
|
595 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
596 |
return false;
|
597 |
}
|
598 |
-
$this->Log( "Preparing Data
|
599 |
return true;
|
600 |
}
|
601 |
|
@@ -626,7 +742,7 @@ class Data extends JobExecutable {
|
|
626 |
$replace.= " // Changed by WP-Staging";
|
627 |
|
628 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
629 |
-
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
|
630 |
return false;
|
631 |
}
|
632 |
} else {
|
@@ -635,10 +751,10 @@ class Data extends JobExecutable {
|
|
635 |
|
636 |
|
637 |
if( false === @file_put_contents( $path, $content ) ) {
|
638 |
-
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
|
639 |
return false;
|
640 |
}
|
641 |
-
$this->Log( "Preparing Data
|
642 |
return true;
|
643 |
}
|
644 |
|
@@ -755,7 +871,7 @@ class Data extends JobExecutable {
|
|
755 |
$this->returnException( " Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
756 |
return false;
|
757 |
}
|
758 |
-
$this->Log( "Preparing Data
|
759 |
return true;
|
760 |
}
|
761 |
|
@@ -839,7 +955,7 @@ class Data extends JobExecutable {
|
|
839 |
$this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
840 |
return false;
|
841 |
}
|
842 |
-
$this->Log( "Preparing Data
|
843 |
return true;
|
844 |
}
|
845 |
|
@@ -879,7 +995,7 @@ class Data extends JobExecutable {
|
|
879 |
$this->log( "Preparing Data Step15: Failed to update WP_CONTENT_DIR. Can't save contents", Logger::TYPE_ERROR );
|
880 |
return false;
|
881 |
}
|
882 |
-
$this->Log( "Preparing Data
|
883 |
return true;
|
884 |
}
|
885 |
|
@@ -919,12 +1035,97 @@ class Data extends JobExecutable {
|
|
919 |
$this->log( "Preparing Data Step16: Failed to update WP_CONTENT_URL. Can't save contents", Logger::TYPE_ERROR );
|
920 |
return false;
|
921 |
}
|
922 |
-
$this->Log( "Preparing Data
|
923 |
return true;
|
924 |
}
|
925 |
|
926 |
/**
|
927 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
928 |
* @return boolean|string
|
929 |
*/
|
930 |
protected function getNewUploadPath() {
|
@@ -934,6 +1135,20 @@ class Data extends JobExecutable {
|
|
934 |
return false;
|
935 |
}
|
936 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
937 |
$customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
|
938 |
|
939 |
$newUploadPath = $this->options->destinationDir . $customSlug;
|
75 |
* @return void
|
76 |
*/
|
77 |
protected function calculateTotalSteps() {
|
78 |
+
$this->options->totalSteps = 18;
|
79 |
}
|
80 |
|
81 |
/**
|
176 |
*/
|
177 |
protected function isTable( $table ) {
|
178 |
if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
|
179 |
+
$this->log( "Preparing Data: Table {$table} does not exist.", Logger::TYPE_ERROR );
|
180 |
return false;
|
181 |
}
|
182 |
return true;
|
196 |
|
197 |
$destination = $this->options->destinationDir . 'wp-config.php';
|
198 |
|
199 |
+
// Check if there is already a valid wp-config.php in root of staging site
|
200 |
+
if( $this->isValidWpConfig( $destination ) ) {
|
|
|
|
|
201 |
return true;
|
202 |
}
|
203 |
|
204 |
+
// Check if there is a valid wp-config.php outside root of wp production site
|
205 |
+
if( $this->isValidWpConfig( $source ) ) {
|
206 |
+
// Copy it to staging site
|
207 |
+
if( $this->copy( $source, $destination ) ) {
|
208 |
+
return true;
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
// No valid wp-config.php found so let's copy wp stagings default wp-config.php to staging site
|
213 |
+
$source = WPSTG_PLUGIN_DIR . "apps/Backend/helpers/wp-config.php";
|
214 |
+
if( $this->copy( $source, $destination ) ) {
|
215 |
+
// add missing db credentials to wp-config.php
|
216 |
+
if( !$this->alterWpConfig( $destination ) ) {
|
217 |
+
$this->log( "Preparing Data Step0: Can not alter db credentials in wp-config.php", Logger::TYPE_INFO );
|
218 |
+
return false;
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
$this->log( "Preparing Data Step0: Successfull", Logger::TYPE_INFO );
|
223 |
+
return true;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Copy files with symlink support
|
228 |
+
* @param type $source
|
229 |
+
* @param type $destination
|
230 |
+
* @return boolean
|
231 |
+
*/
|
232 |
+
protected function copy( $source, $destination ) {
|
233 |
+
// Copy symbolic link
|
234 |
if( is_link( $source ) ) {
|
235 |
+
$this->log( "Preparing Data: Symbolic link found...", Logger::TYPE_INFO );
|
236 |
if( !@copy( readlink( $source ), $destination ) ) {
|
237 |
$errors = error_get_last();
|
238 |
+
$this->log( "Preparing Data: Failed to copy {$source} Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
|
239 |
+
return false;
|
240 |
}
|
241 |
}
|
242 |
|
243 |
+
// Copy file
|
244 |
if( !@copy( $source, $destination ) ) {
|
245 |
$errors = error_get_last();
|
246 |
+
$this->log( "Preparing Data Step0: Failed to copy {$source}! Error: {$errors['message']} {$source} -> {$destination}", Logger::TYPE_ERROR );
|
247 |
+
return false;
|
248 |
+
}
|
249 |
+
|
250 |
+
return true;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Make sure wp-config.php contains correct db credentials
|
255 |
+
* @param type $source
|
256 |
+
* @return boolean
|
257 |
+
*/
|
258 |
+
protected function alterWpConfig( $source ) {
|
259 |
+
$content = file_get_contents( $source );
|
260 |
+
|
261 |
+
if( false === ($content = file_get_contents( $source )) ) {
|
262 |
+
return false;
|
263 |
+
}
|
264 |
+
|
265 |
+
$search = "// ** MySQL settings ** //";
|
266 |
+
|
267 |
+
$replace = "// ** MySQL settings ** //\r\n
|
268 |
+
define( 'DB_NAME', '" . DB_NAME . "' );\r\n
|
269 |
+
/** MySQL database username */\r\n
|
270 |
+
define( 'DB_USER', '" . DB_USER . "' );\r\n
|
271 |
+
/** MySQL database password */\r\n
|
272 |
+
define( 'DB_PASSWORD', '" . DB_PASSWORD . "' );\r\n
|
273 |
+
/** MySQL hostname */\r\n
|
274 |
+
define( 'DB_HOST', '" . DB_HOST . "' );\r\n
|
275 |
+
/** Database Charset to use in creating database tables. */\r\n
|
276 |
+
define( 'DB_CHARSET', '" . DB_CHARSET . "' );\r\n
|
277 |
+
/** The Database Collate type. Don't change this if in doubt. */\r\n
|
278 |
+
define( 'DB_COLLATE', '" . DB_COLLATE . "' );\r\n";
|
279 |
+
|
280 |
+
$content = str_replace( $search, $replace, $content );
|
281 |
+
|
282 |
+
if( false === @file_put_contents( $source, $content ) ) {
|
283 |
+
$this->log( "Preparing Data: Can't save wp-config.php", Logger::TYPE_ERROR );
|
284 |
+
return false;
|
285 |
+
}
|
286 |
+
|
287 |
+
return true;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Check if wp-config.php contains important constants
|
292 |
+
* @param type $source
|
293 |
+
* @return boolean
|
294 |
+
*/
|
295 |
+
protected function isValidWpConfig( $source ) {
|
296 |
+
|
297 |
+
if( !is_file( $source ) && !is_link( $source ) ) {
|
298 |
+
return false;
|
299 |
+
}
|
300 |
+
|
301 |
+
$content = file_get_contents( $source );
|
302 |
+
|
303 |
+
if( false === ($content = file_get_contents( $source )) ) {
|
304 |
+
return false;
|
305 |
+
}
|
306 |
+
|
307 |
+
// Get DB_NAME from wp-config.php
|
308 |
+
preg_match( "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
309 |
+
|
310 |
+
if( empty( $matches[1] ) ) {
|
311 |
+
return false;
|
312 |
+
}
|
313 |
+
|
314 |
+
// Get DB_USER from wp-config.php
|
315 |
+
preg_match( "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
316 |
+
|
317 |
+
if( empty( $matches[1] ) ) {
|
318 |
+
return false;
|
319 |
+
}
|
320 |
+
|
321 |
+
// Get DB_PASSWORD from wp-config.php
|
322 |
+
preg_match( "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
323 |
+
|
324 |
+
if( empty( $matches[1] ) ) {
|
325 |
+
return false;
|
326 |
+
}
|
327 |
+
|
328 |
+
// Get DB_HOST from wp-config.php
|
329 |
+
preg_match( "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
330 |
+
|
331 |
+
if( empty( $matches[1] ) ) {
|
332 |
+
return false;
|
333 |
}
|
|
|
334 |
return true;
|
335 |
}
|
336 |
|
362 |
|
363 |
|
364 |
// All good
|
365 |
+
if( $result ) {
|
|
|
366 |
return true;
|
367 |
}
|
368 |
|
369 |
+
$this->log( "Preparing Data Step1: Skip updating siteurl and homeurl in {$this->prefix}options. Probably already did! {$this->db->last_error}", Logger::TYPE_WARNING );
|
370 |
return true;
|
371 |
}
|
372 |
|
410 |
);
|
411 |
//}
|
412 |
// All good
|
413 |
+
if( $insert ) {
|
414 |
+
$this->log( "Preparing Data Step2: Successfull", Logger::TYPE_INFO );
|
415 |
return true;
|
416 |
}
|
417 |
+
|
418 |
+
$this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
419 |
+
return false;
|
420 |
}
|
421 |
|
422 |
/**
|
429 |
|
430 |
// Skip - Table does not exist
|
431 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
432 |
+
$this->log( "Preparing Data Step3: Skipping" );
|
433 |
return true;
|
434 |
}
|
435 |
|
446 |
);
|
447 |
|
448 |
// All good
|
449 |
+
if( $result ) {
|
|
|
450 |
return true;
|
451 |
}
|
452 |
|
453 |
+
$this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
|
454 |
return true;
|
455 |
}
|
456 |
|
486 |
return false;
|
487 |
}
|
488 |
|
489 |
+
|
490 |
+
|
491 |
return true;
|
492 |
}
|
493 |
|
564 |
$this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
|
565 |
return false;
|
566 |
}
|
567 |
+
$this->Log( "Preparing Data Step6: Finished successfully" );
|
568 |
return true;
|
569 |
}
|
570 |
|
578 |
|
579 |
// Skip - Table does not exist
|
580 |
if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
581 |
+
$this->log( "Preparing Data Step7: Skipping Table {$this->prefix}'options' does not exist" );
|
582 |
return true;
|
583 |
}
|
584 |
|
711 |
$this->log( "Preparing Data Step10: Failed to update WP_HOME. Can't save contents", Logger::TYPE_ERROR );
|
712 |
return false;
|
713 |
}
|
714 |
+
$this->Log( "Preparing Data Step10: Finished successfully" );
|
715 |
return true;
|
716 |
}
|
717 |
|
742 |
$replace.= " // Changed by WP-Staging";
|
743 |
|
744 |
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
745 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL to " . $this->getStagingSiteUrl(), Logger::TYPE_ERROR );
|
746 |
return false;
|
747 |
}
|
748 |
} else {
|
751 |
|
752 |
|
753 |
if( false === @file_put_contents( $path, $content ) ) {
|
754 |
+
$this->log( "Preparing Data Step11: Failed to update WP_SITEURL to " . $this->getStagingSiteUrl() . " Can't save contents", Logger::TYPE_ERROR );
|
755 |
return false;
|
756 |
}
|
757 |
+
$this->Log( "Preparing Data Step11: Finished successfully" );
|
758 |
return true;
|
759 |
}
|
760 |
|
871 |
$this->returnException( " Preparing Data Step12: Failed to update db option_names in {$this->prefix}options. Error: {$this->db->last_error}" );
|
872 |
return false;
|
873 |
}
|
874 |
+
$this->Log( "Preparing Data Step12: Finished successfully" );
|
875 |
return true;
|
876 |
}
|
877 |
|
955 |
$this->log( "Preparing Data Step14: Failed to update WP_CACHE. Can't save contents", Logger::TYPE_ERROR );
|
956 |
return false;
|
957 |
}
|
958 |
+
$this->Log( "Preparing Data Step14: Finished successfully" );
|
959 |
return true;
|
960 |
}
|
961 |
|
995 |
$this->log( "Preparing Data Step15: Failed to update WP_CONTENT_DIR. Can't save contents", Logger::TYPE_ERROR );
|
996 |
return false;
|
997 |
}
|
998 |
+
$this->Log( "Preparing Data Step15: Finished successfully" );
|
999 |
return true;
|
1000 |
}
|
1001 |
|
1035 |
$this->log( "Preparing Data Step16: Failed to update WP_CONTENT_URL. Can't save contents", Logger::TYPE_ERROR );
|
1036 |
return false;
|
1037 |
}
|
1038 |
+
$this->Log( "Preparing Data Step16: Finished successfully" );
|
1039 |
return true;
|
1040 |
}
|
1041 |
|
1042 |
/**
|
1043 |
+
* Remove UPLOADS constant in wp-config.php to reset default image folder
|
1044 |
+
* @return bool
|
1045 |
+
*/
|
1046 |
+
protected function step17() {
|
1047 |
+
$path = $this->options->destinationDir . "wp-config.php";
|
1048 |
+
|
1049 |
+
$this->log( "Preparing Data Step17: Remove UPLOADS in wp-config.php" );
|
1050 |
+
|
1051 |
+
if( false === ($content = file_get_contents( $path )) ) {
|
1052 |
+
$this->log( "Preparing Data Step17: Failed to get UPLOADS in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
|
1053 |
+
return false;
|
1054 |
+
}
|
1055 |
+
|
1056 |
+
|
1057 |
+
// Get UPLOADS from wp-config.php
|
1058 |
+
preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $content, $matches );
|
1059 |
+
|
1060 |
+
if( !empty( $matches[0] ) ) {
|
1061 |
+
|
1062 |
+
$pattern = "/define\s*\(\s*'UPLOADS'\s*,\s*(.*)\s*\);/";
|
1063 |
+
|
1064 |
+
$replace = "";
|
1065 |
+
|
1066 |
+
if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
|
1067 |
+
$this->log( "Preparing Data: Failed to change UPLOADS", Logger::TYPE_ERROR );
|
1068 |
+
return false;
|
1069 |
+
}
|
1070 |
+
} else {
|
1071 |
+
$this->log( "Preparing Data Step17: UPLOADS not defined in wp-config.php. Skipping this step." );
|
1072 |
+
}
|
1073 |
+
|
1074 |
+
if( false === @file_put_contents( $path, $content ) ) {
|
1075 |
+
$this->log( "Preparing Data Step17: Failed to update UPLOADS. Can't save contents", Logger::TYPE_ERROR );
|
1076 |
+
return false;
|
1077 |
+
}
|
1078 |
+
$this->Log( "Preparing Data Step17: Finished successfully" );
|
1079 |
+
return true;
|
1080 |
+
}
|
1081 |
+
|
1082 |
+
/**
|
1083 |
+
* Empty wpstg_existing_clones_beta on the staging site
|
1084 |
+
* @return bool
|
1085 |
+
*/
|
1086 |
+
// protected function step18() {
|
1087 |
+
//
|
1088 |
+
// $this->log( "Preparing Data Step18: Clean up list of staging sites" );
|
1089 |
+
//
|
1090 |
+
// if( false === $this->isTable( $this->prefix . 'options' ) ) {
|
1091 |
+
// return true;
|
1092 |
+
// }
|
1093 |
+
//
|
1094 |
+
// // Skip - Table is not selected or updated
|
1095 |
+
// if( !in_array( $this->prefix . 'options', $this->tables ) ) {
|
1096 |
+
// $this->log( "Preparing Data Step18: Skipping" );
|
1097 |
+
// return true;
|
1098 |
+
// }
|
1099 |
+
//
|
1100 |
+
// $result = $this->db->query(
|
1101 |
+
// $this->db->prepare(
|
1102 |
+
// "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_existing_clones_beta'", 'a:0:{}'
|
1103 |
+
// )
|
1104 |
+
// );
|
1105 |
+
//
|
1106 |
+
// // All good
|
1107 |
+
// if( false === $result ) {
|
1108 |
+
// $this->log( "Preparing Data Step18: Can not update listing of staging sites.", Logger::TYPE_WARNING );
|
1109 |
+
// return true;
|
1110 |
+
// }
|
1111 |
+
//
|
1112 |
+
// $this->Log( "Preparing Data Step18: Finished successfully" );
|
1113 |
+
// return true;
|
1114 |
+
// }
|
1115 |
+
|
1116 |
+
/**
|
1117 |
+
* Create a robots.txt in path of the staging site
|
1118 |
+
*/
|
1119 |
+
// protected function step18() {
|
1120 |
+
// $file = new RobotsTxt();
|
1121 |
+
// if( false === $file->create( $this->options->destinationDir . "robots.txt" ) ) {
|
1122 |
+
// $this->log( "Preparing Data Step18: Failed to create robots.txt", Logger::TYPE_ERROR );
|
1123 |
+
// }
|
1124 |
+
// return true;
|
1125 |
+
// }
|
1126 |
+
|
1127 |
+
/**
|
1128 |
+
* Change upload path of staging site if it has been customized on production site
|
1129 |
* @return boolean|string
|
1130 |
*/
|
1131 |
protected function getNewUploadPath() {
|
1135 |
return false;
|
1136 |
}
|
1137 |
|
1138 |
+
// Get absolute custom upload dir
|
1139 |
+
$uploads = wp_upload_dir();
|
1140 |
+
|
1141 |
+
// Get absolute upload dir from ABSPATH
|
1142 |
+
$uploadsAbsPath = trailingslashit( $uploads['basedir'] );
|
1143 |
+
|
1144 |
+
// Get absolute custom wp-content dir
|
1145 |
+
$wpContentDir = trailingslashit( WP_CONTENT_DIR );
|
1146 |
+
|
1147 |
+
// Do nothing if uploads path is identical to wp-content dir
|
1148 |
+
if( $uploadsAbsPath == $wpContentDir ) {
|
1149 |
+
return false;
|
1150 |
+
}
|
1151 |
+
|
1152 |
$customSlug = str_replace( \WPStaging\WPStaging::getWPpath(), '', $uploadPath );
|
1153 |
|
1154 |
$newUploadPath = $this->options->destinationDir . $customSlug;
|
apps/Backend/Modules/Jobs/Database.php
CHANGED
@@ -120,7 +120,9 @@ class Database extends JobExecutable {
|
|
120 |
$stagingPrefix = $this->options->prefix;
|
121 |
// Make sure prefix of staging site is NEVER identical to prefix of live site!
|
122 |
if( $stagingPrefix == $this->db->prefix ) {
|
123 |
-
|
|
|
|
|
124 |
}
|
125 |
return $stagingPrefix;
|
126 |
}
|
120 |
$stagingPrefix = $this->options->prefix;
|
121 |
// Make sure prefix of staging site is NEVER identical to prefix of live site!
|
122 |
if( $stagingPrefix == $this->db->prefix ) {
|
123 |
+
$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';
|
124 |
+
$this->returnException($error);
|
125 |
+
wp_die( $error );
|
126 |
}
|
127 |
return $stagingPrefix;
|
128 |
}
|
apps/Backend/Modules/Jobs/Delete.php
CHANGED
@@ -18,6 +18,12 @@ class Delete extends Job {
|
|
18 |
*/
|
19 |
private $clone = false;
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
/**
|
22 |
* @var null|object
|
23 |
*/
|
@@ -48,6 +54,8 @@ class Delete extends Job {
|
|
48 |
public function __construct( $isExternal = false ) {
|
49 |
parent::__construct();
|
50 |
$this->isExternal = $isExternal;
|
|
|
|
|
51 |
}
|
52 |
|
53 |
/**
|
@@ -338,24 +346,24 @@ class Delete extends Job {
|
|
338 |
*/
|
339 |
public function deleteDirectory() {
|
340 |
if( $this->isFatalError() ) {
|
341 |
-
$this->returnException( 'Can not delete directory: ' . $this->
|
342 |
-
throw new \Exception( 'Can not delete directory: ' . $this->
|
343 |
}
|
344 |
// Finished or path does not exist
|
345 |
if(
|
346 |
-
empty( $this->
|
347 |
-
$this->
|
348 |
-
!is_dir( $this->
|
349 |
|
350 |
$this->job->current = "finish";
|
351 |
$this->updateJob();
|
352 |
-
return $this->
|
353 |
}
|
354 |
|
355 |
$this->log( "Delete staging site: " . $this->clone->path, Logger::TYPE_INFO );
|
356 |
|
357 |
// Just to make sure the root dir is never deleted!
|
358 |
-
if( $this->
|
359 |
$this->log( "Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL );
|
360 |
$this->returnException( 'Fatal Error 8: Trying to delete root of WP installation!' );
|
361 |
}
|
@@ -366,10 +374,9 @@ class Delete extends Job {
|
|
366 |
return;
|
367 |
}
|
368 |
|
369 |
-
$di = new \RecursiveDirectoryIterator( $this->
|
370 |
$ri = new \RecursiveIteratorIterator( $di, \RecursiveIteratorIterator::CHILD_FIRST );
|
371 |
foreach ( $ri as $file ) {
|
372 |
-
//$file->isDir() ? @rmdir($file) : unlink($file);
|
373 |
$this->deleteFile( $file );
|
374 |
if( $this->isOverThreshold() ) {
|
375 |
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
|
@@ -377,10 +384,24 @@ class Delete extends Job {
|
|
377 |
}
|
378 |
}
|
379 |
|
380 |
-
|
381 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
382 |
}
|
383 |
-
|
|
|
|
|
384 |
}
|
385 |
|
386 |
/**
|
@@ -407,12 +428,12 @@ class Delete extends Job {
|
|
407 |
/**
|
408 |
* @return bool
|
409 |
*/
|
410 |
-
public function isDirectoryDeletingFinished() {
|
411 |
-
return (
|
412 |
-
(false === $this->forceDeleteDirectories && (!isset( $_POST["deleteDir"] ) || '1' !== $_POST["deleteDir"])) ||
|
413 |
-
!is_dir( $this->clone->path ) || ABSPATH === $this->job->nextDirectoryToDelete
|
414 |
-
);
|
415 |
-
}
|
416 |
|
417 |
/**
|
418 |
*
|
@@ -420,7 +441,7 @@ class Delete extends Job {
|
|
420 |
*/
|
421 |
public function isFatalError() {
|
422 |
$homePath = rtrim( get_home_path(), "/" );
|
423 |
-
if( rtrim( $this->
|
424 |
return true;
|
425 |
}
|
426 |
return false;
|
@@ -430,19 +451,26 @@ class Delete extends Job {
|
|
430 |
* Finish / Update Existing Clones
|
431 |
*/
|
432 |
public function deleteFinish() {
|
|
|
|
|
|
|
|
|
|
|
433 |
$existingClones = get_option( "wpstg_existing_clones_beta", array() );
|
434 |
|
435 |
-
// Check if
|
436 |
$this->log( "Verifying existing clones..." );
|
437 |
foreach ( $existingClones as $name => $clone ) {
|
438 |
-
if(
|
439 |
unset( $existingClones[$name] );
|
440 |
}
|
|
|
|
|
|
|
441 |
}
|
442 |
-
$this->log( "Existing clones verified!" );
|
443 |
|
444 |
if( false === update_option( "wpstg_existing_clones_beta", $existingClones ) ) {
|
445 |
-
$this->log( "
|
446 |
}
|
447 |
|
448 |
// Delete cached file
|
@@ -450,26 +478,7 @@ class Delete extends Job {
|
|
450 |
$this->cache->delete( "delete_directories_{$this->clone->name}" );
|
451 |
$this->cache->delete( "clone_options" );
|
452 |
|
453 |
-
//return true;
|
454 |
-
$response = array('delete' => 'finished');
|
455 |
wp_die( json_encode( $response ) );
|
456 |
}
|
457 |
|
458 |
-
/**
|
459 |
-
* Get json response
|
460 |
-
* return json
|
461 |
-
*/
|
462 |
-
private function returnFinish( $message = '' ) {
|
463 |
-
|
464 |
-
$this->deleteFinish();
|
465 |
-
|
466 |
-
wp_die( json_encode( array(
|
467 |
-
'job' => 'delete',
|
468 |
-
'status' => true,
|
469 |
-
'message' => $message,
|
470 |
-
'error' => false,
|
471 |
-
'delete' => 'finished'
|
472 |
-
) ) );
|
473 |
}
|
474 |
-
|
475 |
-
}
|
18 |
*/
|
19 |
private $clone = false;
|
20 |
|
21 |
+
/**
|
22 |
+
* The path to delete
|
23 |
+
* @var type
|
24 |
+
*/
|
25 |
+
private $deleteDir = '';
|
26 |
+
|
27 |
/**
|
28 |
* @var null|object
|
29 |
*/
|
54 |
public function __construct( $isExternal = false ) {
|
55 |
parent::__construct();
|
56 |
$this->isExternal = $isExternal;
|
57 |
+
|
58 |
+
$this->deleteDir = !empty( $_POST['deleteDir'] ) ? urldecode( $_POST['deleteDir'] ) : '';
|
59 |
}
|
60 |
|
61 |
/**
|
346 |
*/
|
347 |
public function deleteDirectory() {
|
348 |
if( $this->isFatalError() ) {
|
349 |
+
$this->returnException( 'Can not delete directory: ' . $this->deleteDir . '. This seems to be the root directory. Exclude this directory from deleting and try again.' );
|
350 |
+
throw new \Exception( 'Can not delete directory: ' . $this->deleteDir . ' This seems to be the root directory. Exclude this directory from deleting and try again.' );
|
351 |
}
|
352 |
// Finished or path does not exist
|
353 |
if(
|
354 |
+
empty( $this->deleteDir ) ||
|
355 |
+
$this->deleteDir == get_home_path() ||
|
356 |
+
!is_dir( $this->deleteDir ) ) {
|
357 |
|
358 |
$this->job->current = "finish";
|
359 |
$this->updateJob();
|
360 |
+
return $this->deleteFinish();
|
361 |
}
|
362 |
|
363 |
$this->log( "Delete staging site: " . $this->clone->path, Logger::TYPE_INFO );
|
364 |
|
365 |
// Just to make sure the root dir is never deleted!
|
366 |
+
if( $this->deleteDir == get_home_path() ) {
|
367 |
$this->log( "Fatal Error 8: Trying to delete root of WP installation!", Logger::TYPE_CRITICAL );
|
368 |
$this->returnException( 'Fatal Error 8: Trying to delete root of WP installation!' );
|
369 |
}
|
374 |
return;
|
375 |
}
|
376 |
|
377 |
+
$di = new \RecursiveDirectoryIterator( $this->deleteDir, \FilesystemIterator::SKIP_DOTS );
|
378 |
$ri = new \RecursiveIteratorIterator( $di, \RecursiveIteratorIterator::CHILD_FIRST );
|
379 |
foreach ( $ri as $file ) {
|
|
|
380 |
$this->deleteFile( $file );
|
381 |
if( $this->isOverThreshold() ) {
|
382 |
//$this->returnException('Maximum PHP execution time exceeded. Run again and repeat the deletion process until it is sucessfully finished.');
|
384 |
}
|
385 |
}
|
386 |
|
387 |
+
// Delete left over staging site root folder
|
388 |
+
@rmdir( $this->deleteDir );
|
389 |
+
|
390 |
+
// Throw fatal error if the folder is still not deleted
|
391 |
+
if( is_dir($this->deleteDir) ) {
|
392 |
+
$clone = ( string ) $this->clone->path;
|
393 |
+
$response = array(
|
394 |
+
'job' => 'delete',
|
395 |
+
'status' => true,
|
396 |
+
'delete' => 'finished',
|
397 |
+
'message' => "Could not the staging site entirely. The folder {$clone}is still not empty. <br/><br/> If this happens again please contact us at support@wp-staging.com",
|
398 |
+
'error' => true,
|
399 |
+
);
|
400 |
+
wp_die( json_encode( $response ) );
|
401 |
}
|
402 |
+
|
403 |
+
// Successfull finish deleting job
|
404 |
+
return $this->deleteFinish();
|
405 |
}
|
406 |
|
407 |
/**
|
428 |
/**
|
429 |
* @return bool
|
430 |
*/
|
431 |
+
// public function isDirectoryDeletingFinished() {
|
432 |
+
// return (
|
433 |
+
// (false === $this->forceDeleteDirectories && (!isset( $_POST["deleteDir"] ) || '1' !== $_POST["deleteDir"])) ||
|
434 |
+
// !is_dir( $this->clone->path ) || ABSPATH === $this->job->nextDirectoryToDelete
|
435 |
+
// );
|
436 |
+
// }
|
437 |
|
438 |
/**
|
439 |
*
|
441 |
*/
|
442 |
public function isFatalError() {
|
443 |
$homePath = rtrim( get_home_path(), "/" );
|
444 |
+
if( rtrim( $this->deleteDir, "/" ) == $homePath ) {
|
445 |
return true;
|
446 |
}
|
447 |
return false;
|
451 |
* Finish / Update Existing Clones
|
452 |
*/
|
453 |
public function deleteFinish() {
|
454 |
+
|
455 |
+
$response = array(
|
456 |
+
'delete' => 'finished',
|
457 |
+
);
|
458 |
+
|
459 |
$existingClones = get_option( "wpstg_existing_clones_beta", array() );
|
460 |
|
461 |
+
// Check if clone exist and then remove it from options
|
462 |
$this->log( "Verifying existing clones..." );
|
463 |
foreach ( $existingClones as $name => $clone ) {
|
464 |
+
if( $clone["path"] == $this->clone->path ) {
|
465 |
unset( $existingClones[$name] );
|
466 |
}
|
467 |
+
// if( !is_dir( $clone["path"] ) ) {
|
468 |
+
// unset( $existingClones[$name] );
|
469 |
+
// }
|
470 |
}
|
|
|
471 |
|
472 |
if( false === update_option( "wpstg_existing_clones_beta", $existingClones ) ) {
|
473 |
+
$this->log( "Delete: Nothing to save.'" );
|
474 |
}
|
475 |
|
476 |
// Delete cached file
|
478 |
$this->cache->delete( "delete_directories_{$this->clone->name}" );
|
479 |
$this->cache->delete( "clone_options" );
|
480 |
|
|
|
|
|
481 |
wp_die( json_encode( $response ) );
|
482 |
}
|
483 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
484 |
}
|
|
|
|
apps/Backend/Modules/Jobs/Directories.php
CHANGED
@@ -120,22 +120,41 @@ class Directories extends JobExecutable {
|
|
120 |
// open file handle
|
121 |
$files = $this->open( $this->filename, 'a' );
|
122 |
|
123 |
-
|
|
|
|
|
|
|
124 |
'cache',
|
125 |
-
'wps-hide-login',
|
126 |
-
'
|
127 |
);
|
128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
try {
|
130 |
|
131 |
// Iterate over content directory
|
132 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
|
133 |
|
134 |
-
// Exclude new line file names Do not use this. Leads to error 500 on some systems
|
135 |
// $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
136 |
-
|
137 |
// Exclude uploads, plugins or themes
|
138 |
-
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_excl_folders', $
|
|
|
139 |
// Recursively iterate over content directory
|
140 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
141 |
|
@@ -144,7 +163,7 @@ class Directories extends JobExecutable {
|
|
144 |
// Write path line
|
145 |
foreach ( $iterator as $item ) {
|
146 |
if( $item->isFile() ) {
|
147 |
-
$wpContentDir = str_replace(ABSPATH, '', WP_CONTENT_DIR);
|
148 |
if( $this->write( $files, $wpContentDir . '/' . $iterator->getSubPathName() . PHP_EOL ) ) {
|
149 |
$this->options->totalFiles++;
|
150 |
|
@@ -156,6 +175,8 @@ class Directories extends JobExecutable {
|
|
156 |
} catch ( \Exception $e ) {
|
157 |
$this->returnException( 'Error: ' . $e->getMessage() );
|
158 |
//throw new \Exception( 'Error: ' . $e->getMessage() );
|
|
|
|
|
159 |
}
|
160 |
|
161 |
// close the file handler
|
@@ -186,7 +207,6 @@ class Directories extends JobExecutable {
|
|
186 |
|
187 |
// Exclude new line file names Do not use this. Leads to error 500 on some systems
|
188 |
// $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
189 |
-
|
190 |
// Recursively iterate over wp-includes directory
|
191 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
192 |
|
@@ -204,10 +224,8 @@ class Directories extends JobExecutable {
|
|
204 |
}
|
205 |
}
|
206 |
} catch ( \Exception $e ) {
|
207 |
-
|
208 |
-
throw new \Exception( 'Error: ' . $e->getMessage() );
|
209 |
-
} catch ( \Exception $e ) {
|
210 |
-
// Skip bad file permissions
|
211 |
}
|
212 |
|
213 |
// close the file handler
|
@@ -238,7 +256,6 @@ class Directories extends JobExecutable {
|
|
238 |
|
239 |
// Exclude new line file names Do not use this. Leads to error 500 on some systems
|
240 |
// $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
241 |
-
|
242 |
// Recursively iterate over content directory
|
243 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
244 |
|
@@ -256,8 +273,6 @@ class Directories extends JobExecutable {
|
|
256 |
}
|
257 |
} catch ( \Exception $e ) {
|
258 |
$this->returnException( 'Error: ' . $e->getMessage() );
|
259 |
-
} catch ( \Exception $e ) {
|
260 |
-
// Skip bad file permissions
|
261 |
}
|
262 |
|
263 |
// close the file handler
|
@@ -309,8 +324,6 @@ class Directories extends JobExecutable {
|
|
309 |
}
|
310 |
} catch ( \Exception $e ) {
|
311 |
$this->returnException( 'Error: ' . $e->getMessage() );
|
312 |
-
} catch ( \Exception $e ) {
|
313 |
-
// Skip bad file permissions
|
314 |
}
|
315 |
|
316 |
// close the file handler
|
120 |
// open file handle
|
121 |
$files = $this->open( $this->filename, 'a' );
|
122 |
|
123 |
+
/**
|
124 |
+
* Excluded folders relative to the folder to iterate
|
125 |
+
*/
|
126 |
+
$excludePaths = array(
|
127 |
'cache',
|
128 |
+
'plugins/wps-hide-login',
|
129 |
+
'uploads/sites'
|
130 |
);
|
131 |
|
132 |
+
/**
|
133 |
+
* Get user excluded folders
|
134 |
+
*/
|
135 |
+
$directory = array();
|
136 |
+
foreach ( $this->options->excludedDirectories as $dir ) {
|
137 |
+
// Windows compatibility fix
|
138 |
+
$dir = wpstg_replace_windows_directory_separator($dir);
|
139 |
+
$wpContentDir = wpstg_replace_windows_directory_separator(WP_CONTENT_DIR);
|
140 |
+
|
141 |
+
if( strpos( $dir, $wpContentDir ) !== false ) {
|
142 |
+
$directory[] = ltrim( str_replace( $wpContentDir, '', $dir ), '/\\' );
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
$excludePaths = array_merge( $excludePaths, $directory );
|
147 |
+
|
148 |
try {
|
149 |
|
150 |
// Iterate over content directory
|
151 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
|
152 |
|
153 |
+
// Exclude new line file names. Do not use this. Leads to error 500 on some systems
|
154 |
// $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
|
|
155 |
// Exclude uploads, plugins or themes
|
156 |
+
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_excl_folders', $excludePaths ) );
|
157 |
+
|
158 |
// Recursively iterate over content directory
|
159 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
160 |
|
163 |
// Write path line
|
164 |
foreach ( $iterator as $item ) {
|
165 |
if( $item->isFile() ) {
|
166 |
+
$wpContentDir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
|
167 |
if( $this->write( $files, $wpContentDir . '/' . $iterator->getSubPathName() . PHP_EOL ) ) {
|
168 |
$this->options->totalFiles++;
|
169 |
|
175 |
} catch ( \Exception $e ) {
|
176 |
$this->returnException( 'Error: ' . $e->getMessage() );
|
177 |
//throw new \Exception( 'Error: ' . $e->getMessage() );
|
178 |
+
} catch ( \Exception $e ) {
|
179 |
+
// Skip bad file permissions
|
180 |
}
|
181 |
|
182 |
// close the file handler
|
207 |
|
208 |
// Exclude new line file names Do not use this. Leads to error 500 on some systems
|
209 |
// $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
|
|
210 |
// Recursively iterate over wp-includes directory
|
211 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
212 |
|
224 |
}
|
225 |
}
|
226 |
} catch ( \Exception $e ) {
|
227 |
+
$this->returnException( 'Error: ' . $e->getMessage() );
|
228 |
+
//throw new \Exception( 'Error: ' . $e->getMessage() );
|
|
|
|
|
229 |
}
|
230 |
|
231 |
// close the file handler
|
256 |
|
257 |
// Exclude new line file names Do not use this. Leads to error 500 on some systems
|
258 |
// $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
|
|
259 |
// Recursively iterate over content directory
|
260 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
261 |
|
273 |
}
|
274 |
} catch ( \Exception $e ) {
|
275 |
$this->returnException( 'Error: ' . $e->getMessage() );
|
|
|
|
|
276 |
}
|
277 |
|
278 |
// close the file handler
|
324 |
}
|
325 |
} catch ( \Exception $e ) {
|
326 |
$this->returnException( 'Error: ' . $e->getMessage() );
|
|
|
|
|
327 |
}
|
328 |
|
329 |
// close the file handler
|
apps/Backend/Modules/Jobs/Finish.php
CHANGED
@@ -46,6 +46,8 @@ class Finish extends Job {
|
|
46 |
);
|
47 |
|
48 |
//$this->flush();
|
|
|
|
|
49 |
|
50 |
return ( object ) $return;
|
51 |
}
|
@@ -75,6 +77,7 @@ class Finish extends Job {
|
|
75 |
if( isset( $this->options->existingClones[$this->options->clone] ) ) {
|
76 |
$this->options->existingClones[$this->options->clone]['datetime'] = time();
|
77 |
$this->options->existingClones[$this->options->clone]['status'] = 'finished';
|
|
|
78 |
update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
|
79 |
$this->log( "Finish: The job finished!" );
|
80 |
return true;
|
46 |
);
|
47 |
|
48 |
//$this->flush();
|
49 |
+
do_action('wpstg_cloning_complete');
|
50 |
+
|
51 |
|
52 |
return ( object ) $return;
|
53 |
}
|
77 |
if( isset( $this->options->existingClones[$this->options->clone] ) ) {
|
78 |
$this->options->existingClones[$this->options->clone]['datetime'] = time();
|
79 |
$this->options->existingClones[$this->options->clone]['status'] = 'finished';
|
80 |
+
$this->options->existingClones[$this->options->clone]['prefix'] = $this->options->prefix;
|
81 |
update_option( "wpstg_existing_clones_beta", $this->options->existingClones );
|
82 |
$this->log( "Finish: The job finished!" );
|
83 |
return true;
|
apps/Backend/Modules/Jobs/Multisite/Data.php
CHANGED
@@ -799,7 +799,7 @@ class Data extends JobExecutable {
|
|
799 |
return false;
|
800 |
}
|
801 |
|
802 |
-
$this->log( "Data Crunching Step 14:
|
803 |
return true;
|
804 |
}
|
805 |
|
799 |
return false;
|
800 |
}
|
801 |
|
802 |
+
$this->log( "Data Crunching Step 14: Successful!" );
|
803 |
return true;
|
804 |
}
|
805 |
|
apps/Backend/Modules/Jobs/Multisite/DataExternal.php
CHANGED
@@ -823,7 +823,7 @@ class DataExternal extends JobExecutable {
|
|
823 |
return false;
|
824 |
}
|
825 |
|
826 |
-
$this->log( "Data Crunching Step 14:
|
827 |
return true;
|
828 |
}
|
829 |
|
823 |
return false;
|
824 |
}
|
825 |
|
826 |
+
$this->log( "Data Crunching Step 14: Successful!" );
|
827 |
return true;
|
828 |
}
|
829 |
|
apps/Backend/Modules/Jobs/Multisite/Database.php
CHANGED
@@ -1,362 +1,363 @@
|
|
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 Database 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 |
-
// if( isset( $this->options->tables[$this->options->currentStep] ) && $this->db->prefix . 'users' === $this->options->tables[$this->options->currentStep] ) {
|
108 |
-
// $this->copyWpUsers();
|
109 |
-
// }
|
110 |
-
// if( isset( $this->options->tables[$this->options->currentStep] ) && $this->db->prefix . 'usermeta' === $this->options->tables[$this->options->currentStep] ) {
|
111 |
-
// $this->copyWpUsermeta();
|
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 |
-
// $this->
|
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 |
-
// $this->
|
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 |
-
|
|
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 |
+
if( isset( $this->options->mainJob ) && $this->options->mainJob !== 'updating' && is_dir( $path ) ) {
|
63 |
+
$this->returnException( " Can not continue! Change the name of the clone or delete existing folder. Then try again. Folder already exists: " . $path );
|
64 |
+
}
|
65 |
+
return false;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
|
70 |
+
* @return void
|
71 |
+
*/
|
72 |
+
protected function calculateTotalSteps() {
|
73 |
+
$this->options->totalSteps = ($this->total === 0) ? 1 : $this->total;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Execute the Current Step
|
78 |
+
* Returns false when over threshold limits are hit or when the job is done, true otherwise
|
79 |
+
* @return bool
|
80 |
+
*/
|
81 |
+
protected function execute() {
|
82 |
+
|
83 |
+
|
84 |
+
// Over limits threshold
|
85 |
+
if( $this->isOverThreshold() ) {
|
86 |
+
// Prepare response and save current progress
|
87 |
+
$this->prepareResponse( false, false );
|
88 |
+
$this->saveOptions();
|
89 |
+
return false;
|
90 |
+
}
|
91 |
+
|
92 |
+
// No more steps, finished
|
93 |
+
if( !isset( $this->options->isRunning ) || $this->options->currentStep > $this->total ) {
|
94 |
+
$this->prepareResponse( true, false );
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
// Copy table
|
99 |
+
if( isset( $this->options->tables[$this->options->currentStep] ) && !$this->copyTable( $this->options->tables[$this->options->currentStep] ) ) {
|
100 |
+
// Prepare Response
|
101 |
+
$this->prepareResponse( false, false );
|
102 |
+
|
103 |
+
// Not finished
|
104 |
+
return true;
|
105 |
+
}
|
106 |
+
|
107 |
+
// if( isset( $this->options->tables[$this->options->currentStep] ) && $this->db->prefix . 'users' === $this->options->tables[$this->options->currentStep] ) {
|
108 |
+
// $this->copyWpUsers();
|
109 |
+
// }
|
110 |
+
// if( isset( $this->options->tables[$this->options->currentStep] ) && $this->db->prefix . 'usermeta' === $this->options->tables[$this->options->currentStep] ) {
|
111 |
+
// $this->copyWpUsermeta();
|
112 |
+
// }
|
113 |
+
// Prepare Response
|
114 |
+
$this->prepareResponse();
|
115 |
+
|
116 |
+
// Not finished
|
117 |
+
return true;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Get new prefix for the staging site
|
122 |
+
* @return string
|
123 |
+
*/
|
124 |
+
private function getStagingPrefix() {
|
125 |
+
$stagingPrefix = $this->options->prefix;
|
126 |
+
// Make sure prefix of staging site is NEVER identical to prefix of live site!
|
127 |
+
if( $stagingPrefix == $this->db->prefix ) {
|
128 |
+
$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';
|
129 |
+
$this->returnException($error);
|
130 |
+
wp_die( $error );
|
131 |
+
|
132 |
+
}
|
133 |
+
return $stagingPrefix;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* No worries, SQL queries don't eat from PHP execution time!
|
138 |
+
* @param string $tableName
|
139 |
+
* @return bool
|
140 |
+
*/
|
141 |
+
private function copyTable( $tableName ) {
|
142 |
+
|
143 |
+
$strings = new Strings();
|
144 |
+
$tableName = is_object( $tableName ) ? $tableName->name : $tableName;
|
145 |
+
$newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->prefix, null, $tableName );
|
146 |
+
|
147 |
+
// Get wp_users from main site
|
148 |
+
if( 'users' === $strings->str_replace_first( $this->db->prefix, null, $tableName ) ) {
|
149 |
+
$tableName = $this->db->base_prefix . 'users';
|
150 |
+
}
|
151 |
+
// Get wp_usermeta from main site
|
152 |
+
if( 'usermeta' === $strings->str_replace_first( $this->db->prefix, null, $tableName ) ) {
|
153 |
+
$tableName = $this->db->base_prefix . 'usermeta';
|
154 |
+
}
|
155 |
+
|
156 |
+
// Drop table if necessary
|
157 |
+
$this->dropTable( $newTableName );
|
158 |
+
|
159 |
+
// Save current job
|
160 |
+
$this->setJob( $newTableName );
|
161 |
+
|
162 |
+
// Beginning of the job
|
163 |
+
if( !$this->startJob( $newTableName, $tableName ) ) {
|
164 |
+
return true;
|
165 |
+
}
|
166 |
+
|
167 |
+
// Copy data
|
168 |
+
$this->copyData( $newTableName, $tableName );
|
169 |
+
|
170 |
+
// Finish the step
|
171 |
+
return $this->finishStep();
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Copy multisite global user table wp_users to wpstgX_users
|
176 |
+
* @return bool
|
177 |
+
*/
|
178 |
+
// private function copyWpUsers() {
|
179 |
+
//// $strings = new Strings();
|
180 |
+
//// $tableName = $this->db->base_prefix . 'users';
|
181 |
+
//// $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
|
182 |
+
//
|
183 |
+
// $tableName = $this->db->base_prefix . 'users';
|
184 |
+
// $newTableName = $this->getStagingPrefix() . 'users';
|
185 |
+
//
|
186 |
+
// $this->log( "DB Copy: Try to create table {$newTableName}" );
|
187 |
+
//
|
188 |
+
// // Drop table if necessary
|
189 |
+
// $this->dropTable( $newTableName );
|
190 |
+
//
|
191 |
+
// // Save current job
|
192 |
+
// $this->setJob( $newTableName );
|
193 |
+
//
|
194 |
+
// // Beginning of the job
|
195 |
+
// if( !$this->startJob( $newTableName, $tableName ) ) {
|
196 |
+
// return true;
|
197 |
+
// }
|
198 |
+
//
|
199 |
+
// // Copy data
|
200 |
+
// $this->copyData( $newTableName, $tableName );
|
201 |
+
//
|
202 |
+
// // Finish the step
|
203 |
+
// return $this->finishStep();
|
204 |
+
// }
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Copy multisite global user table wp_usermeta to wpstgX_users
|
208 |
+
* @return bool
|
209 |
+
*/
|
210 |
+
// private function copyWpUsermeta() {
|
211 |
+
//// $strings = new Strings();
|
212 |
+
//// $tableName = $this->db->base_prefix . 'usermeta';
|
213 |
+
//// $newTableName = $this->getStagingPrefix() . $strings->str_replace_first( $this->db->base_prefix, null, $tableName );
|
214 |
+
// $tableName = $this->db->base_prefix . 'usermeta';
|
215 |
+
// $newTableName = $this->getStagingPrefix() . 'usermeta';
|
216 |
+
//
|
217 |
+
// $this->log( "DB Copy: Try to create table {$newTableName}" );
|
218 |
+
//
|
219 |
+
//
|
220 |
+
// // Drop table if necessary
|
221 |
+
// $this->dropTable( $newTableName );
|
222 |
+
//
|
223 |
+
// // Save current job
|
224 |
+
// $this->setJob( $newTableName );
|
225 |
+
//
|
226 |
+
// // Beginning of the job
|
227 |
+
// if( !$this->startJob( $newTableName, $tableName ) ) {
|
228 |
+
// return true;
|
229 |
+
// }
|
230 |
+
// // Copy data
|
231 |
+
// $this->copyData( $newTableName, $tableName );
|
232 |
+
//
|
233 |
+
// // Finish the step
|
234 |
+
// return $this->finishStep();
|
235 |
+
// }
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Copy data from old table to new table
|
239 |
+
* @param string $new
|
240 |
+
* @param string $old
|
241 |
+
*/
|
242 |
+
private function copyData( $new, $old ) {
|
243 |
+
$rows = $this->options->job->start + $this->settings->queryLimit;
|
244 |
+
$this->log(
|
245 |
+
"DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
|
246 |
+
);
|
247 |
+
|
248 |
+
$limitation = '';
|
249 |
+
|
250 |
+
if( 0 < ( int ) $this->settings->queryLimit ) {
|
251 |
+
$limitation = " LIMIT {$this->settings->queryLimit} OFFSET {$this->options->job->start}";
|
252 |
+
}
|
253 |
+
|
254 |
+
$this->db->query(
|
255 |
+
"INSERT INTO {$new} SELECT * FROM {$old} {$limitation}"
|
256 |
+
);
|
257 |
+
|
258 |
+
// Set new offset
|
259 |
+
$this->options->job->start += $this->settings->queryLimit;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Set the job
|
264 |
+
* @param string $table
|
265 |
+
*/
|
266 |
+
private function setJob( $table ) {
|
267 |
+
if( isset( $this->options->job->current ) ) {
|
268 |
+
return;
|
269 |
+
}
|
270 |
+
|
271 |
+
$this->options->job->current = $table;
|
272 |
+
$this->options->job->start = 0;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Start Job
|
277 |
+
* @param string $new
|
278 |
+
* @param string $old
|
279 |
+
* @return bool
|
280 |
+
*/
|
281 |
+
private function startJob( $new, $old ) {
|
282 |
+
|
283 |
+
if( 0 != $this->options->job->start ) {
|
284 |
+
return true;
|
285 |
+
}
|
286 |
+
|
287 |
+
// Table does not exist
|
288 |
+
$result = $this->db->query( "SHOW TABLES LIKE '{$old}'" );
|
289 |
+
if( !$result || 0 === $result ) {
|
290 |
+
return true;
|
291 |
+
}
|
292 |
+
|
293 |
+
$this->log( "DB Copy: Creating table {$new}" );
|
294 |
+
|
295 |
+
$this->db->query( "CREATE TABLE {$new} LIKE {$old}" );
|
296 |
+
|
297 |
+
$this->options->job->total = 0;
|
298 |
+
$this->options->job->total = ( int ) $this->db->get_var( "SELECT COUNT(1) FROM {$old}" );
|
299 |
+
|
300 |
+
if( 0 == $this->options->job->total ) {
|
301 |
+
$this->finishStep();
|
302 |
+
return false;
|
303 |
+
}
|
304 |
+
|
305 |
+
return true;
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* Finish the step
|
310 |
+
*/
|
311 |
+
private function finishStep() {
|
312 |
+
// This job is not finished yet
|
313 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
314 |
+
return false;
|
315 |
+
}
|
316 |
+
|
317 |
+
// Add it to cloned tables listing
|
318 |
+
$this->options->clonedTables[] = isset( $this->options->tables[$this->options->currentStep] ) ? $this->options->tables[$this->options->currentStep] : false;
|
319 |
+
|
320 |
+
// Reset job
|
321 |
+
$this->options->job = new \stdClass();
|
322 |
+
|
323 |
+
return true;
|
324 |
+
}
|
325 |
+
|
326 |
+
/**
|
327 |
+
* Drop table if necessary
|
328 |
+
* @param string $new
|
329 |
+
*/
|
330 |
+
private function dropTable( $new ) {
|
331 |
+
|
332 |
+
$old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
|
333 |
+
|
334 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
335 |
+
return;
|
336 |
+
}
|
337 |
+
|
338 |
+
$this->log( "DB Copy: {$new} already exists, dropping it first" );
|
339 |
+
$this->db->query( "DROP TABLE {$new}" );
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Check if table needs to be dropped
|
344 |
+
* @param string $new
|
345 |
+
* @param string $old
|
346 |
+
* @return bool
|
347 |
+
*/
|
348 |
+
private function shouldDropTable( $new, $old ) {
|
349 |
+
|
350 |
+
|
351 |
+
|
352 |
+
if( $old === $new &&
|
353 |
+
(
|
354 |
+
!isset( $this->options->job->current ) ||
|
355 |
+
!isset( $this->options->job->start ) ||
|
356 |
+
0 == $this->options->job->start
|
357 |
+
) ) {
|
358 |
+
return true;
|
359 |
+
}
|
360 |
+
return false;
|
361 |
+
}
|
362 |
+
|
363 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/Directories.php
CHANGED
@@ -11,7 +11,6 @@ use WPStaging\WPStaging;
|
|
11 |
use WPStaging\Utils\Logger;
|
12 |
use WPStaging\Utils\Strings;
|
13 |
use WPStaging\Iterators\RecursiveDirectoryIterator;
|
14 |
-
use WPStaging\Iterators\RecursiveFilterNewLine;
|
15 |
use WPStaging\Iterators\RecursiveFilterExclude;
|
16 |
use WPStaging\Backend\Modules\Jobs\JobExecutable;
|
17 |
|
@@ -127,8 +126,8 @@ class Directories extends JobExecutable {
|
|
127 |
*/
|
128 |
$excludePaths = array(
|
129 |
'cache',
|
130 |
-
'plugins
|
131 |
-
'uploads
|
132 |
);
|
133 |
|
134 |
/**
|
@@ -136,30 +135,25 @@ class Directories extends JobExecutable {
|
|
136 |
*/
|
137 |
$directory = array();
|
138 |
foreach ( $this->options->excludedDirectories as $dir ) {
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
141 |
}
|
142 |
}
|
143 |
|
144 |
$excludePaths = array_merge( $excludePaths, $directory );
|
145 |
|
146 |
-
// $excludeFolders = array(
|
147 |
-
// 'cache',
|
148 |
-
// 'node_modules',
|
149 |
-
// 'nbproject',
|
150 |
-
// 'wps-hide-login'
|
151 |
-
// );
|
152 |
-
|
153 |
try {
|
154 |
|
155 |
// Iterate over content directory
|
156 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
|
157 |
|
158 |
-
// Exclude new line file names
|
159 |
-
$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
160 |
-
|
161 |
// Exclude sites, uploads, plugins or themes
|
162 |
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_mu_excl_folders', $excludePaths ) );
|
|
|
163 |
// Recursively iterate over content directory
|
164 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
165 |
|
@@ -210,9 +204,6 @@ class Directories extends JobExecutable {
|
|
210 |
// Iterate over wp-admin directory
|
211 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
|
212 |
|
213 |
-
// Exclude new line file names
|
214 |
-
$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
215 |
-
|
216 |
// Recursively iterate over wp-includes directory
|
217 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
218 |
|
@@ -262,9 +253,6 @@ class Directories extends JobExecutable {
|
|
262 |
// Iterate over wp-admin directory
|
263 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
|
264 |
|
265 |
-
// Exclude new line file names
|
266 |
-
$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
267 |
-
|
268 |
// Recursively iterate over content directory
|
269 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
270 |
|
@@ -328,8 +316,8 @@ class Directories extends JobExecutable {
|
|
328 |
*/
|
329 |
$excludePaths = array(
|
330 |
'cache',
|
331 |
-
'plugins
|
332 |
-
'uploads
|
333 |
);
|
334 |
|
335 |
/**
|
@@ -337,6 +325,8 @@ class Directories extends JobExecutable {
|
|
337 |
*/
|
338 |
$directory = array();
|
339 |
foreach ( $this->options->excludedDirectories as $dir ) {
|
|
|
|
|
340 |
if( strpos( $dir, $path ) !== false ) {
|
341 |
$directory[] = ltrim( str_replace( $path, '', $dir ), '/' );
|
342 |
}
|
@@ -349,9 +339,6 @@ class Directories extends JobExecutable {
|
|
349 |
// Iterate over content directory
|
350 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $path );
|
351 |
|
352 |
-
// Exclude new line file names
|
353 |
-
$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
354 |
-
|
355 |
// Exclude sites, uploads, plugins or themes
|
356 |
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $excludePaths );
|
357 |
|
@@ -434,20 +421,6 @@ class Directories extends JobExecutable {
|
|
434 |
// Iterate over extra directory
|
435 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
|
436 |
|
437 |
-
// Exclude new line file names
|
438 |
-
$iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
|
439 |
-
|
440 |
-
// Exclude wp core folders
|
441 |
-
// $exclude = array('wp-includes',
|
442 |
-
// 'wp-admin',
|
443 |
-
// 'wp-content');
|
444 |
-
//
|
445 |
-
// $excludeMore = array();
|
446 |
-
// foreach ($this->options->excludedDirectories as $key => $value){
|
447 |
-
// $excludeMore[] = $this->getLastElemAfterString('/', $value);
|
448 |
-
// }
|
449 |
-
//$exclude = array_merge($exclude, $excludeMore);
|
450 |
-
|
451 |
$exclude = array();
|
452 |
|
453 |
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
|
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 |
|
126 |
*/
|
127 |
$excludePaths = array(
|
128 |
'cache',
|
129 |
+
'plugins/wps-hide-login',
|
130 |
+
'uploads/sites'
|
131 |
);
|
132 |
|
133 |
/**
|
135 |
*/
|
136 |
$directory = array();
|
137 |
foreach ( $this->options->excludedDirectories as $dir ) {
|
138 |
+
// Windows compatibility fix
|
139 |
+
$dir = wpstg_replace_windows_directory_separator($dir);
|
140 |
+
$wpContentDir = wpstg_replace_windows_directory_separator(WP_CONTENT_DIR);
|
141 |
+
|
142 |
+
if( strpos( $dir, $wpContentDir ) !== false ) {
|
143 |
+
$directory[] = ltrim( str_replace( $wpContentDir, '', $dir ), '/\\' );
|
144 |
}
|
145 |
}
|
146 |
|
147 |
$excludePaths = array_merge( $excludePaths, $directory );
|
148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
try {
|
150 |
|
151 |
// Iterate over content directory
|
152 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
|
153 |
|
|
|
|
|
|
|
154 |
// Exclude sites, uploads, plugins or themes
|
155 |
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_clone_mu_excl_folders', $excludePaths ) );
|
156 |
+
|
157 |
// Recursively iterate over content directory
|
158 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
159 |
|
204 |
// Iterate over wp-admin directory
|
205 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-includes' . DIRECTORY_SEPARATOR );
|
206 |
|
|
|
|
|
|
|
207 |
// Recursively iterate over wp-includes directory
|
208 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
209 |
|
253 |
// Iterate over wp-admin directory
|
254 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( \WPStaging\WPStaging::getWPpath() . 'wp-admin' . DIRECTORY_SEPARATOR );
|
255 |
|
|
|
|
|
|
|
256 |
// Recursively iterate over content directory
|
257 |
$iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
|
258 |
|
316 |
*/
|
317 |
$excludePaths = array(
|
318 |
'cache',
|
319 |
+
'plugins/wps-hide-login',
|
320 |
+
'uploads/sites'
|
321 |
);
|
322 |
|
323 |
/**
|
325 |
*/
|
326 |
$directory = array();
|
327 |
foreach ( $this->options->excludedDirectories as $dir ) {
|
328 |
+
$path = wpstg_replace_windows_directory_separator($path);
|
329 |
+
$dir = wpstg_replace_windows_directory_separator($dir);
|
330 |
if( strpos( $dir, $path ) !== false ) {
|
331 |
$directory[] = ltrim( str_replace( $path, '', $dir ), '/' );
|
332 |
}
|
339 |
// Iterate over content directory
|
340 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $path );
|
341 |
|
|
|
|
|
|
|
342 |
// Exclude sites, uploads, plugins or themes
|
343 |
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $excludePaths );
|
344 |
|
421 |
// Iterate over extra directory
|
422 |
$iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
|
423 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
424 |
$exclude = array();
|
425 |
|
426 |
$iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
|
apps/Backend/Modules/Jobs/Multisite/SearchReplace.php
CHANGED
@@ -1,784 +1,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
|
67 |
-
$this->db
|
68 |
-
$this->tmpPrefix
|
69 |
-
$this->strings
|
70 |
-
$this->sourceHostname
|
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 |
-
|
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 |
-
$this->
|
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 |
-
$this->
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
$args['
|
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 |
-
$this->
|
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 |
-
|
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 |
-
return $
|
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 |
-
$this->options->
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
return;
|
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 |
-
private function
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
}
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 Processing: 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 Processing: 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 |
+
|
319 |
+
|
320 |
+
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
321 |
+
$args['search_for'] = array(
|
322 |
+
'//' . $this->getSourceHostname(),
|
323 |
+
ABSPATH,
|
324 |
+
'\/\/' . str_replace( '/', '\/', $this->getSourceHostname() ), // Used by revslider and several visual editors
|
325 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->getSourceHostname() ), // HTML entitity for WP Backery Page Builder Plugin
|
326 |
+
//$this->getImagePathLive()
|
327 |
+
);
|
328 |
+
|
329 |
+
|
330 |
+
$args['replace_with'] = array(
|
331 |
+
'//' . $this->getDestinationHostname(),
|
332 |
+
$this->options->destinationDir,
|
333 |
+
'\/\/' . str_replace( '/', '\/', $this->getDestinationHostname() ), // Used by revslider and several visual editors
|
334 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->getDestinationHostname() ), // HTML entitity for WP Backery Page Builder Plugin
|
335 |
+
//$this->getImagePathStaging()
|
336 |
+
);
|
337 |
+
|
338 |
+
$this->debugLog( "DB Processing: Search: {$args['search_for'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
339 |
+
$this->debugLog( "DB Processing: Replace: {$args['replace_with'][0]}", \WPStaging\Utils\Logger::TYPE_INFO );
|
340 |
+
|
341 |
+
|
342 |
+
|
343 |
+
$args['replace_guids'] = 'off';
|
344 |
+
$args['dry_run'] = 'off';
|
345 |
+
$args['case_insensitive'] = false;
|
346 |
+
//$args['replace_mails'] = 'off';
|
347 |
+
$args['skip_transients'] = 'on';
|
348 |
+
|
349 |
+
|
350 |
+
// Allow filtering of search & replace parameters
|
351 |
+
$args = apply_filters( 'wpstg_clone_searchreplace_params', $args );
|
352 |
+
|
353 |
+
// Get a list of columns in this table.
|
354 |
+
list( $primary_key, $columns ) = $this->get_columns( $table );
|
355 |
+
|
356 |
+
// Bail out early if there isn't a primary key.
|
357 |
+
// We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
|
358 |
+
// @todo test this carefully. If it causes (performance) issues we need to activate it again!
|
359 |
+
// @since 2.4.4
|
360 |
+
// if( null === $primary_key ) {
|
361 |
+
// return false;
|
362 |
+
// }
|
363 |
+
|
364 |
+
$current_row = 0;
|
365 |
+
$start = $this->options->job->start;
|
366 |
+
$end = $this->settings->querySRLimit;
|
367 |
+
|
368 |
+
// Grab the content of the table.
|
369 |
+
$data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
370 |
+
|
371 |
+
// Filter certain rows option_name in wpstg_options
|
372 |
+
$filter = array(
|
373 |
+
'Admin_custome_login_Slidshow',
|
374 |
+
'Admin_custome_login_Social',
|
375 |
+
'Admin_custome_login_logo',
|
376 |
+
'Admin_custome_login_text',
|
377 |
+
'Admin_custome_login_login',
|
378 |
+
'Admin_custome_login_top',
|
379 |
+
'Admin_custome_login_dashboard',
|
380 |
+
'Admin_custome_login_Version',
|
381 |
+
'upload_path',
|
382 |
+
);
|
383 |
+
|
384 |
+
$filter = apply_filters( 'wpstg_clone_searchreplace_excl_rows', $filter );
|
385 |
+
|
386 |
+
// Loop through the data.
|
387 |
+
foreach ( $data as $row ) {
|
388 |
+
$current_row++;
|
389 |
+
$update_sql = array();
|
390 |
+
$where_sql = array();
|
391 |
+
$upd = false;
|
392 |
+
|
393 |
+
// Skip rows below
|
394 |
+
if( isset( $row['option_name'] ) && in_array( $row['option_name'], $filter ) ) {
|
395 |
+
continue;
|
396 |
+
}
|
397 |
+
|
398 |
+
// Skip rows with transients (They can store huge data and we need to save memory)
|
399 |
+
if( isset( $row['option_name'] ) && 'on' === $args['skip_transients'] && false !== strpos( $row['option_name'], '_transient' ) ) {
|
400 |
+
continue;
|
401 |
+
}
|
402 |
+
// Skip rows with more than 5MB to save memory
|
403 |
+
if( isset( $row['option_value'] ) && strlen( $row['option_value'] ) >= 5000000 ) {
|
404 |
+
continue;
|
405 |
+
}
|
406 |
+
|
407 |
+
|
408 |
+
foreach ( $columns as $column ) {
|
409 |
+
|
410 |
+
$dataRow = $row[$column];
|
411 |
+
|
412 |
+
// Skip rows larger than 5MB
|
413 |
+
$size = strlen( $dataRow );
|
414 |
+
if( $size >= 5000000 ) {
|
415 |
+
continue;
|
416 |
+
}
|
417 |
+
|
418 |
+
// Skip Primary key
|
419 |
+
if( $column == $primary_key ) {
|
420 |
+
$where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
421 |
+
continue;
|
422 |
+
}
|
423 |
+
|
424 |
+
// Skip GUIDs by default.
|
425 |
+
if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
|
426 |
+
continue;
|
427 |
+
}
|
428 |
+
|
429 |
+
// Skip mail addresses
|
430 |
+
// if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->multisiteDomainWithoutScheme ) ) {
|
431 |
+
// continue;
|
432 |
+
// }
|
433 |
+
// Check options table
|
434 |
+
if( $this->options->prefix . 'options' === $table ) {
|
435 |
+
|
436 |
+
// Skip certain options
|
437 |
+
// if( isset( $should_skip ) && true === $should_skip ) {
|
438 |
+
// $should_skip = false;
|
439 |
+
// continue;
|
440 |
+
// }
|
441 |
+
// Skip this row
|
442 |
+
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
443 |
+
'wpstg_existing_clones' === $dataRow ||
|
444 |
+
'wpstg_settings' === $dataRow ||
|
445 |
+
'wpstg_license_status' === $dataRow ||
|
446 |
+
'siteurl' === $dataRow ||
|
447 |
+
'home' === $dataRow
|
448 |
+
) {
|
449 |
+
//$should_skip = true;
|
450 |
+
continue;
|
451 |
+
}
|
452 |
+
}
|
453 |
+
|
454 |
+
// Check the path delimiter for / or \/ and remove one of those which prevents from resulting in wrong syntax like domain.com/staging\/.
|
455 |
+
// 1. local.wordpress.test -> local.wordpress.test/staging
|
456 |
+
// 2. local.wordpress.test\/ -> local.wordpress.test\/staging\/
|
457 |
+
$tmp = $args;
|
458 |
+
if( false === strpos( $dataRow, $tmp['search_for'][0] ) ) {
|
459 |
+
array_shift( $tmp['search_for'] ); // rtrim( $this->homeUrl, '/' ),
|
460 |
+
array_shift( $tmp['replace_with'] ); // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
|
461 |
+
} else {
|
462 |
+
unset( $tmp['search_for'][1] );
|
463 |
+
unset( $tmp['replace_with'][1] );
|
464 |
+
// recount array
|
465 |
+
$tmp['search_for'] = array_values( $tmp['search_for'] );
|
466 |
+
$tmp['replace_with'] = array_values( $tmp['replace_with'] );
|
467 |
+
}
|
468 |
+
|
469 |
+
// Run a search replace on the data row and respect the serialisation.
|
470 |
+
$i = 0;
|
471 |
+
foreach ( $tmp['search_for'] as $replace ) {
|
472 |
+
$dataRow = $this->recursive_unserialize_replace( $tmp['search_for'][$i], $tmp['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
|
473 |
+
$i++;
|
474 |
+
}
|
475 |
+
unset( $replace );
|
476 |
+
unset( $i );
|
477 |
+
unset( $tmp );
|
478 |
+
|
479 |
+
// Something was changed
|
480 |
+
if( $row[$column] != $dataRow ) {
|
481 |
+
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
482 |
+
$upd = true;
|
483 |
+
}
|
484 |
+
}
|
485 |
+
|
486 |
+
// Determine what to do with updates.
|
487 |
+
if( $args['dry_run'] === 'on' ) {
|
488 |
+
// Don't do anything if a dry run
|
489 |
+
} elseif( $upd && !empty( $where_sql ) ) {
|
490 |
+
// If there are changes to make, run the query.
|
491 |
+
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
492 |
+
$result = $this->db->query( $sql );
|
493 |
+
|
494 |
+
if( !$result ) {
|
495 |
+
$this->log( "Error updating row {$current_row} SQL: {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR );
|
496 |
+
}
|
497 |
+
}
|
498 |
+
} // end row loop
|
499 |
+
unset( $row );
|
500 |
+
unset( $update_sql );
|
501 |
+
unset( $where_sql );
|
502 |
+
unset( $sql );
|
503 |
+
|
504 |
+
|
505 |
+
// DB Flush
|
506 |
+
$this->db->flush();
|
507 |
+
return true;
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* Get path to multisite image folder e.g. wp-content/blogs.dir/ID/files or wp-content/uploads/sites/ID
|
512 |
+
* @return string
|
513 |
+
*/
|
514 |
+
private function getImagePathLive() {
|
515 |
+
// Check first which structure is used
|
516 |
+
$uploads = wp_upload_dir();
|
517 |
+
$basedir = $uploads['basedir'];
|
518 |
+
$blogId = get_current_blog_id();
|
519 |
+
|
520 |
+
if( false === strpos( $basedir, 'blogs.dir' ) ) {
|
521 |
+
// Since WP 3.5
|
522 |
+
$path = $blogId > 1 ?
|
523 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR :
|
524 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
525 |
+
} else {
|
526 |
+
// old blog structure
|
527 |
+
$path = $blogId > 1 ?
|
528 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR :
|
529 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
530 |
+
}
|
531 |
+
return $path;
|
532 |
+
}
|
533 |
+
|
534 |
+
/**
|
535 |
+
* Get path to staging site image path wp-content/uploads
|
536 |
+
* @return string
|
537 |
+
*/
|
538 |
+
private function getImagePathStaging() {
|
539 |
+
return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
540 |
+
}
|
541 |
+
|
542 |
+
/**
|
543 |
+
* Adapted from interconnect/it's search/replace script.
|
544 |
+
*
|
545 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
546 |
+
*
|
547 |
+
* Take a serialised array and unserialise it replacing elements as needed and
|
548 |
+
* unserialising any subordinate arrays and performing the replace on those too.
|
549 |
+
*
|
550 |
+
* @access private
|
551 |
+
* @param string $from String we're looking to replace.
|
552 |
+
* @param string $to What we want it to be replaced with
|
553 |
+
* @param array $data Used to pass any subordinate arrays back to in.
|
554 |
+
* @param boolean $serialized Does the array passed via $data need serialising.
|
555 |
+
* @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
|
556 |
+
*
|
557 |
+
* @return string|array The original array with all elements replaced as needed.
|
558 |
+
*/
|
559 |
+
private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
|
560 |
+
try {
|
561 |
+
// Some unserialized data cannot be re-serialized eg. SimpleXMLElements
|
562 |
+
if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
563 |
+
$data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
|
564 |
+
} elseif( is_array( $data ) ) {
|
565 |
+
$tmp = array();
|
566 |
+
foreach ( $data as $key => $value ) {
|
567 |
+
$tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
568 |
+
}
|
569 |
+
|
570 |
+
$data = $tmp;
|
571 |
+
unset( $tmp );
|
572 |
+
} elseif( is_object( $data ) ) {
|
573 |
+
$tmp = $data;
|
574 |
+
$props = get_object_vars( $data );
|
575 |
+
|
576 |
+
// Do not continue if class contains __PHP_Incomplete_Class_Name
|
577 |
+
if( !empty( $props['__PHP_Incomplete_Class_Name'] ) ) {
|
578 |
+
return $data;
|
579 |
+
|
580 |
+
}
|
581 |
+
|
582 |
+
// Do a search & replace
|
583 |
+
foreach ( $props as $key => $value ) {
|
584 |
+
if( $key === '' || ord( $key[0] ) === 0 ) {
|
585 |
+
continue;
|
586 |
+
}
|
587 |
+
$tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
588 |
+
}
|
589 |
+
|
590 |
+
$data = $tmp;
|
591 |
+
unset( $tmp );
|
592 |
+
unset( $props );
|
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 |
+
* Finish the step
|
752 |
+
*/
|
753 |
+
private function finishStep() {
|
754 |
+
// This job is not finished yet
|
755 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
756 |
+
return false;
|
757 |
+
}
|
758 |
+
|
759 |
+
// Add it to cloned tables listing
|
760 |
+
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
761 |
+
|
762 |
+
// Reset job
|
763 |
+
$this->options->job = new \stdClass();
|
764 |
+
|
765 |
+
return true;
|
766 |
+
}
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Drop table if necessary
|
770 |
+
* @param string $new
|
771 |
+
*/
|
772 |
+
private function dropTable( $new ) {
|
773 |
+
$old = $this->db->get_var( $this->db->prepare( "SHOW TABLES LIKE %s", $new ) );
|
774 |
+
|
775 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
776 |
+
return;
|
777 |
+
}
|
778 |
+
|
779 |
+
$this->log( "DB Processing: {$new} already exists, dropping it first" );
|
780 |
+
$this->db->query( "DROP TABLE {$new}" );
|
781 |
+
}
|
782 |
+
|
783 |
+
/**
|
784 |
+
* Check if table needs to be dropped
|
785 |
+
* @param string $new
|
786 |
+
* @param string $old
|
787 |
+
* @return bool
|
788 |
+
*/
|
789 |
+
private function shouldDropTable( $new, $old ) {
|
790 |
+
return (
|
791 |
+
$old == $new &&
|
792 |
+
(
|
793 |
+
!isset( $this->options->job->current ) ||
|
794 |
+
!isset( $this->options->job->start ) ||
|
795 |
+
0 == $this->options->job->start
|
796 |
+
)
|
797 |
+
);
|
798 |
+
}
|
799 |
+
|
800 |
+
/**
|
801 |
+
* Check if WP is installed in subdir
|
802 |
+
* @return boolean
|
803 |
+
*/
|
804 |
+
private function isSubDir() {
|
805 |
+
// Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
|
806 |
+
// This is happening much more often than you would expect
|
807 |
+
$siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
|
808 |
+
$home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
|
809 |
+
|
810 |
+
if( $home !== $siteurl ) {
|
811 |
+
return true;
|
812 |
+
}
|
813 |
+
return false;
|
814 |
+
}
|
815 |
+
|
816 |
+
}
|
apps/Backend/Modules/Jobs/Multisite/SearchReplaceExternal.php
CHANGED
@@ -1,818 +1,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 Processing: 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 |
-
* Return absolute destination path
|
287 |
-
* @return string
|
288 |
-
*/
|
289 |
-
// private function getAbsDestination() {
|
290 |
-
// if( empty( $this->options->cloneDir ) ) {
|
291 |
-
// return \WPStaging\WPStaging::getWPpath();
|
292 |
-
// }
|
293 |
-
// return trailingslashit( $this->options->cloneDir );
|
294 |
-
// }
|
295 |
-
|
296 |
-
/**
|
297 |
-
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
298 |
-
*
|
299 |
-
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
300 |
-
* and to be compatible with batch processing.
|
301 |
-
*
|
302 |
-
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
303 |
-
*
|
304 |
-
* @access public
|
305 |
-
* @param string $table The table to run the replacement on.
|
306 |
-
* @param int $page The page/block to begin the query on.
|
307 |
-
* @param array $args An associative array containing arguments for this run.
|
308 |
-
* @return array
|
309 |
-
*/
|
310 |
-
private function searchReplace( $table, $page, $args ) {
|
311 |
-
|
312 |
-
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
313 |
-
$this->log( "DB Processing: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
|
314 |
-
return true;
|
315 |
-
}
|
316 |
-
|
317 |
-
// Load up the default settings for this chunk.
|
318 |
-
$table = esc_sql( $table );
|
319 |
-
$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
320 |
-
$pages = $this->get_pages_in_table( $table );
|
321 |
-
|
322 |
-
|
323 |
-
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
324 |
-
$args['search_for'] = array(
|
325 |
-
'//' . $this->sourceHostname,
|
326 |
-
ABSPATH,
|
327 |
-
'\/\/' . str_replace( '/', '\/', $this->sourceHostname ), //
|
328 |
-
$this->
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
$this->
|
335 |
-
|
336 |
-
$this->
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
$args['
|
343 |
-
$args['
|
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 |
-
if(
|
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 |
-
|
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 |
-
* @param string
|
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 |
-
if(
|
712 |
-
return
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 Processing: 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 |
+
* Return absolute destination path
|
287 |
+
* @return string
|
288 |
+
*/
|
289 |
+
// private function getAbsDestination() {
|
290 |
+
// if( empty( $this->options->cloneDir ) ) {
|
291 |
+
// return \WPStaging\WPStaging::getWPpath();
|
292 |
+
// }
|
293 |
+
// return trailingslashit( $this->options->cloneDir );
|
294 |
+
// }
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Adapated from interconnect/it's search/replace script, adapted from Better Search Replace
|
298 |
+
*
|
299 |
+
* Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions,
|
300 |
+
* and to be compatible with batch processing.
|
301 |
+
*
|
302 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
303 |
+
*
|
304 |
+
* @access public
|
305 |
+
* @param string $table The table to run the replacement on.
|
306 |
+
* @param int $page The page/block to begin the query on.
|
307 |
+
* @param array $args An associative array containing arguments for this run.
|
308 |
+
* @return array
|
309 |
+
*/
|
310 |
+
private function searchReplace( $table, $page, $args ) {
|
311 |
+
|
312 |
+
if( $this->thirdParty->isSearchReplaceExcluded( $table ) ) {
|
313 |
+
$this->log( "DB Processing: Skip {$table}", \WPStaging\Utils\Logger::TYPE_INFO );
|
314 |
+
return true;
|
315 |
+
}
|
316 |
+
|
317 |
+
// Load up the default settings for this chunk.
|
318 |
+
$table = esc_sql( $table );
|
319 |
+
$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
320 |
+
$pages = $this->get_pages_in_table( $table );
|
321 |
+
|
322 |
+
|
323 |
+
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
|
324 |
+
$args['search_for'] = array(
|
325 |
+
'//' . $this->sourceHostname,
|
326 |
+
ABSPATH,
|
327 |
+
'\/\/' . str_replace( '/', '\/', $this->sourceHostname ), // Used by revslider and several visual editors
|
328 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->sourceHostname ), // HTML entitity for WP Backery Page Builder Plugin
|
329 |
+
//$this->getImagePathLive()
|
330 |
+
);
|
331 |
+
|
332 |
+
|
333 |
+
$args['replace_with'] = array(
|
334 |
+
'//' . $this->destinationHostname,
|
335 |
+
$this->options->destinationDir,
|
336 |
+
'\/\/' . str_replace( '/', '\/', $this->destinationHostname ), // Used by revslider and several visual editors
|
337 |
+
'%2F%2F' . str_replace( '/', '%2F', $this->destinationHostname ), // HTML entitity for WP Backery Page Builder Plugin
|
338 |
+
//$this->getImagePathStaging()
|
339 |
+
);
|
340 |
+
|
341 |
+
|
342 |
+
$args['replace_guids'] = 'off';
|
343 |
+
$args['dry_run'] = 'off';
|
344 |
+
$args['case_insensitive'] = false;
|
345 |
+
//$args['replace_mails'] = 'off';
|
346 |
+
$args['skip_transients'] = 'on';
|
347 |
+
|
348 |
+
|
349 |
+
// Allow filtering of search & replace parameters
|
350 |
+
$args = apply_filters( 'wpstg_clone_searchreplace_params', $args );
|
351 |
+
|
352 |
+
// Get a list of columns in this table.
|
353 |
+
list( $primary_key, $columns ) = $this->get_columns( $table );
|
354 |
+
|
355 |
+
// Bail out early if there isn't a primary key.
|
356 |
+
// We commented this to search & replace through tables which have no primary keys like wp_revslider_slides
|
357 |
+
// @todo test this carefully. If it causes (performance) issues we need to activate it again!
|
358 |
+
// @since 2.4.4
|
359 |
+
// if( null === $primary_key ) {
|
360 |
+
// return false;
|
361 |
+
// }
|
362 |
+
|
363 |
+
$current_row = 0;
|
364 |
+
$start = $this->options->job->start;
|
365 |
+
$end = $this->settings->querySRLimit;
|
366 |
+
|
367 |
+
// Grab the content of the table.
|
368 |
+
$data = $this->stagingDb->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
369 |
+
|
370 |
+
// Filter certain rows (of other plugins)
|
371 |
+
$filter = array(
|
372 |
+
'Admin_custome_login_Slidshow',
|
373 |
+
'Admin_custome_login_Social',
|
374 |
+
'Admin_custome_login_logo',
|
375 |
+
'Admin_custome_login_text',
|
376 |
+
'Admin_custome_login_login',
|
377 |
+
'Admin_custome_login_top',
|
378 |
+
'Admin_custome_login_dashboard',
|
379 |
+
'Admin_custome_login_Version',
|
380 |
+
'upload_path',
|
381 |
+
);
|
382 |
+
|
383 |
+
$filter = apply_filters( 'wpstg_clone_searchreplace_excl_rows', $filter );
|
384 |
+
|
385 |
+
// Loop through the data.
|
386 |
+
foreach ( $data as $row ) {
|
387 |
+
$current_row++;
|
388 |
+
$update_sql = array();
|
389 |
+
$where_sql = array();
|
390 |
+
$upd = false;
|
391 |
+
|
392 |
+
// Skip rows below
|
393 |
+
if( isset( $row['option_name'] ) && in_array( $row['option_name'], $filter ) ) {
|
394 |
+
continue;
|
395 |
+
}
|
396 |
+
|
397 |
+
// Skip rows with transients (They can store huge data and we need to save memory)
|
398 |
+
if( isset( $row['option_name'] ) && 'on' === $args['skip_transients'] && false !== strpos( $row['option_name'], '_transient' ) ) {
|
399 |
+
continue;
|
400 |
+
}
|
401 |
+
// Skip rows with more than 5MB to save memory
|
402 |
+
if( isset( $row['option_value'] ) && strlen( $row['option_value'] ) >= 5000000 ) {
|
403 |
+
continue;
|
404 |
+
}
|
405 |
+
|
406 |
+
|
407 |
+
foreach ( $columns as $column ) {
|
408 |
+
|
409 |
+
$dataRow = $row[$column];
|
410 |
+
|
411 |
+
// Skip rows larger than 10MB
|
412 |
+
$size = strlen( $dataRow );
|
413 |
+
if( $size >= 5000000 ) {
|
414 |
+
continue;
|
415 |
+
}
|
416 |
+
|
417 |
+
// Skip Primary key
|
418 |
+
if( $column == $primary_key ) {
|
419 |
+
$where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
420 |
+
continue;
|
421 |
+
}
|
422 |
+
|
423 |
+
// Skip GUIDs by default.
|
424 |
+
if( 'on' !== $args['replace_guids'] && 'guid' == $column ) {
|
425 |
+
continue;
|
426 |
+
}
|
427 |
+
|
428 |
+
// Skip mail addresses
|
429 |
+
// if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->multisiteDomainWithoutScheme ) ) {
|
430 |
+
// continue;
|
431 |
+
// }
|
432 |
+
|
433 |
+
// Check options table
|
434 |
+
if( $this->options->prefix . 'options' === $table ) {
|
435 |
+
|
436 |
+
// Skip certain options
|
437 |
+
if( isset( $should_skip ) && true === $should_skip ) {
|
438 |
+
$should_skip = false;
|
439 |
+
continue;
|
440 |
+
}
|
441 |
+
|
442 |
+
// Skip this row
|
443 |
+
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
444 |
+
'wpstg_existing_clones' === $dataRow ||
|
445 |
+
'wpstg_settings' === $dataRow ||
|
446 |
+
'wpstg_license_status' === $dataRow ||
|
447 |
+
'siteurl' === $dataRow ||
|
448 |
+
'home' === $dataRow
|
449 |
+
) {
|
450 |
+
$should_skip = true;
|
451 |
+
}
|
452 |
+
}
|
453 |
+
|
454 |
+
// Check the path delimiter for / or \/ and remove one of those which prevents from resulting in wrong syntax like domain.com/staging\/.
|
455 |
+
// 1. local.wordpress.test -> local.wordpress.test/staging
|
456 |
+
// 2. local.wordpress.test\/ -> local.wordpress.test\/staging\/
|
457 |
+
$tmp = $args;
|
458 |
+
if( false === strpos( $dataRow, $tmp['search_for'][0] ) ) {
|
459 |
+
array_shift( $tmp['search_for'] ); // rtrim( $this->homeUrl, '/' ),
|
460 |
+
array_shift( $tmp['replace_with'] ); // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
|
461 |
+
} else {
|
462 |
+
unset( $tmp['search_for'][1] );
|
463 |
+
unset( $tmp['replace_with'][1] );
|
464 |
+
// recount array
|
465 |
+
$tmp['search_for'] = array_values( $tmp['search_for'] );
|
466 |
+
$tmp['replace_with'] = array_values( $tmp['replace_with'] );
|
467 |
+
}
|
468 |
+
|
469 |
+
// Run a search replace on the data row and respect the serialisation.
|
470 |
+
$i = 0;
|
471 |
+
foreach ( $tmp['search_for'] as $replace ) {
|
472 |
+
$dataRow = $this->recursive_unserialize_replace( $tmp['search_for'][$i], $tmp['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
|
473 |
+
$i++;
|
474 |
+
}
|
475 |
+
unset( $replace );
|
476 |
+
unset( $i );
|
477 |
+
unset( $tmp );
|
478 |
+
|
479 |
+
// Something was changed
|
480 |
+
if( $row[$column] != $dataRow ) {
|
481 |
+
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
|
482 |
+
$upd = true;
|
483 |
+
}
|
484 |
+
}
|
485 |
+
|
486 |
+
// Determine what to do with updates.
|
487 |
+
if( $args['dry_run'] === 'on' ) {
|
488 |
+
// Don't do anything if a dry run
|
489 |
+
} elseif( $upd && !empty( $where_sql ) ) {
|
490 |
+
// If there are changes to make, run the query.
|
491 |
+
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
492 |
+
$result = $this->stagingDb->query( $sql );
|
493 |
+
|
494 |
+
if( !$result ) {
|
495 |
+
$this->log( "Error updating row {$current_row} SQL: {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR );
|
496 |
+
}
|
497 |
+
}
|
498 |
+
} // end row loop
|
499 |
+
unset( $row );
|
500 |
+
unset( $update_sql );
|
501 |
+
unset( $where_sql );
|
502 |
+
unset( $sql );
|
503 |
+
|
504 |
+
|
505 |
+
// DB Flush
|
506 |
+
$this->stagingDb->flush();
|
507 |
+
return true;
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* Get path to multisite image folder e.g. wp-content/blogs.dir/ID/files or wp-content/uploads/sites/ID
|
512 |
+
* @return string
|
513 |
+
*/
|
514 |
+
private function getImagePathLive() {
|
515 |
+
// Check first which structure is used
|
516 |
+
$uploads = wp_upload_dir();
|
517 |
+
$basedir = $uploads['basedir'];
|
518 |
+
$blogId = get_current_blog_id();
|
519 |
+
|
520 |
+
if( false === strpos( $basedir, 'blogs.dir' ) ) {
|
521 |
+
// Since WP 3.5
|
522 |
+
$path = $blogId > 1 ?
|
523 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR :
|
524 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
525 |
+
} else {
|
526 |
+
// old blog structure
|
527 |
+
$path = $blogId > 1 ?
|
528 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'blogs.dir' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR :
|
529 |
+
'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
530 |
+
}
|
531 |
+
return $path;
|
532 |
+
}
|
533 |
+
|
534 |
+
/**
|
535 |
+
* Get path to staging site image path wp-content/uploads
|
536 |
+
* @return string
|
537 |
+
*/
|
538 |
+
private function getImagePathStaging() {
|
539 |
+
return 'wp-content' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
540 |
+
}
|
541 |
+
|
542 |
+
/**
|
543 |
+
* Adapted from interconnect/it's search/replace script.
|
544 |
+
*
|
545 |
+
* @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/
|
546 |
+
*
|
547 |
+
* Take a serialised array and unserialise it replacing elements as needed and
|
548 |
+
* unserialising any subordinate arrays and performing the replace on those too.
|
549 |
+
*
|
550 |
+
* @access private
|
551 |
+
* @param string $from String we're looking to replace.
|
552 |
+
* @param string $to What we want it to be replaced with
|
553 |
+
* @param array $data Used to pass any subordinate arrays back to in.
|
554 |
+
* @param boolean $serialized Does the array passed via $data need serialising.
|
555 |
+
* @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise.
|
556 |
+
*
|
557 |
+
* @return string|array The original array with all elements replaced as needed.
|
558 |
+
*/
|
559 |
+
private function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialized = false, $case_insensitive = false ) {
|
560 |
+
try {
|
561 |
+
// Some unserialized data cannot be re-serialized eg. SimpleXMLElements
|
562 |
+
if( is_serialized( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
563 |
+
$data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive );
|
564 |
+
} elseif( is_array( $data ) ) {
|
565 |
+
$tmp = array();
|
566 |
+
foreach ( $data as $key => $value ) {
|
567 |
+
$tmp[$key] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
568 |
+
}
|
569 |
+
|
570 |
+
$data = $tmp;
|
571 |
+
unset( $tmp );
|
572 |
+
} elseif( is_object( $data ) ) {
|
573 |
+
$tmp = $data;
|
574 |
+
$props = get_object_vars( $data );
|
575 |
+
|
576 |
+
// Do not continue if class contains __PHP_Incomplete_Class_Name
|
577 |
+
if( !empty( $props['__PHP_Incomplete_Class_Name'] ) ) {
|
578 |
+
return $data;
|
579 |
+
}
|
580 |
+
|
581 |
+
// Do a search & replace
|
582 |
+
foreach ( $props as $key => $value ) {
|
583 |
+
if( $key === '' || ord( $key[0] ) === 0 ) {
|
584 |
+
continue;
|
585 |
+
}
|
586 |
+
$tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
587 |
+
}
|
588 |
+
|
589 |
+
$data = $tmp;
|
590 |
+
unset( $tmp );
|
591 |
+
unset( $props );
|
592 |
+
} else {
|
593 |
+
if( is_string( $data ) ) {
|
594 |
+
if( !empty( $from ) && !empty( $to ) ) {
|
595 |
+
$data = $this->str_replace( $from, $to, $data, $case_insensitive );
|
596 |
+
}
|
597 |
+
}
|
598 |
+
}
|
599 |
+
|
600 |
+
if( $serialized ) {
|
601 |
+
return serialize( $data );
|
602 |
+
}
|
603 |
+
} catch ( Exception $error ) {
|
604 |
+
|
605 |
+
}
|
606 |
+
|
607 |
+
return $data;
|
608 |
+
}
|
609 |
+
|
610 |
+
/**
|
611 |
+
* Check if the object is a valid one and not __PHP_Incomplete_Class_Name
|
612 |
+
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
613 |
+
* @return boolean
|
614 |
+
*/
|
615 |
+
// private function isValidObject( $data ) {
|
616 |
+
// if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
617 |
+
// return false;
|
618 |
+
// }
|
619 |
+
//
|
620 |
+
// $invalid_class_props = get_object_vars( $data );
|
621 |
+
//
|
622 |
+
// if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
623 |
+
// // Assume it must be an valid object
|
624 |
+
// return true;
|
625 |
+
// }
|
626 |
+
//
|
627 |
+
// $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
628 |
+
//
|
629 |
+
// if( !empty( $invalid_object_class ) ) {
|
630 |
+
// return false;
|
631 |
+
// }
|
632 |
+
//
|
633 |
+
// // Assume it must be an valid object
|
634 |
+
// return true;
|
635 |
+
// }
|
636 |
+
|
637 |
+
/**
|
638 |
+
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
639 |
+
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
|
640 |
+
* @access public
|
641 |
+
* @param string $input The string to escape.
|
642 |
+
* @return string
|
643 |
+
*/
|
644 |
+
private function mysql_escape_mimic( $input ) {
|
645 |
+
if( is_array( $input ) ) {
|
646 |
+
return array_map( __METHOD__, $input );
|
647 |
+
}
|
648 |
+
if( !empty( $input ) && is_string( $input ) ) {
|
649 |
+
return str_replace( array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $input );
|
650 |
+
}
|
651 |
+
|
652 |
+
return $input;
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* Return unserialized object or array
|
657 |
+
*
|
658 |
+
* @param string $serialized_string Serialized string.
|
659 |
+
* @param string $method The name of the caller method.
|
660 |
+
*
|
661 |
+
* @return mixed, false on failure
|
662 |
+
*/
|
663 |
+
private static function unserialize( $serialized_string ) {
|
664 |
+
if( !is_serialized( $serialized_string ) ) {
|
665 |
+
return false;
|
666 |
+
}
|
667 |
+
|
668 |
+
$serialized_string = trim( $serialized_string );
|
669 |
+
$unserialized_string = @unserialize( $serialized_string );
|
670 |
+
|
671 |
+
return $unserialized_string;
|
672 |
+
}
|
673 |
+
|
674 |
+
/**
|
675 |
+
* Wrapper for str_replace
|
676 |
+
*
|
677 |
+
* @param string $from
|
678 |
+
* @param string $to
|
679 |
+
* @param string $data
|
680 |
+
* @param string|bool $case_insensitive
|
681 |
+
*
|
682 |
+
* @return string
|
683 |
+
*/
|
684 |
+
private function str_replace( $from, $to, $data, $case_insensitive = false ) {
|
685 |
+
|
686 |
+
// Add filter
|
687 |
+
$excludes = apply_filters( 'wpstg_clone_searchreplace_excl', array() );
|
688 |
+
|
689 |
+
// Build pattern
|
690 |
+
$regexExclude = '';
|
691 |
+
foreach ( $excludes as $exclude ) {
|
692 |
+
$regexExclude .= $exclude . '(*SKIP)(FAIL)|';
|
693 |
+
}
|
694 |
+
|
695 |
+
if( 'on' === $case_insensitive ) {
|
696 |
+
//$data = str_ireplace( $from, $to, $data );
|
697 |
+
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#i', $to, $data );
|
698 |
+
} else {
|
699 |
+
//$data = str_replace( $from, $to, $data );
|
700 |
+
$data = preg_replace( '#' . $regexExclude . preg_quote( $from ) . '#', $to, $data );
|
701 |
+
}
|
702 |
+
|
703 |
+
return $data;
|
704 |
+
}
|
705 |
+
|
706 |
+
/**
|
707 |
+
* Set the job
|
708 |
+
* @param string $table
|
709 |
+
*/
|
710 |
+
private function setJob( $table ) {
|
711 |
+
if( !empty( $this->options->job->current ) ) {
|
712 |
+
return;
|
713 |
+
}
|
714 |
+
|
715 |
+
$this->options->job->current = $table;
|
716 |
+
$this->options->job->start = 0;
|
717 |
+
}
|
718 |
+
|
719 |
+
/**
|
720 |
+
* Start Job
|
721 |
+
* @param string $new
|
722 |
+
* @param string $old
|
723 |
+
* @return bool
|
724 |
+
*/
|
725 |
+
private function startJob( $new, $old ) {
|
726 |
+
|
727 |
+
if( $this->isExcludedTable( $new ) ) {
|
728 |
+
return false;
|
729 |
+
}
|
730 |
+
|
731 |
+
// Table does not exist
|
732 |
+
$result = $this->productionDb->query( "SHOW TABLES LIKE '{$old}'" );
|
733 |
+
if( !$result || 0 === $result ) {
|
734 |
+
return false;
|
735 |
+
}
|
736 |
+
|
737 |
+
if( 0 != $this->options->job->start ) {
|
738 |
+
return true;
|
739 |
+
}
|
740 |
+
|
741 |
+
$this->options->job->total = ( int ) $this->productionDb->get_var( "SELECT COUNT(1) FROM {$old}" );
|
742 |
+
|
743 |
+
if( 0 == $this->options->job->total ) {
|
744 |
+
$this->finishStep();
|
745 |
+
return false;
|
746 |
+
}
|
747 |
+
|
748 |
+
return true;
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* Is table excluded from search replace processing?
|
753 |
+
* @param string $table
|
754 |
+
* @return boolean
|
755 |
+
*/
|
756 |
+
private function isExcludedTable( $table ) {
|
757 |
+
|
758 |
+
$customTables = apply_filters( 'wpstg_clone_searchreplace_tables_exclude', array() );
|
759 |
+
$defaultTables = array('blogs');
|
760 |
+
|
761 |
+
$tables = array_merge( $customTables, $defaultTables );
|
762 |
+
|
763 |
+
$excludedTables = array();
|
764 |
+
foreach ( $tables as $key => $value ) {
|
765 |
+
$excludedTables[] = $this->options->prefix . $value;
|
766 |
+
}
|
767 |
+
|
768 |
+
if( in_array( $table, $excludedTables ) ) {
|
769 |
+
return true;
|
770 |
+
}
|
771 |
+
return false;
|
772 |
+
}
|
773 |
+
|
774 |
+
/**
|
775 |
+
* Finish the step
|
776 |
+
*/
|
777 |
+
private function finishStep() {
|
778 |
+
// This job is not finished yet
|
779 |
+
if( $this->options->job->total > $this->options->job->start ) {
|
780 |
+
return false;
|
781 |
+
}
|
782 |
+
|
783 |
+
// Add it to cloned tables listing
|
784 |
+
$this->options->clonedTables[] = $this->options->tables[$this->options->currentStep];
|
785 |
+
|
786 |
+
// Reset job
|
787 |
+
$this->options->job = new \stdClass();
|
788 |
+
|
789 |
+
return true;
|
790 |
+
}
|
791 |
+
|
792 |
+
/**
|
793 |
+
* Drop table if necessary
|
794 |
+
* @param string $new
|
795 |
+
*/
|
796 |
+
private function dropTable( $new ) {
|
797 |
+
$old = $this->stagingDb->get_var( $this->stagingDb->prepare( "SHOW TABLES LIKE %s", $new ) );
|
798 |
+
|
799 |
+
if( !$this->shouldDropTable( $new, $old ) ) {
|
800 |
+
return;
|
801 |
+
}
|
802 |
+
|
803 |
+
$this->log( "DB Processing: {$new} already exists, dropping it first" );
|
804 |
+
$this->stagingDb->query( "DROP TABLE {$new}" );
|
805 |
+
}
|
806 |
+
|
807 |
+
/**
|
808 |
+
* Check if table needs to be dropped
|
809 |
+
* @param string $new
|
810 |
+
* @param string $old
|
811 |
+
* @return bool
|
812 |
+
*/
|
813 |
+
private function shouldDropTable( $new, $old ) {
|
814 |
+
return (
|
815 |
+
$old == $new &&
|
816 |
+
(
|
817 |
+
!isset( $this->options->job->current ) ||
|
818 |
+
!isset( $this->options->job->start ) ||
|
819 |
+
0 == $this->options->job->start
|
820 |
+
)
|
821 |
+
);
|
822 |
+
}
|
823 |
+
|
824 |
+
/**
|
825 |
+
* Check if WP is installed in subdir
|
826 |
+
* @return boolean
|
827 |
+
*/
|
828 |
+
private function isSubDir() {
|
829 |
+
// Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
|
830 |
+
// This is happening much more often than you would expect
|
831 |
+
$siteurl = preg_replace( '#^https?://#', '', rtrim( get_option( 'siteurl' ), '/' ) );
|
832 |
+
$home = preg_replace( '#^https?://#', '', rtrim( get_option( 'home' ), '/' ) );
|
833 |
+
|
834 |
+
if( $home !== $siteurl ) {
|
835 |
+
return true;
|
836 |
+
}
|
837 |
+
return false;
|
838 |
+
}
|
839 |
+
|
840 |
+
}
|
apps/Backend/Modules/Jobs/Scan.php
CHANGED
@@ -213,20 +213,20 @@ class Scan extends Job {
|
|
213 |
return null;
|
214 |
}
|
215 |
|
216 |
-
$freeSpace = @disk_free_space( \WPStaging\WPStaging::getWPpath() );
|
217 |
-
|
218 |
-
if( false === $freeSpace ) {
|
219 |
-
$data = array(
|
220 |
-
'freespace' => false,
|
221 |
-
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( \WPStaging\WPStaging::getWPpath() ) )
|
222 |
-
);
|
223 |
-
echo json_encode( $data );
|
224 |
-
die();
|
225 |
-
}
|
226 |
|
227 |
|
228 |
$data = array(
|
229 |
-
'freespace' => $this->formatSize( $freeSpace ),
|
230 |
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( \WPStaging\WPStaging::getWPpath() ) )
|
231 |
);
|
232 |
|
213 |
return null;
|
214 |
}
|
215 |
|
216 |
+
// $freeSpace = @disk_free_space( \WPStaging\WPStaging::getWPpath() );
|
217 |
+
//
|
218 |
+
// if( false === $freeSpace ) {
|
219 |
+
// $data = array(
|
220 |
+
// 'freespace' => false,
|
221 |
+
// 'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( \WPStaging\WPStaging::getWPpath() ) )
|
222 |
+
// );
|
223 |
+
// echo json_encode( $data );
|
224 |
+
// die();
|
225 |
+
// }
|
226 |
|
227 |
|
228 |
$data = array(
|
229 |
+
//'freespace' => $this->formatSize( $freeSpace ),
|
230 |
'usedspace' => $this->formatSize( $this->getDirectorySizeInclSubdirs( \WPStaging\WPStaging::getWPpath() ) )
|
231 |
);
|
232 |
|
apps/Backend/Modules/Jobs/SearchReplace.php
CHANGED
@@ -235,20 +235,20 @@ class SearchReplace extends JobExecutable {
|
|
235 |
* @access public
|
236 |
* @return int
|
237 |
*/
|
238 |
-
private function get_pages_in_table( $table ) {
|
239 |
-
|
240 |
-
// Table does not exist
|
241 |
-
$table = str_replace( $this->options->prefix . '.', null, $table );
|
242 |
-
$result = $this->db->query( "SHOW TABLES LIKE '{$table}'" );
|
243 |
-
if( !$result || 0 === $result ) {
|
244 |
-
return 0;
|
245 |
-
}
|
246 |
-
|
247 |
-
$table = esc_sql( $table );
|
248 |
-
$rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
|
249 |
-
$pages = ceil( $rows / $this->settings->querySRLimit );
|
250 |
-
return absint( $pages );
|
251 |
-
}
|
252 |
|
253 |
/**
|
254 |
* Gets the columns in a table.
|
@@ -293,9 +293,9 @@ class SearchReplace extends JobExecutable {
|
|
293 |
}
|
294 |
|
295 |
// Load up the default settings for this chunk.
|
296 |
-
$table
|
297 |
-
|
298 |
-
|
299 |
|
300 |
|
301 |
$args['search_for'] = array(
|
@@ -316,7 +316,7 @@ class SearchReplace extends JobExecutable {
|
|
316 |
$args['replace_guids'] = 'off';
|
317 |
$args['dry_run'] = 'off';
|
318 |
$args['case_insensitive'] = false;
|
319 |
-
|
320 |
$args['skip_transients'] = 'on';
|
321 |
|
322 |
|
@@ -336,11 +336,39 @@ class SearchReplace extends JobExecutable {
|
|
336 |
|
337 |
$current_row = 0;
|
338 |
$start = $this->options->job->start;
|
|
|
|
|
|
|
339 |
$end = $this->settings->querySRLimit;
|
340 |
|
341 |
-
// Grab the content of the table.
|
342 |
$data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
343 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
// Filter certain rows (of other plugins)
|
345 |
$filter = array(
|
346 |
'Admin_custome_login_Slidshow',
|
@@ -373,7 +401,7 @@ class SearchReplace extends JobExecutable {
|
|
373 |
continue;
|
374 |
}
|
375 |
// Skip rows with more than 5MB to save memory
|
376 |
-
if( isset( $row['option_value'] ) && strlen($row['option_value']) >= 5000000
|
377 |
continue;
|
378 |
}
|
379 |
|
@@ -382,10 +410,10 @@ class SearchReplace extends JobExecutable {
|
|
382 |
|
383 |
$dataRow = $row[$column];
|
384 |
|
385 |
-
|
386 |
// Skip rows larger than 5MB
|
387 |
-
$size = strlen($dataRow);
|
388 |
-
if
|
389 |
continue;
|
390 |
}
|
391 |
|
@@ -401,20 +429,17 @@ class SearchReplace extends JobExecutable {
|
|
401 |
}
|
402 |
|
403 |
// Skip mail addresses
|
404 |
-
if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->options->homeHostname ) ) {
|
405 |
-
continue;
|
406 |
-
}
|
407 |
-
|
408 |
-
|
409 |
// Check options table
|
410 |
if( $this->options->prefix . 'options' === $table ) {
|
411 |
|
412 |
// Skip certain options
|
413 |
-
if( isset( $should_skip ) && true === $should_skip ) {
|
414 |
-
$should_skip = false;
|
415 |
-
continue;
|
416 |
-
}
|
417 |
-
|
418 |
// Skip this row
|
419 |
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
420 |
'wpstg_existing_clones' === $dataRow ||
|
@@ -423,7 +448,8 @@ class SearchReplace extends JobExecutable {
|
|
423 |
'siteurl' === $dataRow ||
|
424 |
'home' === $dataRow
|
425 |
) {
|
426 |
-
|
|
|
427 |
}
|
428 |
}
|
429 |
|
@@ -477,13 +503,15 @@ class SearchReplace extends JobExecutable {
|
|
477 |
unset( $where_sql );
|
478 |
unset( $sql );
|
479 |
unset( $current_row );
|
480 |
-
|
481 |
|
482 |
// DB Flush
|
483 |
$this->db->flush();
|
484 |
return true;
|
485 |
}
|
486 |
|
|
|
|
|
487 |
/**
|
488 |
* Adapted from interconnect/it's search/replace script.
|
489 |
*
|
@@ -515,24 +543,23 @@ class SearchReplace extends JobExecutable {
|
|
515 |
$data = $tmp;
|
516 |
unset( $tmp );
|
517 |
} elseif( is_object( $data ) ) {
|
518 |
-
|
519 |
-
// Is no valid or is incomplete object
|
520 |
-
// Do not use it any longer because it increases the memory consumption and can lead to memory exhausted errors
|
521 |
-
// if (!$this->isValidObject($data)){
|
522 |
-
// return $data;
|
523 |
-
// }
|
524 |
-
|
525 |
-
$tmp = $data;
|
526 |
$props = get_object_vars( $data );
|
527 |
-
|
528 |
-
|
529 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
530 |
}
|
531 |
-
$
|
|
|
|
|
|
|
|
|
532 |
}
|
533 |
-
|
534 |
-
$data = $tmp;
|
535 |
-
unset( $tmp );
|
536 |
} else {
|
537 |
if( is_string( $data ) ) {
|
538 |
if( !empty( $from ) && !empty( $to ) ) {
|
@@ -556,27 +583,27 @@ class SearchReplace extends JobExecutable {
|
|
556 |
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
557 |
* @return boolean
|
558 |
*/
|
559 |
-
private function isValidObject($data){
|
560 |
-
if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
561 |
-
return false;
|
562 |
-
}
|
563 |
-
|
564 |
-
$invalid_class_props = get_object_vars( $data );
|
565 |
-
|
566 |
-
if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
567 |
-
// Assume it must be an valid object
|
568 |
-
return true;
|
569 |
-
}
|
570 |
-
|
571 |
-
$invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
572 |
-
|
573 |
-
if( !empty( $invalid_object_class ) ) {
|
574 |
-
return false;
|
575 |
-
}
|
576 |
-
|
577 |
-
// Assume it must be an valid object
|
578 |
-
return true;
|
579 |
-
}
|
580 |
|
581 |
/**
|
582 |
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
235 |
* @access public
|
236 |
* @return int
|
237 |
*/
|
238 |
+
// private function get_pages_in_table( $table ) {
|
239 |
+
//
|
240 |
+
// // Table does not exist
|
241 |
+
// $table = str_replace( $this->options->prefix . '.', null, $table );
|
242 |
+
// $result = $this->db->query( "SHOW TABLES LIKE '{$table}'" );
|
243 |
+
// if( !$result || 0 === $result ) {
|
244 |
+
// return 0;
|
245 |
+
// }
|
246 |
+
//
|
247 |
+
// $table = esc_sql( $table );
|
248 |
+
// $rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
|
249 |
+
// $pages = ceil( $rows / $this->settings->querySRLimit );
|
250 |
+
// return absint( $pages );
|
251 |
+
// }
|
252 |
|
253 |
/**
|
254 |
* Gets the columns in a table.
|
293 |
}
|
294 |
|
295 |
// Load up the default settings for this chunk.
|
296 |
+
$table = esc_sql( $table );
|
297 |
+
//$current_page = $this->options->job->start + $this->settings->querySRLimit;
|
298 |
+
//$pages = $this->get_pages_in_table( $table );
|
299 |
|
300 |
|
301 |
$args['search_for'] = array(
|
316 |
$args['replace_guids'] = 'off';
|
317 |
$args['dry_run'] = 'off';
|
318 |
$args['case_insensitive'] = false;
|
319 |
+
//$args['replace_mails'] = 'off';
|
320 |
$args['skip_transients'] = 'on';
|
321 |
|
322 |
|
336 |
|
337 |
$current_row = 0;
|
338 |
$start = $this->options->job->start;
|
339 |
+
|
340 |
+
//Make sure value is never smaller than 1 or greater than 20000
|
341 |
+
//$end = $this->settings->querySRLimit == '0' || empty( $this->settings->querySRLimit ) ? 1 : $this->settings->querySRLimit > 20000 ? 20000 : $this->settings->querySRLimit;
|
342 |
$end = $this->settings->querySRLimit;
|
343 |
|
344 |
+
// Grab the content of the current table.
|
345 |
$data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
346 |
|
347 |
+
// // Current memory usage
|
348 |
+
// $memUsage = memory_get_usage();
|
349 |
+
//
|
350 |
+
// // 80% Memory Limit
|
351 |
+
// $memLimit = wpstg_get_memory_in_bytes( ini_get( 'memory_limit' ) ) * 0.8;
|
352 |
+
// //$memLimit = 10;
|
353 |
+
//
|
354 |
+
// // Memory limit higher than 80% - reduce the numbers and try again
|
355 |
+
// if( $memUsage >= $memLimit ) {
|
356 |
+
// unset( $data );
|
357 |
+
// $end = ceil( $end - 100 );
|
358 |
+
// $this->settings->querySRLimit = $end;
|
359 |
+
// update_option( 'wpstg_settings', $this->settings );
|
360 |
+
// // Grab again the content of the current table.
|
361 |
+
// $data = $this->db->get_results( "SELECT * FROM $table LIMIT $start, $end", ARRAY_A );
|
362 |
+
// }
|
363 |
+
//
|
364 |
+
// // If memory limit lower than 50% - increase the numbers for the next batch to get more speed
|
365 |
+
// $memLimit = wpstg_get_memory_in_bytes( ini_get( 'memory_limit' ) ) * 0.5;
|
366 |
+
// if( $memUsage < $memLimit ) {
|
367 |
+
// $end = ceil( $end + 100 );
|
368 |
+
// $this->settings->querySRLimit = $end;
|
369 |
+
// update_option( 'wpstg_settings', $this->settings );
|
370 |
+
// }
|
371 |
+
|
372 |
// Filter certain rows (of other plugins)
|
373 |
$filter = array(
|
374 |
'Admin_custome_login_Slidshow',
|
401 |
continue;
|
402 |
}
|
403 |
// Skip rows with more than 5MB to save memory
|
404 |
+
if( isset( $row['option_value'] ) && strlen( $row['option_value'] ) >= 5000000 ) {
|
405 |
continue;
|
406 |
}
|
407 |
|
410 |
|
411 |
$dataRow = $row[$column];
|
412 |
|
413 |
+
|
414 |
// Skip rows larger than 5MB
|
415 |
+
$size = strlen( $dataRow );
|
416 |
+
if( $size >= 5000000 ) {
|
417 |
continue;
|
418 |
}
|
419 |
|
429 |
}
|
430 |
|
431 |
// Skip mail addresses
|
432 |
+
// if( 'off' === $args['replace_mails'] && false !== strpos( $dataRow, '@' . $this->options->homeHostname ) ) {
|
433 |
+
// continue;
|
434 |
+
// }
|
|
|
|
|
435 |
// Check options table
|
436 |
if( $this->options->prefix . 'options' === $table ) {
|
437 |
|
438 |
// Skip certain options
|
439 |
+
// if( isset( $should_skip ) && true === $should_skip ) {
|
440 |
+
// $should_skip = false;
|
441 |
+
// continue;
|
442 |
+
// }
|
|
|
443 |
// Skip this row
|
444 |
if( 'wpstg_existing_clones_beta' === $dataRow ||
|
445 |
'wpstg_existing_clones' === $dataRow ||
|
448 |
'siteurl' === $dataRow ||
|
449 |
'home' === $dataRow
|
450 |
) {
|
451 |
+
//$should_skip = true;
|
452 |
+
continue;
|
453 |
}
|
454 |
}
|
455 |
|
503 |
unset( $where_sql );
|
504 |
unset( $sql );
|
505 |
unset( $current_row );
|
506 |
+
|
507 |
|
508 |
// DB Flush
|
509 |
$this->db->flush();
|
510 |
return true;
|
511 |
}
|
512 |
|
513 |
+
|
514 |
+
|
515 |
/**
|
516 |
* Adapted from interconnect/it's search/replace script.
|
517 |
*
|
543 |
$data = $tmp;
|
544 |
unset( $tmp );
|
545 |
} elseif( is_object( $data ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
546 |
$props = get_object_vars( $data );
|
547 |
+
|
548 |
+
// Do a search & replace
|
549 |
+
if( empty( $props['__PHP_Incomplete_Class_Name'] ) ) {
|
550 |
+
$tmp = $data;
|
551 |
+
foreach ( $props as $key => $value ) {
|
552 |
+
if( $key === '' || ord( $key[0] ) === 0 ) {
|
553 |
+
continue;
|
554 |
+
}
|
555 |
+
$tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive );
|
556 |
}
|
557 |
+
$data = $tmp;
|
558 |
+
$tmp = '';
|
559 |
+
$props = '';
|
560 |
+
unset( $tmp );
|
561 |
+
unset( $props );
|
562 |
}
|
|
|
|
|
|
|
563 |
} else {
|
564 |
if( is_string( $data ) ) {
|
565 |
if( !empty( $from ) && !empty( $to ) ) {
|
583 |
* Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
|
584 |
* @return boolean
|
585 |
*/
|
586 |
+
// private function isValidObject($data){
|
587 |
+
// if( !is_object( $data ) || gettype( $data ) != 'object' ) {
|
588 |
+
// return false;
|
589 |
+
// }
|
590 |
+
//
|
591 |
+
// $invalid_class_props = get_object_vars( $data );
|
592 |
+
//
|
593 |
+
// if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
|
594 |
+
// // Assume it must be an valid object
|
595 |
+
// return true;
|
596 |
+
// }
|
597 |
+
//
|
598 |
+
// $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
|
599 |
+
//
|
600 |
+
// if( !empty( $invalid_object_class ) ) {
|
601 |
+
// return false;
|
602 |
+
// }
|
603 |
+
//
|
604 |
+
// // Assume it must be an valid object
|
605 |
+
// return true;
|
606 |
+
// }
|
607 |
|
608 |
/**
|
609 |
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
|
apps/Backend/Modules/Jobs/Updating.php
CHANGED
@@ -38,13 +38,14 @@ class Updating extends Job {
|
|
38 |
|
39 |
// Generate Options
|
40 |
// Clone
|
41 |
-
|
42 |
-
$this->options->
|
43 |
-
$this->options->
|
44 |
-
$this->options->
|
45 |
-
$this->options->
|
46 |
-
$this->options->
|
47 |
-
$this->options->
|
|
|
48 |
'.htaccess',
|
49 |
'.DS_Store',
|
50 |
'.git',
|
38 |
|
39 |
// Generate Options
|
40 |
// Clone
|
41 |
+
//$this->options->clone = $_POST["cloneID"];
|
42 |
+
$this->options->clone = preg_replace( "#\W+#", '-', strtolower( $_POST["cloneID"] ) );
|
43 |
+
$this->options->cloneDirectoryName = preg_replace( "#\W+#", '-', strtolower( $this->options->clone ) );
|
44 |
+
$this->options->cloneNumber = 1;
|
45 |
+
$this->options->includedDirectories = array();
|
46 |
+
$this->options->excludedDirectories = array();
|
47 |
+
$this->options->extraDirectories = array();
|
48 |
+
$this->options->excludedFiles = array(
|
49 |
'.htaccess',
|
50 |
'.DS_Store',
|
51 |
'.git',
|
apps/Backend/Upgrade/Upgrade.php
CHANGED
@@ -79,10 +79,32 @@ class Upgrade {
|
|
79 |
$this->upgrade2_1_2();
|
80 |
$this->upgrade2_2_0();
|
81 |
$this->upgrade2_4_4();
|
|
|
82 |
|
83 |
$this->setVersion();
|
84 |
}
|
85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
private function upgrade2_4_4() {
|
87 |
// Previous version lower than 2.4.4
|
88 |
if( version_compare( $this->previousVersion, '2.4.4', '<' ) ) {
|
79 |
$this->upgrade2_1_2();
|
80 |
$this->upgrade2_2_0();
|
81 |
$this->upgrade2_4_4();
|
82 |
+
$this->upgrade2_5_9();
|
83 |
|
84 |
$this->setVersion();
|
85 |
}
|
86 |
|
87 |
+
/**
|
88 |
+
* Fix array keys of staging sites
|
89 |
+
*/
|
90 |
+
private function upgrade2_5_9() {
|
91 |
+
// Previous version lower than 2.5.9
|
92 |
+
if( version_compare( $this->previousVersion, '2.5.9', '<' ) ) {
|
93 |
+
|
94 |
+
// Current options
|
95 |
+
$sites = get_option( "wpstg_existing_clones_beta", array() );
|
96 |
+
|
97 |
+
$new = array();
|
98 |
+
|
99 |
+
// Fix keys. Replace white spaces with dash character
|
100 |
+
foreach ( $sites as $oldKey => $site ) {
|
101 |
+
$key = preg_replace( "#\W+#", '-', strtolower( $oldKey ) );
|
102 |
+
$new[$key] = $sites[$oldKey];
|
103 |
+
}
|
104 |
+
update_option( "wpstg_existing_clones_beta", $new );
|
105 |
+
}
|
106 |
+
}
|
107 |
+
|
108 |
private function upgrade2_4_4() {
|
109 |
// Previous version lower than 2.4.4
|
110 |
if( version_compare( $this->previousVersion, '2.4.4', '<' ) ) {
|
apps/Backend/helpers/wp-config.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The base configuration for WordPress
|
4 |
+
*
|
5 |
+
* The wp-config.php creation script uses this file during the
|
6 |
+
* installation. You don't have to use the web site, you can
|
7 |
+
* copy this file to "wp-config.php" and fill in the values.
|
8 |
+
*
|
9 |
+
* This file contains the following configurations:
|
10 |
+
*
|
11 |
+
* * MySQL settings
|
12 |
+
* * Secret keys
|
13 |
+
* * Database table prefix
|
14 |
+
* * ABSPATH
|
15 |
+
*
|
16 |
+
* @link https://codex.wordpress.org/Editing_wp-config.php
|
17 |
+
*
|
18 |
+
* @package WordPress
|
19 |
+
*/
|
20 |
+
|
21 |
+
// ** MySQL settings ** //
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Authentication Unique Keys and Salts.
|
25 |
+
*
|
26 |
+
* Change these to different unique phrases!
|
27 |
+
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
|
28 |
+
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
|
29 |
+
*
|
30 |
+
* @since 2.6.0
|
31 |
+
*/
|
32 |
+
define( 'AUTH_KEY', 'cwC1]Y4Qz<-jS?V W0U*CbW~$yY9U@=-t5X{][-S0GF~LqY yt[ChoYm@?`}iJzm' );
|
33 |
+
define( 'SECURE_AUTH_KEY', 'dmZ5$.d:gCbUxX)FZ+[h-t81O>Yd5W!x<:D[q;6{A?;Q0F>fvKcjaM V0V?-XD(t' );
|
34 |
+
define( 'LOGGED_IN_KEY', '{B%(DDM,))zFtDD8gLk;N^EK`iiG<V`XNZ/k~d]ne^t/MLN85o5ILrMWC!.:cq.X' );
|
35 |
+
define( 'NONCE_KEY', 'tvt!~WL>)x{ ``SK6ZO^/R1lwZPerR?&(W>]h/da(Z^M$2?)ZDVsICxQV3?/h6)U' );
|
36 |
+
define( 'AUTH_SALT', ';%e$?CJm$s-N!a;(B;NM/>_~gDuPa(VM1t:nUvQ+LZw;e]1)_`-qwCZe,-@^{Xd%' );
|
37 |
+
define( 'SECURE_AUTH_SALT', '? nh*~x!7Jm^4E4w(y(qmaPK:$l5aB6!n6L}$IN+cXZSsE?<2~FfK|qN=s:=P+(c' );
|
38 |
+
define( 'LOGGED_IN_SALT', '=Ty=Q):}E/[pW3y4IDN@Bas/&-MInTxFXziE)9H9^rnl g7TUj-7OP*UX2Oyz=Y$' );
|
39 |
+
define( 'NONCE_SALT', 'HATY?A^EQ#F;oN8!W-oe5P%)aFaeU,E;rLFmRm&u-<g6tL9k(pyh77_,Kc _q2BY' );
|
40 |
+
define( 'WP_CACHE_KEY_SALT', '5Y@>B@S8O{a w%ASH BX!;wu/RBk.HT[~R{csF.r)5f0q/YTy%$8lwV4o0eygz};' );
|
41 |
+
|
42 |
+
/**
|
43 |
+
* WordPress Database Table prefix.
|
44 |
+
*
|
45 |
+
* You can have multiple installations in one database if you give each
|
46 |
+
* a unique prefix. Only numbers, letters, and underscores please!
|
47 |
+
*/
|
48 |
+
$table_prefix = 'wp_';
|
49 |
+
|
50 |
+
/* That's all, stop editing! Happy blogging. */
|
51 |
+
|
52 |
+
/** Absolute path to the WordPress directory. */
|
53 |
+
if ( ! defined( 'ABSPATH' ) )
|
54 |
+
define( 'ABSPATH', dirname( __FILE__ ) . '/' );
|
55 |
+
|
56 |
+
/** Sets up WordPress vars and included files. */
|
57 |
+
require_once ABSPATH . 'wp-settings.php';
|
apps/Backend/public/css/wpstg-admin.css
CHANGED
@@ -246,16 +246,17 @@
|
|
246 |
}
|
247 |
|
248 |
.wpstg-clone {
|
249 |
-
border:
|
250 |
margin-bottom: 5px;
|
251 |
padding: 5px 10px;
|
252 |
width: 400px;
|
253 |
position: relative;
|
254 |
overflow: hidden;
|
255 |
transition: border-color .2s ease-in-out;
|
256 |
-
background-color: #
|
257 |
}
|
258 |
|
|
|
259 |
.wpstg-clone.active {
|
260 |
border-color: #1d94cf;;
|
261 |
}
|
@@ -266,9 +267,14 @@
|
|
266 |
max-width: 130px;
|
267 |
text-decoration: none;
|
268 |
font-weight: bold;
|
269 |
-
color
|
|
|
|
|
|
|
270 |
}
|
271 |
|
|
|
|
|
272 |
.wpstg-clone-action {
|
273 |
background: #ffffff;
|
274 |
border-left: 1px solid #ccc;
|
@@ -486,6 +492,20 @@
|
|
486 |
box-shadow: 0 0 2px rgba(255, 66, 53, .8);
|
487 |
}
|
488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
489 |
#wpstg-clone-path {
|
490 |
margin-left: 10px;
|
491 |
width: 350px;
|
@@ -497,8 +517,9 @@
|
|
497 |
|
498 |
#wpstg-clone-id-error {
|
499 |
display: block;
|
500 |
-
|
501 |
-
|
|
|
502 |
}
|
503 |
|
504 |
#wpstg-start-cloning + .wpstg-error-msg {
|
@@ -537,13 +558,13 @@
|
|
537 |
}
|
538 |
|
539 |
.wpstg-tabs-wrapper {
|
540 |
-
border: 1px solid #ddd;
|
541 |
border-right: none;
|
542 |
-
border-left: none
|
543 |
}
|
544 |
|
545 |
.wpstg-tab-section {
|
546 |
-
border: 1px solid #ddd;
|
547 |
border-right: none;
|
548 |
border-left: none;
|
549 |
display: none;
|
@@ -557,7 +578,7 @@
|
|
557 |
}
|
558 |
|
559 |
.wpstg-tab-header {
|
560 |
-
border: 1px solid #ddd;
|
561 |
border-right: none;
|
562 |
border-left: none;
|
563 |
color: #444;
|
@@ -819,7 +840,7 @@
|
|
819 |
.wpstg-staging-info {
|
820 |
clear:both;
|
821 |
float: left;
|
822 |
-
color:
|
823 |
font-size: 12px;
|
824 |
}
|
825 |
|
@@ -866,6 +887,32 @@
|
|
866 |
text-decoration: none;
|
867 |
}
|
868 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
869 |
|
870 |
.wpstg-report-issue-form {
|
871 |
position: absolute;
|
246 |
}
|
247 |
|
248 |
.wpstg-clone {
|
249 |
+
border: 5px solid #ffffff;
|
250 |
margin-bottom: 5px;
|
251 |
padding: 5px 10px;
|
252 |
width: 400px;
|
253 |
position: relative;
|
254 |
overflow: hidden;
|
255 |
transition: border-color .2s ease-in-out;
|
256 |
+
background-color: #25a1f0;
|
257 |
}
|
258 |
|
259 |
+
|
260 |
.wpstg-clone.active {
|
261 |
border-color: #1d94cf;;
|
262 |
}
|
267 |
max-width: 130px;
|
268 |
text-decoration: none;
|
269 |
font-weight: bold;
|
270 |
+
color:white;
|
271 |
+
}
|
272 |
+
.wpstg-clone-title:hover{
|
273 |
+
color:#f1f1f1;
|
274 |
}
|
275 |
|
276 |
+
|
277 |
+
|
278 |
.wpstg-clone-action {
|
279 |
background: #ffffff;
|
280 |
border-left: 1px solid #ccc;
|
492 |
box-shadow: 0 0 2px rgba(255, 66, 53, .8);
|
493 |
}
|
494 |
|
495 |
+
#wpstg-new-clone {
|
496 |
+
background: #25a1f0;
|
497 |
+
border-color: #2188c9;
|
498 |
+
/*box-shadow: inset 0 2px 0 #006799;*/
|
499 |
+
background-color: #25a1f0;
|
500 |
+
}
|
501 |
+
#wpstg-new-clone:hover {
|
502 |
+
background: #259be6;
|
503 |
+
border-color: #2188c9;
|
504 |
+
/*border-width: 2px;*/
|
505 |
+
/*box-shadow: inset 0 2px 0 #006799;*/
|
506 |
+
background-color: #259be6;
|
507 |
+
}
|
508 |
+
|
509 |
#wpstg-clone-path {
|
510 |
margin-left: 10px;
|
511 |
width: 350px;
|
517 |
|
518 |
#wpstg-clone-id-error {
|
519 |
display: block;
|
520 |
+
background-color:#f0f8ff;
|
521 |
+
padding:10px;
|
522 |
+
margin: 20px;
|
523 |
}
|
524 |
|
525 |
#wpstg-start-cloning + .wpstg-error-msg {
|
558 |
}
|
559 |
|
560 |
.wpstg-tabs-wrapper {
|
561 |
+
/*border: 1px solid #ddd;
|
562 |
border-right: none;
|
563 |
+
border-left: none;*/
|
564 |
}
|
565 |
|
566 |
.wpstg-tab-section {
|
567 |
+
border-bottom: 1px solid #ddd;
|
568 |
border-right: none;
|
569 |
border-left: none;
|
570 |
display: none;
|
578 |
}
|
579 |
|
580 |
.wpstg-tab-header {
|
581 |
+
border-bottom: 1px solid #ddd;
|
582 |
border-right: none;
|
583 |
border-left: none;
|
584 |
color: #444;
|
840 |
.wpstg-staging-info {
|
841 |
clear:both;
|
842 |
float: left;
|
843 |
+
color:white;
|
844 |
font-size: 12px;
|
845 |
}
|
846 |
|
887 |
text-decoration: none;
|
888 |
}
|
889 |
|
890 |
+
.wpstg-blue-primary {
|
891 |
+
display: inline-block;
|
892 |
+
text-decoration: none;
|
893 |
+
font-size: 13px;
|
894 |
+
line-height: 26px;
|
895 |
+
height: 28px;
|
896 |
+
margin: 0;
|
897 |
+
padding: 0 10px 1px;
|
898 |
+
cursor: pointer;
|
899 |
+
border-width: 1px;
|
900 |
+
border-style: solid;
|
901 |
+
-webkit-appearance: none;
|
902 |
+
border-radius: 3px;
|
903 |
+
white-space: nowrap;
|
904 |
+
box-sizing: border-box;
|
905 |
+
background: #25a1f0;
|
906 |
+
border-color: #2188c9;
|
907 |
+
/*box-shadow: 0 1px 0 #006799;*/
|
908 |
+
color: #fff;
|
909 |
+
text-decoration: none;
|
910 |
+
text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
|
911 |
+
}
|
912 |
+
.wpstg-blue-primary:hover {
|
913 |
+
background-color: #259be6;
|
914 |
+
}
|
915 |
+
|
916 |
|
917 |
.wpstg-report-issue-form {
|
918 |
position: absolute;
|
apps/Backend/public/js/wpstg-admin.js
CHANGED
@@ -550,7 +550,7 @@ var WPStaging = (function ($)
|
|
550 |
|
551 |
if ($this.data("action") === "wpstg_update") {
|
552 |
// Update Clone - confirmed
|
553 |
-
if (!confirm("
|
554 |
{
|
555 |
return false;
|
556 |
}
|
@@ -589,8 +589,8 @@ var WPStaging = (function ($)
|
|
589 |
if (false === response)
|
590 |
{
|
591 |
showError(
|
592 |
-
"Something went wrong!<br/><br/> Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low
|
593 |
-
"
|
594 |
"<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
|
595 |
);
|
596 |
}
|
@@ -599,7 +599,7 @@ var WPStaging = (function ($)
|
|
599 |
if (response.length < 1)
|
600 |
{
|
601 |
showError(
|
602 |
-
"Something went wrong! No response. Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low
|
603 |
"and try again. If that does not help, " +
|
604 |
"<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
|
605 |
);
|
@@ -706,7 +706,6 @@ var WPStaging = (function ($)
|
|
706 |
{
|
707 |
// Add directories from the root level
|
708 |
var extraDirectories = [];
|
709 |
-
|
710 |
$(".wpstg-dir input:checked.wpstg-extra").each(function () {
|
711 |
var $this = $(this);
|
712 |
extraDirectories.push(encodeURIComponent($this.val()));
|
@@ -843,13 +842,15 @@ var WPStaging = (function ($)
|
|
843 |
var deleteClone = function (clone)
|
844 |
{
|
845 |
|
|
|
|
|
846 |
ajax(
|
847 |
{
|
848 |
action: "wpstg_delete_clone",
|
849 |
clone: clone,
|
850 |
nonce: wpstg.nonce,
|
851 |
excludedTables: getExcludedTables(),
|
852 |
-
deleteDir:
|
853 |
},
|
854 |
function (response)
|
855 |
{
|
550 |
|
551 |
if ($this.data("action") === "wpstg_update") {
|
552 |
// Update Clone - confirmed
|
553 |
+
if (!confirm("STOP! This will overwrite your staging site with all selected data from the live site! This should be used only if you want to clone again your production site. Are you sure you want to do this? \n\nMake sure to exclude all tables and folders which you do not want to overwrite, first! \n\nDo not necessarily cancel the updating process! This can break your staging site. \n\n\Make sure you have a backop of your staging website before you proceed."))
|
554 |
{
|
555 |
return false;
|
556 |
}
|
589 |
if (false === response)
|
590 |
{
|
591 |
showError(
|
592 |
+
"Something went wrong!<br/><br/> Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low '" +
|
593 |
+
"and try again. If that does not help, " +
|
594 |
"<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
|
595 |
);
|
596 |
}
|
599 |
if (response.length < 1)
|
600 |
{
|
601 |
showError(
|
602 |
+
"Something went wrong! No response. Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low '" +
|
603 |
"and try again. If that does not help, " +
|
604 |
"<a href='https://wp-staging.com/support/' target='_blank'>open a support ticket</a> "
|
605 |
);
|
706 |
{
|
707 |
// Add directories from the root level
|
708 |
var extraDirectories = [];
|
|
|
709 |
$(".wpstg-dir input:checked.wpstg-extra").each(function () {
|
710 |
var $this = $(this);
|
711 |
extraDirectories.push(encodeURIComponent($this.val()));
|
842 |
var deleteClone = function (clone)
|
843 |
{
|
844 |
|
845 |
+
var deleteDir = $("#deleteDirectory:checked").data("deletepath");
|
846 |
+
|
847 |
ajax(
|
848 |
{
|
849 |
action: "wpstg_delete_clone",
|
850 |
clone: clone,
|
851 |
nonce: wpstg.nonce,
|
852 |
excludedTables: getExcludedTables(),
|
853 |
+
deleteDir: deleteDir
|
854 |
},
|
855 |
function (response)
|
856 |
{
|
apps/Backend/views/clone/ajax/delete-confirmation.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<div class="wpstg-notice-alert wpstg-failed">
|
2 |
<h4 style="margin:0">
|
3 |
<?php
|
4 |
-
_e("
|
5 |
?>
|
6 |
</h4>
|
7 |
|
@@ -27,7 +27,7 @@
|
|
27 |
<?php
|
28 |
_e(
|
29 |
'Usually the preselected data can be deleted without any risk '.
|
30 |
-
'but in case something
|
31 |
'wp-staging'
|
32 |
);
|
33 |
?>
|
@@ -38,14 +38,19 @@
|
|
38 |
|
39 |
<a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
|
40 |
<span class="wpstg-tab-triangle">►</span>
|
41 |
-
<?php echo __("
|
42 |
</a>
|
43 |
|
44 |
<!-- Database -->
|
45 |
<div class="wpstg-tab-section" id="wpstg-scanning-db">
|
46 |
<h4 style="margin:0;">
|
47 |
-
<?php _e("Unselect database tables you do not want to delete:", "wp-staging")?>
|
48 |
</h4>
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
<?php foreach ($delete->getTables() as $table):?>
|
51 |
<div class="wpstg-db-table">
|
@@ -58,9 +63,9 @@
|
|
58 |
</span>
|
59 |
</div>
|
60 |
<?php endforeach ?>
|
61 |
-
<div>
|
62 |
<a href="#" class="wpstg-button-unselect">
|
63 |
-
|
64 |
</a>
|
65 |
</div>
|
66 |
</div>
|
@@ -73,13 +78,13 @@
|
|
73 |
|
74 |
<!-- Files -->
|
75 |
<div class="wpstg-tab-section" id="wpstg-scanning-files">
|
76 |
-
<h4 style="margin:0;">
|
77 |
-
<?php _e("
|
78 |
</h4>
|
79 |
|
80 |
<div class="wpstg-dir">
|
81 |
<label>
|
82 |
-
<input id="deleteDirectory" type="checkbox" class="wpstg-check-dir" name="deleteDirectory" value="1" checked>
|
83 |
<?php echo $clone->path;?>
|
84 |
<span class="wpstg-size-info"><?php echo isset($clone->size) ? $clone->size : ''; ?></span>
|
85 |
</label>
|
1 |
<div class="wpstg-notice-alert wpstg-failed">
|
2 |
<h4 style="margin:0">
|
3 |
<?php
|
4 |
+
_e("Note: The selected tables below will be deleted. <br/>Verify that before you delete them.", "wp-staging")
|
5 |
?>
|
6 |
</h4>
|
7 |
|
27 |
<?php
|
28 |
_e(
|
29 |
'Usually the preselected data can be deleted without any risk '.
|
30 |
+
'but in case something is going wrong you better verify it.',
|
31 |
'wp-staging'
|
32 |
);
|
33 |
?>
|
38 |
|
39 |
<a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
|
40 |
<span class="wpstg-tab-triangle">►</span>
|
41 |
+
<?php echo __("Database tables to remove", "wp-staging")?>
|
42 |
</a>
|
43 |
|
44 |
<!-- Database -->
|
45 |
<div class="wpstg-tab-section" id="wpstg-scanning-db">
|
46 |
<h4 style="margin:0;">
|
47 |
+
<?php _e("Unselect all database tables you do not want to delete:", "wp-staging")?>
|
48 |
</h4>
|
49 |
+
<div style="margin-bottom:6px;margin-top:6px;">
|
50 |
+
<a href="#" class="wpstg-button-unselect">
|
51 |
+
Unselect All
|
52 |
+
</a>
|
53 |
+
</div>
|
54 |
|
55 |
<?php foreach ($delete->getTables() as $table):?>
|
56 |
<div class="wpstg-db-table">
|
63 |
</span>
|
64 |
</div>
|
65 |
<?php endforeach ?>
|
66 |
+
<div style="margin-bottom:6px;margin-top:6px;">
|
67 |
<a href="#" class="wpstg-button-unselect">
|
68 |
+
Unselect All
|
69 |
</a>
|
70 |
</div>
|
71 |
</div>
|
78 |
|
79 |
<!-- Files -->
|
80 |
<div class="wpstg-tab-section" id="wpstg-scanning-files">
|
81 |
+
<h4 style="margin:0;margin-bottom:10px;">
|
82 |
+
<?php _e("Selected folder and all of its subfolders and files will be deleted. <br/>Unselect it if you want to keep the staging site file data.", "wp-staging") ?>
|
83 |
</h4>
|
84 |
|
85 |
<div class="wpstg-dir">
|
86 |
<label>
|
87 |
+
<input id="deleteDirectory" type="checkbox" class="wpstg-check-dir" name="deleteDirectory" value="1" checked data-deletepath="<?php echo urlencode($clone->path);?>">
|
88 |
<?php echo $clone->path;?>
|
89 |
<span class="wpstg-size-info"><?php echo isset($clone->size) ? $clone->size : ''; ?></span>
|
90 |
</label>
|
apps/Backend/views/clone/ajax/scan.php
CHANGED
@@ -23,7 +23,7 @@
|
|
23 |
<h4 style="margin:0">
|
24 |
<?php
|
25 |
echo __(
|
26 |
-
"Select the tables
|
27 |
);
|
28 |
?>
|
29 |
<p></p>
|
@@ -128,7 +128,10 @@
|
|
128 |
|
129 |
</div>
|
130 |
|
131 |
-
<
|
|
|
|
|
|
|
132 |
<?php _e( "Back", "wp-staging" ) ?>
|
133 |
</button>
|
134 |
|
@@ -137,13 +140,11 @@ if( null !== $options->current ) {
|
|
137 |
$label = __( "Update Clone", "wp-staging" );
|
138 |
$action = 'wpstg_update';
|
139 |
|
140 |
-
echo '<button type="button" id="wpstg-start-updating" class="wpstg-next-step-link wpstg-link-btn
|
141 |
} else {
|
142 |
$label = __( "Start Cloning", "wp-staging" );
|
143 |
$action = 'wpstg_cloning';
|
144 |
|
145 |
-
echo '<button type="button" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn
|
146 |
}
|
147 |
?>
|
148 |
-
|
149 |
-
<a href="#" id="wpstg-check-space"><?php _e( 'Check Free Disk Space', 'wp-staging' ); ?></a>
|
23 |
<h4 style="margin:0">
|
24 |
<?php
|
25 |
echo __(
|
26 |
+
"Select the tables to copy. Tables beginning with the prefix '{$scan->prefix}' have already been selected.", "wp-staging"
|
27 |
);
|
28 |
?>
|
29 |
<p></p>
|
128 |
|
129 |
</div>
|
130 |
|
131 |
+
<strong>Important:</strong><a href="#" id="wpstg-check-space"><?php _e( 'Check required disk space', 'wp-staging' ); ?></a>
|
132 |
+
<p></p>
|
133 |
+
|
134 |
+
<button type="button" class="wpstg-prev-step-link wpstg-link-btn wpstg-blue-primary wpstg-button">
|
135 |
<?php _e( "Back", "wp-staging" ) ?>
|
136 |
</button>
|
137 |
|
140 |
$label = __( "Update Clone", "wp-staging" );
|
141 |
$action = 'wpstg_update';
|
142 |
|
143 |
+
echo '<button type="button" id="wpstg-start-updating" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="' . $action . '">' . $label . '</button>';
|
144 |
} else {
|
145 |
$label = __( "Start Cloning", "wp-staging" );
|
146 |
$action = 'wpstg_cloning';
|
147 |
|
148 |
+
echo '<button type="button" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="' . $action . '">' . $label . '</button>';
|
149 |
}
|
150 |
?>
|
|
|
|
apps/Backend/views/clone/ajax/single-overview.php
CHANGED
@@ -1,49 +1,54 @@
|
|
1 |
<div id="wpstg-step-1">
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
</button>
|
5 |
</div>
|
6 |
|
7 |
-
<?php if
|
8 |
<!-- Existing Clones -->
|
9 |
<div id="wpstg-existing-clones">
|
10 |
<h3>
|
11 |
-
<?php _e("Your Staging Sites:", "wp-staging")?>
|
12 |
</h3>
|
13 |
-
<?php //wp_die(var_dump($availableClones)); ?>
|
14 |
-
<?php foreach ($availableClones as $name => $data)
|
15 |
<div id="<?php echo $data["directoryName"]; ?>" class="wpstg-clone">
|
16 |
|
17 |
-
<?php $urlLogin = $data["url"]
|
18 |
|
19 |
-
<a href="<?php echo $urlLogin?>" class="wpstg-clone-title" target="_blank">
|
20 |
-
<?php //echo $name?>
|
21 |
<?php echo $data["directoryName"]; ?>
|
22 |
</a>
|
23 |
|
24 |
-
<?php echo apply_filters("wpstg_before_stage_buttons", $html
|
25 |
|
26 |
-
<a href="<?php echo $urlLogin?>" class="wpstg-open-clone wpstg-clone-action" target="_blank">
|
27 |
-
<?php _e("Open", "wp-staging"); ?>
|
28 |
</a>
|
29 |
|
30 |
-
<a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $name?>">
|
31 |
-
<?php _e("Update", "wp-staging"); ?>
|
32 |
</a>
|
33 |
|
34 |
-
<a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $name?>">
|
35 |
-
<?php _e("Delete", "wp-staging"); ?>
|
36 |
</a>
|
37 |
-
|
38 |
-
<?php echo apply_filters("wpstg_after_stage_buttons", $html = '', $name, $data)?>
|
39 |
<div class="wpstg-staging-info">
|
40 |
<?php
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
|
48 |
echo $dbname;
|
49 |
echo '</br>';
|
@@ -59,10 +64,10 @@
|
|
59 |
?>
|
60 |
</div>
|
61 |
</div>
|
62 |
-
<?php endforeach?>
|
63 |
</div>
|
64 |
<!-- /Existing Clones -->
|
65 |
-
<?php endif?>
|
66 |
|
67 |
<!-- Remove Clone -->
|
68 |
<div id="wpstg-removing-clone">
|
1 |
<div id="wpstg-step-1">
|
2 |
+
<!--
|
3 |
+
<?php //if ( wpstg_is_stagingsite()) {?>
|
4 |
+
<h3 style="color:red;"><?php //echo __("This is your staging website!", "wp-staging") ?></h3>
|
5 |
+
<p><?php //echo __("You can still clone it. <br>For example, to push another clone to this site for testing purposes.", "wp-staging"); ?> </p>
|
6 |
+
<p><?php //echo __("Go to the Production website > WP Staging > Sites if you want to push this site to live.","wp-staging"); ?></p>
|
7 |
+
<?php //} ?>
|
8 |
+
//-->
|
9 |
+
<button id="wpstg-new-clone" class="wpstg-next-step-link wpstg-link-btn wpstg-blue-primary wpstg-button" data-action="wpstg_scanning">
|
10 |
+
<?php echo __( "Create new staging site", "wp-staging" ) ?>
|
11 |
</button>
|
12 |
</div>
|
13 |
|
14 |
+
<?php if( isset( $availableClones ) && !empty( $availableClones ) ): ?>
|
15 |
<!-- Existing Clones -->
|
16 |
<div id="wpstg-existing-clones">
|
17 |
<h3>
|
18 |
+
<?php _e( "Your Staging Sites:", "wp-staging" ) ?>
|
19 |
</h3>
|
20 |
+
<?php //wp_die(var_dump($availableClones)); ?>
|
21 |
+
<?php foreach ( $availableClones as $name => $data ): ?>
|
22 |
<div id="<?php echo $data["directoryName"]; ?>" class="wpstg-clone">
|
23 |
|
24 |
+
<?php $urlLogin = $data["url"]; ?>
|
25 |
|
26 |
+
<a href="<?php echo $urlLogin ?>" class="wpstg-clone-title" target="_blank">
|
|
|
27 |
<?php echo $data["directoryName"]; ?>
|
28 |
</a>
|
29 |
|
30 |
+
<?php echo apply_filters( "wpstg_before_stage_buttons", $html = '', $name, $data ) ?>
|
31 |
|
32 |
+
<a href="<?php echo $urlLogin ?>" class="wpstg-open-clone wpstg-clone-action" target="_blank" title="<?php echo __( "Open the staging site in a new tab", "wp-staging" ) ?>">
|
33 |
+
<?php _e( "Open", "wp-staging" ); ?>
|
34 |
</a>
|
35 |
|
36 |
+
<a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __( "Update and overwrite the staging site. You can select certain folders and database tables in the next step.", "wp-staging" ) ?>">
|
37 |
+
<?php _e( "Update", "wp-staging" ); ?>
|
38 |
</a>
|
39 |
|
40 |
+
<a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $name ?>" title="<?php echo __( "Delete the staging site. You can select certain folders and database tables in the next step.", "wp-staging" ) ?>">
|
41 |
+
<?php _e( "Delete", "wp-staging" ); ?>
|
42 |
</a>
|
43 |
+
<?php echo apply_filters( "wpstg_after_stage_buttons", $html = '', $name, $data ) ?>
|
|
|
44 |
<div class="wpstg-staging-info">
|
45 |
<?php
|
46 |
+
$dbname = !empty( $data['databaseDatabase'] ) ? __( "Database: <span class='wpstg-bold'>" . $data['databaseDatabase'], "wp-staging" ) . '</span>' : 'Database: <span class="wpstg-bold">' . DB_NAME . '</span>';
|
47 |
+
$prefix = !empty( $data['prefix'] ) ? __( "Database Prefix: <span class='wpstg-bold'>" . $data['prefix'], "wp-staging" ) . '</span> ' : '';
|
48 |
+
$cloneDir = !empty( $data['path'] ) ? __( "Directory: <span class='wpstg-bold'>" . $data['path'], "wp-staging" ) . '</span> ' : 'Directory: ';
|
49 |
+
$url = !empty( $data['url'] ) ? __( "URL: <span class='wpstg-bold'>" . $data['url'], "wp-staging" ) . '</span> ' : 'URL: ';
|
50 |
+
$datetime = !empty( $data['datetime'] ) ? __( "Updated: <span>" . date( "D, d M Y H:i:s T", $data['datetime'] ), "wp-staging" ) . '</span> ' : ' ';
|
51 |
+
$status = !empty( $data['status'] ) && $data['status'] !== 'finished' ? "Status: <span class='wpstg-bold' style='color:#ffc2c2;'>" . $data['status'] . "</span>" : ' ';
|
52 |
|
53 |
echo $dbname;
|
54 |
echo '</br>';
|
64 |
?>
|
65 |
</div>
|
66 |
</div>
|
67 |
+
<?php endforeach ?>
|
68 |
</div>
|
69 |
<!-- /Existing Clones -->
|
70 |
+
<?php endif ?>
|
71 |
|
72 |
<!-- Remove Clone -->
|
73 |
<div id="wpstg-removing-clone">
|
apps/Backend/views/clone/ajax/start.php
CHANGED
@@ -13,7 +13,7 @@
|
|
13 |
<div style="clear: both;"></div>
|
14 |
</div>
|
15 |
|
16 |
-
<button type="button" id="wpstg-cancel-cloning" class="wpstg-link-btn
|
17 |
<?php echo __("Cancel", "wp-staging")?>
|
18 |
</button>
|
19 |
|
13 |
<div style="clear: both;"></div>
|
14 |
</div>
|
15 |
|
16 |
+
<button type="button" id="wpstg-cancel-cloning" class="wpstg-button wpstg-link-btn wpstg-blue-primary">
|
17 |
<?php echo __("Cancel", "wp-staging")?>
|
18 |
</button>
|
19 |
|
apps/Backend/views/clone/index.php
CHANGED
@@ -8,13 +8,15 @@
|
|
8 |
<?php
|
9 |
do_action( "wpstg_notifications" );
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
14 |
}
|
15 |
// Single site
|
16 |
else {
|
17 |
-
|
18 |
}
|
19 |
|
20 |
// Footer
|
8 |
<?php
|
9 |
do_action( "wpstg_notifications" );
|
10 |
|
11 |
+
if( wpstg_is_stagingsite() ) {
|
12 |
+
// Staging site
|
13 |
+
require_once($this->path . "views/clone/staging-site/index.php");
|
14 |
+
} elseif( is_multisite() ) {
|
15 |
+
require_once($this->path . "views/clone/multi-site/index.php");
|
16 |
}
|
17 |
// Single site
|
18 |
else {
|
19 |
+
require_once($this->path . "views/clone/single-site/index.php");
|
20 |
}
|
21 |
|
22 |
// Footer
|
apps/Backend/views/clone/staging-site/index.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<span class="wpstg-notice-alert" style="margin-top:20px;">
|
2 |
+
<?php echo sprintf(__("This is your staging site. Go to your live site to use that function.", "wp-staging"),
|
3 |
+
admin_url() . 'admin.php?page=wpstg_clone')?>
|
4 |
+
</span>
|
apps/Core/Iterators/RecursiveFilterExclude.php
CHANGED
@@ -15,16 +15,18 @@ class RecursiveFilterExclude extends \RecursiveFilterIterator {
|
|
15 |
|
16 |
public function accept() {
|
17 |
|
|
|
|
|
18 |
// Path contains new line character on linux
|
19 |
-
if(strpos( $
|
20 |
return false;
|
21 |
|
22 |
// Path contains new line character on Windows
|
23 |
-
if(strpos( $
|
24 |
return false;
|
25 |
-
|
26 |
// Part of the path is excluded
|
27 |
-
if (in_array( $
|
28 |
return false;
|
29 |
|
30 |
return true;
|
15 |
|
16 |
public function accept() {
|
17 |
|
18 |
+
$subPath = $this->getInnerIterator()->getSubPathname();
|
19 |
+
|
20 |
// Path contains new line character on linux
|
21 |
+
if(strpos( $subPath, "\n" ) !== false)
|
22 |
return false;
|
23 |
|
24 |
// Path contains new line character on Windows
|
25 |
+
if(strpos( $subPath, "\r" ) !== false)
|
26 |
return false;
|
27 |
+
|
28 |
// Part of the path is excluded
|
29 |
+
if (in_array( wpstg_replace_windows_directory_separator($subPath), $this->exclude ))
|
30 |
return false;
|
31 |
|
32 |
return true;
|
apps/Core/Utils/functions.php
CHANGED
@@ -6,11 +6,12 @@
|
|
6 |
* @return int
|
7 |
*/
|
8 |
function wpstg_get_permissions_for_directory() {
|
9 |
-
|
10 |
-
|
|
|
11 |
}
|
12 |
|
13 |
-
return
|
14 |
}
|
15 |
|
16 |
/**
|
@@ -137,7 +138,7 @@ function wpstg_is_valid_date( $date, $format = 'Y-m-d' ) {
|
|
137 |
/**
|
138 |
* Convert all values of a string or an array into url decoded values
|
139 |
* Main use for preventing Wordfence firewall rule 'local file inclusion'
|
140 |
-
* @param
|
141 |
* @return mixed string | array
|
142 |
*/
|
143 |
function wpstg_urldecode( $data ) {
|
@@ -169,9 +170,39 @@ function wpstg_is_stagingsite() {
|
|
169 |
return true;
|
170 |
}
|
171 |
|
172 |
-
if(
|
173 |
return true;
|
174 |
}
|
175 |
|
176 |
return false;
|
177 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
* @return int
|
7 |
*/
|
8 |
function wpstg_get_permissions_for_directory() {
|
9 |
+
$octal = 0755;
|
10 |
+
if (defined('FS_CHMOD_DIR')) {
|
11 |
+
$octal = FS_CHMOD_DIR;
|
12 |
}
|
13 |
|
14 |
+
return apply_filters('wpstg_folder_permission', $octal);
|
15 |
}
|
16 |
|
17 |
/**
|
138 |
/**
|
139 |
* Convert all values of a string or an array into url decoded values
|
140 |
* Main use for preventing Wordfence firewall rule 'local file inclusion'
|
141 |
+
* @param mixed string | array $data
|
142 |
* @return mixed string | array
|
143 |
*/
|
144 |
function wpstg_urldecode( $data ) {
|
170 |
return true;
|
171 |
}
|
172 |
|
173 |
+
if( file_exists( ABSPATH . '.wp-staging')){
|
174 |
return true;
|
175 |
}
|
176 |
|
177 |
return false;
|
178 |
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* @param string $memory
|
182 |
+
* @return int
|
183 |
+
*/
|
184 |
+
function wpstg_get_memory_in_bytes( $memory ) {
|
185 |
+
// Handle unlimited ones
|
186 |
+
if( 1 > ( int ) $memory ) {
|
187 |
+
//return (int) $memory;
|
188 |
+
// 128 MB default value
|
189 |
+
return ( int ) 134217728;
|
190 |
+
}
|
191 |
+
|
192 |
+
$bytes = ( int ) $memory; // grab only the number
|
193 |
+
$size = trim( str_replace( $bytes, null, strtolower( $memory ) ) ); // strip away number and lower-case it
|
194 |
+
// Actual calculation
|
195 |
+
switch ( $size ) {
|
196 |
+
case 'k':
|
197 |
+
$bytes *= 1024;
|
198 |
+
break;
|
199 |
+
case 'm':
|
200 |
+
$bytes *= (1024 * 1024);
|
201 |
+
break;
|
202 |
+
case 'g':
|
203 |
+
$bytes *= (1024 * 1024 * 1024);
|
204 |
+
break;
|
205 |
+
}
|
206 |
+
|
207 |
+
return $bytes;
|
208 |
+
}
|
apps/Core/WPStaging.php
CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
|
|
29 |
/**
|
30 |
* Plugin version
|
31 |
*/
|
32 |
-
const VERSION = "2.5.
|
33 |
|
34 |
/**
|
35 |
* Plugin name
|
@@ -44,9 +44,9 @@ final class WPStaging {
|
|
44 |
/**
|
45 |
* Compatible WP Version
|
46 |
*/
|
47 |
-
const WP_COMPATIBLE = "5.2.
|
48 |
|
49 |
-
public $wpPath;
|
50 |
|
51 |
/**
|
52 |
* Slug: Either wp-staging or wp-staging-pro
|
29 |
/**
|
30 |
* Plugin version
|
31 |
*/
|
32 |
+
const VERSION = "2.5.9";
|
33 |
|
34 |
/**
|
35 |
* Plugin name
|
44 |
/**
|
45 |
* Compatible WP Version
|
46 |
*/
|
47 |
+
const WP_COMPATIBLE = "5.2.2";
|
48 |
|
49 |
+
//public $wpPath;
|
50 |
|
51 |
/**
|
52 |
* Slug: Either wp-staging or wp-staging-pro
|
readme.txt
CHANGED
@@ -9,7 +9,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
|
9 |
Tags: staging, duplication, cloning, clone, migration, sandbox, test site, testing, backup, post, admin, administration, duplicate posts
|
10 |
Requires at least: 3.6+
|
11 |
Tested up to: 5.2
|
12 |
-
Stable tag: 2.
|
13 |
Requires PHP: 5.3
|
14 |
|
15 |
A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
|
@@ -151,6 +151,28 @@ https://wp-staging.com
|
|
151 |
|
152 |
== Changelog ==
|
153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
= 2.5.8 =
|
155 |
* Fix: Remove admin notice that wp staging is not compatible with latest wp version
|
156 |
|
@@ -222,13 +244,3 @@ https://wp-staging.com
|
|
222 |
|
223 |
|
224 |
Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
|
225 |
-
|
226 |
-
== Upgrade Notice ==
|
227 |
-
|
228 |
-
= 2.5.6 =
|
229 |
-
* New: compatible to WordPress 5.2
|
230 |
-
* New: Allow adding file .wp-staging to root of website to determine if it's a staging or production website
|
231 |
-
* Tweak: Move admin notices templates to views/notices
|
232 |
-
* Fix: Rating notice appears again immediately after using review later button
|
233 |
-
* Fix: Convert staging site table prefix to lowercase
|
234 |
-
* New: Show unfinished or interrupted clones and allow deletion of them
|
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.0
|
13 |
Requires PHP: 5.3
|
14 |
|
15 |
A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
|
151 |
|
152 |
== Changelog ==
|
153 |
|
154 |
+
= 2.6.0 =
|
155 |
+
* New: Compatible up to WordPress 5.2.2
|
156 |
+
* New: Performance improvement for directory iterator using less server ressources
|
157 |
+
* New: Add filter wpstg_folder_permission to set a custom folder permission like 0755, allows to overwrite FS_CHMOD_DIR if it has been defined.
|
158 |
+
* Fix: Error conditions in class Data does not compare type strict (== vs. ==) resulting in interruption of clone process
|
159 |
+
* Fix: Excluded folders under wp-content level are not take into account on microsoft IIS servers
|
160 |
+
|
161 |
+
|
162 |
+
|
163 |
+
= 2.5.9 =
|
164 |
+
* New: Update for WP 5.2.1
|
165 |
+
* New: Better corporate identity and more friendly UI colors for staging sites listings and button
|
166 |
+
* New: Better warning notices before updating process is executed
|
167 |
+
* New: Add tooltips for explaining navigation buttons
|
168 |
+
* New: Check if UPLOAD constant is defined and use this value for uploads folder destination
|
169 |
+
* New: Show notice if user tries to clone a staging website.
|
170 |
+
* Fix: Staging sites listing entries appeared on the cloned website.
|
171 |
+
* Fix: Do not search & replace through "__PHP_Incomplete_Class_Name" definitions
|
172 |
+
* Fix: Prevent wordfence firewall rule interrupting the clone deletion method
|
173 |
+
* Fix: Excluded wp staging directory from deleting process is ignored and will be deleted either way
|
174 |
+
* Fix: Strip whitespaces in cloning site internal names
|
175 |
+
|
176 |
= 2.5.8 =
|
177 |
* Fix: Remove admin notice that wp staging is not compatible with latest wp version
|
178 |
|
244 |
|
245 |
|
246 |
Complete changelog: [https://wp-staging.com/wp-staging-changelog](https://wp-staging.com/wp-staging-changelog)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
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.
|
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.0
|
11 |
* Text Domain: wp-staging
|
12 |
* Domain Path: /languages/
|
13 |
|
51 |
|
52 |
// Version
|
53 |
if( !defined( 'WPSTG_VERSION' ) ) {
|
54 |
+
define( 'WPSTG_VERSION', '2.6.0' );
|
55 |
}
|
56 |
|
57 |
// Must use version of the optimizer
|