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

Version Description

  • Fix: If WPML is used the live site is not reachable
  • Fix: Can not disable optimizer
  • Fix: Stop cloning if wp_usermeta or wp_options can not be adapted
  • Fix: All methods should be private in class SearchReplace
  • Fix: PHP 7.2 is not countable warning
  • Fix: PHP 7.2 can not replace data in objects when object is incomplete (_PHP_IncompleteClass_Name)
  • New: Use fully custom login form to prevent access denied issues on sites where access to wp-login.php is denied or redirection plugins are used
  • New: Link to support section
Download this release

Release Info

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

Code changes from version 2.2.5 to 2.2.6

apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -1,188 +1,189 @@
1
<?php
2
namespace WPStaging\Backend\Modules\Jobs;
3
4
// No Direct Access
5
- if (!defined("WPINC"))
6
- {
7
- die;
8
}
9
10
use WPStaging\Utils\Logger;
11
use WPStaging\WPStaging;
12
13
/**
14
* Class Data
15
* @package WPStaging\Backend\Modules\Jobs
16
*/
17
- class Data extends JobExecutable
18
- {
19
-
20
- /**
21
- * @var \wpdb
22
- */
23
- private $db;
24
-
25
- /**
26
- * @var string
27
- */
28
- private $prefix;
29
-
30
- /**
31
- * Initialize
32
- */
33
- public function initialize()
34
- {
35
- $this->db = WPStaging::getInstance()->get("wpdb");
36
-
37
- $this->prefix = $this->options->prefix;
38
-
39
- // Fix current step
40
- if (0 == $this->options->currentStep)
41
- {
42
- $this->options->currentStep = 1;
43
- }
44
- }
45
-
46
- /**
47
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
48
- * @return void
49
- */
50
- protected function calculateTotalSteps()
51
- {
52
- $this->options->totalSteps = 11;
53
- }
54
-
55
- /**
56
- * Start Module
57
- * @return object
58
- */
59
- public function start()
60
- {
61
- // Execute steps
62
- $this->run();
63
-
64
- // Save option, progress
65
- $this->saveOptions();
66
-
67
- return (object) $this->response;
68
- }
69
-
70
- /**
71
- * Execute the Current Step
72
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
73
- * @return bool
74
- */
75
- protected function execute()
76
- {
77
- // Fatal error. Let this happen never and break here immediately
78
- if ($this->isRoot()){
79
- return false;
80
- }
81
-
82
- // Over limits threshold
83
- if ($this->isOverThreshold())
84
- {
85
- // Prepare response and save current progress
86
- $this->prepareResponse(false, false);
87
- $this->saveOptions();
88
- return false;
89
- }
90
91
- // No more steps, finished
92
- if ($this->isFinished())
93
- {
94
- $this->prepareResponse(true, false);
95
- return false;
96
- }
97
98
- // Execute step
99
- $stepMethodName = "step" . $this->options->currentStep;
100
- if (!$this->{$stepMethodName}())
101
- {
102
- $this->prepareResponse(false, false);
103
- return false;
104
- }
105
-
106
- // Prepare Response
107
- $this->prepareResponse();
108
-
109
- // Not finished
110
- return true;
111
- }
112
-
113
- /**
114
- * Checks Whether There is Any Job to Execute or Not
115
- * @return bool
116
- */
117
- private function isFinished()
118
- {
119
- return (
120
- $this->options->currentStep > $this->options->totalSteps ||
121
- !method_exists($this, "step" . $this->options->currentStep)
122
- );
123
- }
124
-
125
- /**
126
- * Check if current operation is done on the root folder or on the live DB
127
- * @return boolean
128
- */
129
- private function isRoot(){
130
-
131
- // Prefix is the same as the one of live site
132
- $wpdb = WPStaging::getInstance()->get("wpdb");
133
- if ($wpdb->prefix === $this->prefix){
134
- return true;
135
- }
136
-
137
- // CloneName is empty
138
- $name = (array)$this->options->cloneDirectoryName;
139
- if (empty($name)){
140
- return true;
141
- }
142
-
143
- // Live Path === Staging path
144
- if (get_home_url() . $this->options->cloneDirectoryName === get_home_url()){
145
- return true;
146
- }
147
-
148
- return false;
149
- }
150
-
151
-
152
- /**
153
- * Check if table exists
154
- * @param string $table
155
- * @return boolean
156
- */
157
- protected function isTable($table){
158
- if($this->db->get_var("SHOW TABLES LIKE '{$table}'") != $table ){
159
$this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
160
return false;
161
}
162
return true;
163
- }
164
-
165
- /**
166
- * Get the install sub directory if WP is installed in sub directory
167
- * @return string
168
- */
169
- protected function getSubDir(){
170
- $home = get_option('home');
171
- $siteurl = get_option('siteurl');
172
-
173
- if (empty($home) || empty($siteurl)){
174
- return '/';
175
- }
176
-
177
- $dir = str_replace($home, '', $siteurl);
178
- return '/' . str_replace('/', '', $dir) . '/';
179
- }
180
-
181
- /**
182
- * Replace "siteurl" and "home"
183
- * @return bool
184
- */
185
- protected function step1() {
186
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
187
188
if( false === $this->isTable( $this->prefix . 'options' ) ) {
@@ -191,19 +192,19 @@ class Data extends JobExecutable
191
192
// Installed in sub-directory
193
if( $this->isSubDir() ) {
194
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim(get_home_url(), "/") . $this->getSubDir() . $this->options->cloneDirectoryName );
195
// Replace URLs
196
$result = $this->db->query(
197
$this->db->prepare(
198
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim(get_home_url(), "/") . $this->getSubDir() . $this->options->cloneDirectoryName
199
)
200
);
201
} else {
202
- $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim(get_home_url(), "/") . '/' . $this->options->cloneDirectoryName );
203
// Replace URLs
204
$result = $this->db->query(
205
$this->db->prepare(
206
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $this->options->cloneDirectoryName
207
)
208
);
209
}
@@ -219,298 +220,274 @@ class Data extends JobExecutable
219
}
220
221
/**
222
- * Update "wpstg_is_staging_site"
223
- * @return bool
224
- */
225
- protected function step2()
226
- {
227
-
228
- $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
229
230
if( false === $this->isTable( $this->prefix . 'options' ) ) {
231
return true;
232
}
233
234
- $result = $this->db->query(
235
- $this->db->prepare(
236
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'",
237
- "true"
238
- )
239
- );
240
-
241
- // No errors but no option name such as wpstg_is_staging_site
242
- if ('' === $this->db->last_error && 0 == $result)
243
- {
244
- $result = $this->db->query(
245
- $this->db->prepare(
246
- "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)",
247
- "true"
248
- )
249
- );
250
- }
251
-
252
- // All good
253
- if ($result)
254
- {
255
- return true;
256
- }
257
-
258
- $this->log("Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
259
- return false;
260
- }
261
-
262
- /**
263
- * Update rewrite_rules
264
- * @return bool
265
- */
266
- protected function step3()
267
- {
268
-
269
- $this->log("Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}");
270
-
271
if( false === $this->isTable( $this->prefix . 'options' ) ) {
272
return true;
273
}
274
-
275
- $result = $this->db->query(
276
- $this->db->prepare(
277
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'",
278
- ' '
279
- )
280
- );
281
-
282
- // All good
283
- if ($result)
284
- {
285
- return true;
286
- }
287
-
288
- $this->log("Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
289
- return true;
290
- }
291
-
292
- /**
293
- * Update Table Prefix in meta_keys
294
- * @return bool
295
- */
296
- protected function step4() {
297
- $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. Error: {$this->db->last_error}" );
298
299
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
300
return true;
301
}
302
303
- $resultOptions = $this->db->query(
304
- $this->db->prepare(
305
- "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
306
- )
307
- );
308
-
309
- if( !$resultOptions ) {
310
- $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
311
- //return false;
312
- }
313
-
314
- $this->log( "Updating db prefixes in {$this->prefix}options. Error: {$this->db->last_error}" );
315
-
316
- $resultUserMeta = $this->db->query(
317
- $this->db->prepare(
318
- "UPDATE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
319
- )
320
- );
321
-
322
- if( !$resultUserMeta ) {
323
- $this->log( "Preparing Data Step4: Failed to update db prefixes in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
324
- //return false;
325
- }
326
-
327
- return true;
328
- }
329
-
330
- /**
331
- * Update $table_prefix in wp-config.php
332
- * @return bool
333
- */
334
- protected function step5()
335
- {
336
- $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
337
-
338
- $this->log("Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix);
339
- if (false === ($content = file_get_contents($path)))
340
- {
341
- $this->log("Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR);
342
- return false;
343
- }
344
-
345
- // Replace table prefix
346
- $content = str_replace('$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content);
347
-
348
- // Replace URLs
349
- $content = str_replace(get_home_url(), get_home_url() . '/' . $this->options->cloneDirectoryName, $content);
350
-
351
- if (false === @file_put_contents($path, $content))
352
- {
353
- $this->log("Preparing Data Step5: Failed to update $table_prefix in {$path} to " .$this->prefix . ". Can't save contents", Logger::TYPE_ERROR);
354
- return false;
355
- }
356
-
357
- return true;
358
- }
359
-
360
- /**
361
- * Reset index.php to original file
362
- * This is needed if live site is located in subfolder
363
- * Check first if main wordpress is used in subfolder and index.php in parent directory
364
- * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
365
- * @return bool
366
- */
367
- protected function step6()
368
- {
369
-
370
- if (!$this->isSubDir())
371
- {
372
- $this->debugLog("Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step");
373
- return true;
374
- }
375
-
376
- $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
377
-
378
- if (false === ($content = file_get_contents($path)))
379
- {
380
- $this->log("Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR);
381
- return false;
382
- }
383
384
385
- if (!preg_match("/(require(.*)wp-blog-header.php' \);)/", $content, $matches))
386
- {
387
- $this->log(
388
- "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing",
389
- Logger::TYPE_ERROR
390
- );
391
- return false;
392
- }
393
- $this->log("Preparing Data: WP installation is in a subdirectory. Progressing...");
394
395
- $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
396
397
- $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
398
- $replace.= " // Changed by WP-Staging";
399
400
-
401
-
402
- if (null === ($content = preg_replace(array($pattern), $replace, $content)))
403
- {
404
- $this->log("Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR);
405
- return false;
406
- }
407
408
- if (false === @file_put_contents($path, $content))
409
- {
410
- $this->log("Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR);
411
- return false;
412
- }
413
- $this->Log("Preparing Data: Finished Step 6 successfully");
414
- return true;
415
- }
416
-
417
- /**
418
- * Update wpstg_rmpermalinks_executed
419
- * @return bool
420
- */
421
- protected function step7()
422
- {
423
-
424
- $this->log("Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}");
425
-
426
if( false === $this->isTable( $this->prefix . 'options' ) ) {
427
return true;
428
- }
429
-
430
- $result = $this->db->query(
431
- $this->db->prepare(
432
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'",
433
- ' '
434
- )
435
- );
436
-
437
- // All good
438
- if ($result)
439
- {
440
- $this->Log("Preparing Data Step7: Finished Step 7 successfully");
441
- return true;
442
- }
443
-
444
- $this->log("Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING);
445
- return true;
446
- }
447
-
448
- /**
449
- * Update permalink_structure
450
- * @return bool
451
- */
452
- protected function step8()
453
- {
454
-
455
- $this->log("Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}");
456
-
457
if( false === $this->isTable( $this->prefix . 'options' ) ) {
458
return true;
459
}
460
-
461
- $result = $this->db->query(
462
- $this->db->prepare(
463
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'",
464
- ' '
465
- )
466
- );
467
-
468
- // All good
469
- if ($result)
470
- {
471
- $this->Log("Preparing Data Step8: Finished Step 8 successfully");
472
- return true;
473
- }
474
-
475
- $this->log("Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
476
- return true;
477
- }
478
- /**
479
- * Update blog_public option to not allow staging site to be indexed by search engines
480
- * @return bool
481
- */
482
- protected function step9()
483
- {
484
-
485
- $this->log("Preparing Data Step9: Set staging site to noindex");
486
-
487
if( false === $this->isTable( $this->prefix . 'options' ) ) {
488
return true;
489
}
490
-
491
- $result = $this->db->query(
492
- $this->db->prepare(
493
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'",
494
- '0'
495
- )
496
- );
497
-
498
- // All good
499
- if ($result)
500
- {
501
- $this->Log("Preparing Data Step9: Finished Step 9 successfully");
502
- return true;
503
- }
504
-
505
- $this->log("Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING);
506
- return true;
507
- }
508
-
509
- /**
510
- * Update WP_HOME in wp-config.php
511
- * @return bool
512
- */
513
- protected function step10() {
514
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
515
516
$this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
@@ -549,10 +526,10 @@ class Data extends JobExecutable
549
}
550
551
/**
552
- * Update WP_SITEURL in wp-config.php
553
- * @return bool
554
- */
555
- protected function step11() {
556
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
557
558
$this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
@@ -597,20 +574,21 @@ class Data extends JobExecutable
597
*/
598
protected function getStagingSiteUrl() {
599
if( $this->isSubDir() ) {
600
- return rtrim( get_home_url(), "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
601
}
602
603
- return rtrim( get_home_url(), "/" ) . '/' . $this->options->cloneDirectoryName;
604
}
605
606
/**
607
- * Check if WP is installed in subdir
608
- * @return boolean
609
- */
610
- protected function isSubDir(){
611
- if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
612
- return true;
613
- }
614
- return false;
615
- }
616
- }
1
<?php
2
+
3
namespace WPStaging\Backend\Modules\Jobs;
4
5
// No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
}
9
10
use WPStaging\Utils\Logger;
11
use WPStaging\WPStaging;
12
+ use WPStaging\Utils\Helper;
13
14
/**
15
* Class Data
16
* @package WPStaging\Backend\Modules\Jobs
17
*/
18
+ class Data extends JobExecutable {
19
20
+ /**
21
+ * @var \wpdb
22
+ */
23
+ private $db;
24
25
+ /**
26
+ * @var string
27
+ */
28
+ private $prefix;
29
+
30
+ /**
31
+ *
32
+ * @var string
33
+ */
34
+ private $homeUrl;
35
+
36
+ /**
37
+ * Initialize
38
+ */
39
+ public function initialize() {
40
+ $this->db = WPStaging::getInstance()->get( "wpdb" );
41
+
42
+ $this->prefix = $this->options->prefix;
43
+
44
+ $helper = new Helper();
45
+
46
+ $this->homeUrl = $helper->get_home_url();
47
+
48
+
49
+ // Fix current step
50
+ if( 0 == $this->options->currentStep ) {
51
+ $this->options->currentStep = 1;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
57
+ * @return void
58
+ */
59
+ protected function calculateTotalSteps() {
60
+ $this->options->totalSteps = 11;
61
+ }
62
+
63
+ /**
64
+ * Start Module
65
+ * @return object
66
+ */
67
+ public function start() {
68
+ // Execute steps
69
+ $this->run();
70
+
71
+ // Save option, progress
72
+ $this->saveOptions();
73
+
74
+ return ( object ) $this->response;
75
+ }
76
+
77
+ /**
78
+ * Execute the Current Step
79
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
80
+ * @return bool
81
+ */
82
+ protected function execute() {
83
+ // Fatal error. Let this happen never and break here immediately
84
+ if( $this->isRoot() ) {
85
+ return false;
86
+ }
87
+
88
+ // Over limits threshold
89
+ if( $this->isOverThreshold() ) {
90
+ // Prepare response and save current progress
91
+ $this->prepareResponse( false, false );
92
+ $this->saveOptions();
93
+ return false;
94
+ }
95
+
96
+ // No more steps, finished
97
+ if( $this->isFinished() ) {
98
+ $this->prepareResponse( true, false );
99
+ return false;
100
+ }
101
+
102
+ // Execute step
103
+ $stepMethodName = "step" . $this->options->currentStep;
104
+ if( !$this->{$stepMethodName}() ) {
105
+ $this->prepareResponse( false, false );
106
+ return false;
107
+ }
108
+
109
+ // Prepare Response
110
+ $this->prepareResponse();
111
+
112
+ // Not finished
113
+ return true;
114
+ }
115
+
116
+ /**
117
+ * Checks Whether There is Any Job to Execute or Not
118
+ * @return bool
119
+ */
120
+ protected function isFinished() {
121
+ return (
122
+ $this->options->currentStep > $this->options->totalSteps ||
123
+ !method_exists( $this, "step" . $this->options->currentStep )
124
+ );
125
+ }
126
+
127
+ /**
128
+ * Check if current operation is done on the root folder or on the live DB
129
+ * @return boolean
130
+ */
131
+ protected function isRoot() {
132
+
133
+ // Prefix is the same as the one of live site
134
+ $wpdb = WPStaging::getInstance()->get( "wpdb" );
135
+ if( $wpdb->prefix === $this->prefix ) {
136
+ return true;
137
+ }
138
+
139
+ // CloneName is empty
140
+ $name = ( array ) $this->options->cloneDirectoryName;
141
+ if( empty( $name ) ) {
142
+ return true;
143
+ }
144
+
145
+ // Live Path === Staging path
146
+ if( $this->homeUrl . $this->options->cloneDirectoryName === $this->homeUrl ) {
147
+ return true;
148
+ }
149
+
150
+ return false;
151
+ }
152
+
153
+ /**
154
+ * Check if table exists
155
+ * @param string $table
156
+ * @return boolean
157
+ */
158
+ protected function isTable( $table ) {
159
+ if( $this->db->get_var( "SHOW TABLES LIKE '{$table}'" ) != $table ) {
160
$this->log( "Table {$table} does not exists", Logger::TYPE_ERROR );
161
return false;
162
}
163
return true;
164
+ }
165
+
166
+ /**
167
+ * Get the install sub directory if WP is installed in sub directory
168
+ * @return string
169
+ */
170
+ protected function getSubDir() {
171
+ $home = get_option( 'home' );
172
+ $siteurl = get_option( 'siteurl' );
173
+
174
+ if( empty( $home ) || empty( $siteurl ) ) {
175
+ return '/';
176
+ }
177
+
178
+ $dir = str_replace( $home, '', $siteurl );
179
+ return '/' . str_replace( '/', '', $dir ) . '/';
180
+ }
181
+
182
+ /**
183
+ * Replace "siteurl" and "home"
184
+ * @return bool
185
+ */
186
+ protected function step1() {
187
$this->log( "Preparing Data Step1: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
188
189
if( false === $this->isTable( $this->prefix . 'options' ) ) {
192
193
// Installed in sub-directory
194
if( $this->isSubDir() ) {
195
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName );
196
// Replace URLs
197
$result = $this->db->query(
198
$this->db->prepare(
199
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName
200
)
201
);
202
} else {
203
+ $this->log( "Preparing Data Step1: Updating siteurl and homeurl to " . rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName );
204
// Replace URLs
205
$result = $this->db->query(
206
$this->db->prepare(
207
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", $this->homeUrl . '/' . $this->options->cloneDirectoryName
208
)
209
);
210
}
220
}
221
222
/**
223
+ * Update "wpstg_is_staging_site"
224
+ * @return bool
225
+ */
226
+ protected function step2() {
227
+
228
+ $this->log( "Preparing Data Step2: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
229
230
if( false === $this->isTable( $this->prefix . 'options' ) ) {
231
return true;
232
}
233
234
+ $result = $this->db->query(
235
+ $this->db->prepare(
236
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_is_staging_site'", "true"
237
+ )
238
+ );
239
+
240
+ // No errors but no option name such as wpstg_is_staging_site
241
+ if( '' === $this->db->last_error && 0 == $result ) {
242
+ $result = $this->db->query(
243
+ $this->db->prepare(
244
+ "INSERT INTO {$this->prefix}options (option_name,option_value) VALUES ('wpstg_is_staging_site',%s)", "true"
245
+ )
246
+ );
247
+ }
248
+
249
+ // All good
250
+ if( $result ) {
251
+ return true;
252
+ }
253
+
254
+ $this->log( "Preparing Data Step2: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
255
+ return false;
256
+ }
257
+
258
+ /**
259
+ * Update rewrite_rules
260
+ * @return bool
261
+ */
262
+ protected function step3() {
263
+
264
+ $this->log( "Preparing Data Step3: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}" );
265
+
266
if( false === $this->isTable( $this->prefix . 'options' ) ) {
267
return true;
268
}
269
+
270
+ $result = $this->db->query(
271
+ $this->db->prepare(
272
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", ' '
273
+ )
274
+ );
275
+
276
+ // All good
277
+ if( $result ) {
278
+ return true;
279
+ }
280
+
281
+ $this->log( "Preparing Data Step3: Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
282
+ return true;
283
+ }
284
+
285
+ /**
286
+ * Update Table Prefix in meta_keys
287
+ * @return bool
288
+ */
289
+ protected function step4() {
290
+ $this->log( "Preparing Data Step4: Updating db prefix in {$this->prefix}usermeta. Error: {$this->db->last_error}" );
291
292
if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
293
return true;
294
}
295
296
+ $update = $this->db->query(
297
+ $this->db->prepare(
298
+ "UPDATE {$this->prefix}usermeta SET meta_key = replace(meta_key, %s, %s) WHERE meta_key LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
299
+ )
300
+ );
301
302
+ if( !$update ) {
303
+ $this->log( "Preparing Data Step4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
304
+ $this->returnException( "Data Crunching Step 4: Failed to update {$this->prefix}usermeta meta_key database table prefixes; {$this->db->last_error}" );
305
+ return false;
306
+ }
307
308
+ $this->log( "Updating db prefixes in {$this->prefix}options. Error: {$this->db->last_error}" );
309
310
+ $resultUserMeta = $this->db->query(
311
+ $this->db->prepare(
312
+ "UPDATE {$this->prefix}options SET option_name= replace(option_name, %s, %s) WHERE option_name LIKE %s", $this->db->prefix, $this->prefix, $this->db->prefix . "_%"
313
+ )
314
+ );
315
316
+ if( !$resultUserMeta ) {
317
+ $this->log( "Preparing Data Step4: Failed to update db prefixes in {$this->prefix}options. Error: {$this->db->last_error}", Logger::TYPE_ERROR );
318
+ $this->returnException( "Data Crunching Step 4: Failed to update db prefixes in {$this->prefix}options. Error: {$this->db->last_error}" );
319
+ return false;
320
+ }
321
322
+ return true;
323
+ }
324
+
325
+ /**
326
+ * Update $table_prefix in wp-config.php
327
+ * @return bool
328
+ */
329
+ protected function step5() {
330
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
331
+
332
+ $this->log( "Preparing Data Step5: Updating table_prefix in {$path} to " . $this->prefix );
333
+ if( false === ($content = file_get_contents( $path )) ) {
334
+ $this->log( "Preparing Data Step5: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR );
335
+ return false;
336
+ }
337
+
338
+ // Replace table prefix
339
+ $content = str_replace( '$table_prefix', '$table_prefix = \'' . $this->prefix . '\';//', $content );
340
+
341
+ // Replace URLs
342
+ $content = str_replace( $this->homeUrl, $this->homeUrl . '/' . $this->options->cloneDirectoryName, $content );
343
+
344
+ if( false === @file_put_contents( $path, $content ) ) {
345
+ $this->log( "Preparing Data Step5: Failed to update $table_prefix in {$path} to " . $this->prefix . ". Can't save contents", Logger::TYPE_ERROR );
346
+ return false;
347
+ }
348
+
349
+ return true;
350
+ }
351
+
352
+ /**
353
+ * Reset index.php to original file
354
+ * This is needed if live site is located in subfolder
355
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
356
+ * @see: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
357
+ * @return bool
358
+ */
359
+ protected function step6() {
360
+
361
+ if( !$this->isSubDir() ) {
362
+ $this->debugLog( "Preparing Data Step6: WP installation is not in a subdirectory! All good, skipping this step" );
363
+ return true;
364
+ }
365
+
366
+ $path = ABSPATH . $this->options->cloneDirectoryName . "/index.php";
367
+
368
+ if( false === ($content = file_get_contents( $path )) ) {
369
+ $this->log( "Preparing Data Step6: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR );
370
+ return false;
371
+ }
372
+
373
+
374
+ if( !preg_match( "/(require(.*)wp-blog-header.php' \);)/", $content, $matches ) ) {
375
+ $this->log(
376
+ "Preparing Data Step6: Failed to reset index.php for sub directory; wp-blog-header.php is missing", Logger::TYPE_ERROR
377
+ );
378
+ return false;
379
+ }
380
+ $this->log( "Preparing Data: WP installation is in a subdirectory. Progressing..." );
381
+
382
+ $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
383
+
384
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0];
385
+ $replace.= " // Changed by WP-Staging";
386
+
387
+
388
+
389
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
390
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
391
+ return false;
392
+ }
393
+
394
+ if( false === @file_put_contents( $path, $content ) ) {
395
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR );
396
+ return false;
397
+ }
398
+ $this->Log( "Preparing Data: Finished Step 6 successfully" );
399
+ return true;
400
+ }
401
+
402
+ /**
403
+ * Update wpstg_rmpermalinks_executed
404
+ * @return bool
405
+ */
406
+ protected function step7() {
407
+
408
+ $this->log( "Preparing Data Step7: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}" );
409
410
if( false === $this->isTable( $this->prefix . 'options' ) ) {
411
return true;
412
+ }
413
+
414
+ $result = $this->db->query(
415
+ $this->db->prepare(
416
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'wpstg_rmpermalinks_executed'", ' '
417
+ )
418
+ );
419
+
420
+ // All good
421
+ if( $result ) {
422
+ $this->Log( "Preparing Data Step7: Finished Step 7 successfully" );
423
+ return true;
424
+ }
425
+
426
+ $this->log( "Failed to update wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_WARNING );
427
+ return true;
428
+ }
429
+
430
+ /**
431
+ * Update permalink_structure
432
+ * @return bool
433
+ */
434
+ protected function step8() {
435
+
436
+ $this->log( "Preparing Data Step8: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}" );
437
+
438
if( false === $this->isTable( $this->prefix . 'options' ) ) {
439
return true;
440
}
441
+
442
+ $result = $this->db->query(
443
+ $this->db->prepare(
444
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", ' '
445
+ )
446
+ );
447
+
448
+ // All good
449
+ if( $result ) {
450
+ $this->Log( "Preparing Data Step8: Finished Step 8 successfully" );
451
+ return true;
452
+ }
453
+
454
+ $this->log( "Failed to update permalink_structure in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
455
+ return true;
456
+ }
457
+
458
+ /**
459
+ * Update blog_public option to not allow staging site to be indexed by search engines
460
+ * @return bool
461
+ */
462
+ protected function step9() {
463
+
464
+ $this->log( "Preparing Data Step9: Set staging site to noindex" );
465
+
466
if( false === $this->isTable( $this->prefix . 'options' ) ) {
467
return true;
468
}
469
+
470
+ $result = $this->db->query(
471
+ $this->db->prepare(
472
+ "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0'
473
+ )
474
+ );
475
+
476
+ // All good
477
+ if( $result ) {
478
+ $this->Log( "Preparing Data Step9: Finished Step 9 successfully" );
479
+ return true;
480
+ }
481
+
482
+ $this->log( "Can not update staging site to noindex. Possible already done!", Logger::TYPE_WARNING );
483
+ return true;
484
+ }
485
+
486
+ /**
487
+ * Update WP_HOME in wp-config.php
488
+ * @return bool
489
+ */
490
+ protected function step10() {
491
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
492
493
$this->log( "Preparing Data Step10: Updating WP_HOME in wp-config.php to " . $this->getStagingSiteUrl() );
526
}
527
528
/**
529
+ * Update WP_SITEURL in wp-config.php
530
+ * @return bool
531
+ */
532
+ protected function step11() {
533
$path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
534
535
$this->log( "Preparing Data Step11: Updating WP_SITEURL in wp-config.php to " . $this->getStagingSiteUrl() );
574
*/
575
protected function getStagingSiteUrl() {
576
if( $this->isSubDir() ) {
577
+ return rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . $this->options->cloneDirectoryName;
578
}
579
580
+ return rtrim( $this->homeUrl, "/" ) . '/' . $this->options->cloneDirectoryName;
581
}
582
583
/**
584
+ * Check if WP is installed in subdir
585
+ * @return boolean
586
+ */
587
+ protected function isSubDir() {
588
+ if( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
589
+ return true;
590
+ }
591
+ return false;
592
+ }
593
+
594
+ }
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -25,11 +25,6 @@ class Files extends JobExecutable {
25
*/
26
private $maxFilesPerRun;
27
28
- /**
29
- * File Object
30
- */
31
- //private $verifyFiles = array();
32
-
33
/**
34
* @var string
35
*/
25
*/
26
private $maxFilesPerRun;
27
28
/**
29
* @var string
30
*/
apps/Backend/Modules/Jobs/Scan.php CHANGED
@@ -358,9 +358,10 @@ class Scan extends Job
358
protected function handleDirectory($path)
359
{
360
$directoryArray = explode(DIRECTORY_SEPARATOR, $path);
361
- $total = count($directoryArray);
362
363
- if (count($total) < 1)
364
{
365
return;
366
}
358
protected function handleDirectory($path)
359
{
360
$directoryArray = explode(DIRECTORY_SEPARATOR, $path);
361
+ $total = is_array($directoryArray) || $directoryArray instanceof Countable ? count($directoryArray) : 0;
362
363
+
364
+ if ($total < 1)
365
{
366
return;
367
}
apps/Backend/Modules/Jobs/SearchReplace.php CHANGED
@@ -9,6 +9,7 @@ if( !defined( "WPINC" ) ) {
9
10
use WPStaging\WPStaging;
11
use WPStaging\Utils\Strings;
12
13
/**
14
* Class Database
@@ -26,6 +27,14 @@ class SearchReplace extends JobExecutable {
26
*/
27
public $db;
28
29
/**
30
* The prefix of the new database tables which are used for the live site after updating tables
31
* @var string
@@ -40,7 +49,8 @@ class SearchReplace extends JobExecutable {
40
$this->db = WPStaging::getInstance()->get( "wpdb" );
41
//$this->tmpPrefix = 'wpstgtmp_';
42
$this->tmpPrefix = $this->options->prefix;
43
- //$this->settings->queryLimit = 2;
44
}
45
46
public function start() {
@@ -159,7 +169,7 @@ class SearchReplace extends JobExecutable {
159
private function startReplace( $new ) {
160
$rows = $this->options->job->start + $this->settings->queryLimit;
161
$this->log(
162
- "DB Processing: {$this->options->job->start} to {$rows} records"
163
);
164
165
// Search & Replace
@@ -174,7 +184,7 @@ class SearchReplace extends JobExecutable {
174
* @access public
175
* @return int
176
*/
177
- public function get_pages_in_table( $table ) {
178
$table = esc_sql( $table );
179
$rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
180
$pages = ceil( $rows / $this->settings->queryLimit );
@@ -187,7 +197,7 @@ class SearchReplace extends JobExecutable {
187
* @param string $table The table to check.
188
* @return array
189
*/
190
- public function get_columns( $table ) {
191
$primary_key = null;
192
$columns = array();
193
$fields = $this->db->get_results( 'DESCRIBE ' . $table );
@@ -218,45 +228,46 @@ class SearchReplace extends JobExecutable {
218
*/
219
private function searchReplace( $table, $page, $args ) {
220
221
// Load up the default settings for this chunk.
222
$table = esc_sql( $table );
223
$current_page = $this->options->job->start + $this->settings->queryLimit;
224
$pages = $this->get_pages_in_table( $table );
225
- $done = false;
226
227
228
if( $this->isSubDir() ) {
229
- //$homeUrl = rtrim(get_home_url(), "/") . $this->getSubDir() . $this->options->cloneDirectoryName;
230
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
231
$args['search_for'] = array(
232
- rtrim(get_home_url(), "/") . $this->getSubDir(),
233
ABSPATH
234
);
235
236
$args['replace_with'] = array(
237
- rtrim(get_home_url(), "/") . $this->getSubDir() . '/'. $this->options->cloneDirectoryName,
238
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
239
);
240
} else {
241
$args['search_for'] = array(
242
- rtrim(get_home_url(), '/'),
243
ABSPATH
244
);
245
$args['replace_with'] = array(
246
- rtrim( get_home_url(), '/' ) . '/' . $this->options->cloneDirectoryName,
247
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
248
);
249
}
250
251
// // Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
252
// $args['search_for'] = array(
253
- // get_home_url(),
254
// ABSPATH
255
// );
256
//
257
//
258
// $args['replace_with'] = array(
259
- // rtrim( get_home_url(), '/' ) . '/' . $this->options->cloneDirectoryName,
260
// rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
261
// );
262
$args['replace_guids'] = 'off';
@@ -267,9 +278,6 @@ class SearchReplace extends JobExecutable {
267
// Get a list of columns in this table.
268
list( $primary_key, $columns ) = $this->get_columns( $table );
269
270
- $this->log( "DB Processing: Table {$table}" );
271
-
272
-
273
// Bail out early if there isn't a primary key.
274
if( null === $primary_key ) {
275
return false;
@@ -287,7 +295,7 @@ class SearchReplace extends JobExecutable {
287
$current_row++;
288
$update_sql = array();
289
$where_sql = array();
290
- $isUpdate = false;
291
292
foreach ( $columns as $column ) {
293
@@ -329,39 +337,36 @@ class SearchReplace extends JobExecutable {
329
$i = 0;
330
foreach ( $args['search_for'] as $replace ) {
331
$dataRow = $this->recursive_unserialize_replace( $args['search_for'][$i], $args['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
332
- // Do not uncomment line below! Will lead to memory issues and timeouts
333
- //$this->debugLog('DB Processing: '.$table.' - Replace ' . $args['search_for'][$i] . ' with ' . $args['replace_with'][$i]);
334
$i++;
335
}
336
- unset ($replace);
337
- unset ($i);
338
339
// Something was changed
340
if( $row[$column] != $dataRow ) {
341
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
342
- $isUpdate = true;
343
- //$this->log("Changed {$update_sql[]} ", \WPStaging\Utils\Logger::TYPE_INFO);
344
}
345
}
346
347
// Determine what to do with updates.
348
if( $args['dry_run'] === 'on' ) {
349
// Don't do anything if a dry run
350
- } elseif( $isUpdate && !empty( $where_sql ) ) {
351
// If there are changes to make, run the query.
352
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
353
$result = $this->db->query( $sql );
354
355
if( !$result ) {
356
- $this->log("Error updating row {$current_row}", \WPStaging\Utils\Logger::TYPE_ERROR);
357
}
358
}
359
} // end row loop
360
- unset ($row);
361
362
- if( $current_page >= $pages - 1 ) {
363
- $done = true;
364
- }
365
366
// DB Flush
367
$this->db->flush();
@@ -401,8 +406,7 @@ class SearchReplace extends JobExecutable {
401
}
402
403
// Submitted by Tina Matter
404
- elseif( is_object( $data ) ) {
405
- // $data_class = get_class( $data );
406
$_tmp = $data; // new $data_class( );
407
$props = get_object_vars( $data );
408
foreach ( $props as $key => $value ) {
@@ -432,6 +436,33 @@ class SearchReplace extends JobExecutable {
432
return $data;
433
}
434
435
/**
436
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
437
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
@@ -439,7 +470,7 @@ class SearchReplace extends JobExecutable {
439
* @param string $input The string to escape.
440
* @return string
441
*/
442
- public function mysql_escape_mimic( $input ) {
443
if( is_array( $input ) ) {
444
return array_map( __METHOD__, $input );
445
}
@@ -458,7 +489,7 @@ class SearchReplace extends JobExecutable {
458
*
459
* @return mixed, false on failure
460
*/
461
- public static function unserialize( $serialized_string ) {
462
if( !is_serialized( $serialized_string ) ) {
463
return false;
464
}
@@ -479,7 +510,7 @@ class SearchReplace extends JobExecutable {
479
*
480
* @return string
481
*/
482
- public function str_replace( $from, $to, $data, $case_insensitive = false ) {
483
if( 'on' === $case_insensitive ) {
484
$data = str_ireplace( $from, $to, $data );
485
} else {
@@ -588,16 +619,16 @@ class SearchReplace extends JobExecutable {
588
* Get the install sub directory if WP is installed in sub directory
589
* @return string
590
*/
591
- private function getSubDir(){
592
- $home = get_option('home');
593
- $siteurl = get_option('siteurl');
594
595
- if (empty($home) || empty($siteurl)){
596
return '/';
597
}
598
599
- $dir = str_replace($home, '', $siteurl);
600
- return '/' . str_replace('/', '', $dir);
601
}
602
603
}
9
10
use WPStaging\WPStaging;
11
use WPStaging\Utils\Strings;
12
+ use WPStaging\Utils\Helper;
13
14
/**
15
* Class Database
27
*/
28
public $db;
29
30
+
31
+ /**
32
+ *
33
+ * @var string
34
+ */
35
+ private $homeUrl;
36
+
37
+
38
/**
39
* The prefix of the new database tables which are used for the live site after updating tables
40
* @var string
49
$this->db = WPStaging::getInstance()->get( "wpdb" );
50
//$this->tmpPrefix = 'wpstgtmp_';
51
$this->tmpPrefix = $this->options->prefix;
52
+ $helper = new Helper();
53
+ $this->homeUrl = $helper->get_home_url();
54
}
55
56
public function start() {
169
private function startReplace( $new ) {
170
$rows = $this->options->job->start + $this->settings->queryLimit;
171
$this->log(
172
+ "DB Processing: Table {$new} {$this->options->job->start} to {$rows} records"
173
);
174
175
// Search & Replace
184
* @access public
185
* @return int
186
*/
187
+ private function get_pages_in_table( $table ) {
188
$table = esc_sql( $table );
189
$rows = $this->db->get_var( "SELECT COUNT(*) FROM $table" );
190
$pages = ceil( $rows / $this->settings->queryLimit );
197
* @param string $table The table to check.
198
* @return array
199
*/
200
+ private function get_columns( $table ) {
201
$primary_key = null;
202
$columns = array();
203
$fields = $this->db->get_results( 'DESCRIBE ' . $table );
228
*/
229
private function searchReplace( $table, $page, $args ) {
230
231
+
232
// Load up the default settings for this chunk.
233
$table = esc_sql( $table );
234
$current_page = $this->options->job->start + $this->settings->queryLimit;
235
$pages = $this->get_pages_in_table( $table );
236
+ //$done = false;
237
238
239
if( $this->isSubDir() ) {
240
+ //$homeUrl = rtrim($this->homeUrl, "/") . $this->getSubDir() . $this->options->cloneDirectoryName;
241
// Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
242
$args['search_for'] = array(
243
+ rtrim( $this->homeUrl, "/" ) . $this->getSubDir(),
244
ABSPATH
245
);
246
247
$args['replace_with'] = array(
248
+ rtrim( $this->homeUrl, "/" ) . $this->getSubDir() . '/' . $this->options->cloneDirectoryName,
249
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
250
);
251
} else {
252
$args['search_for'] = array(
253
+ rtrim( $this->homeUrl, '/' ),
254
ABSPATH
255
);
256
$args['replace_with'] = array(
257
+ rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
258
rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
259
);
260
}
261
262
// // Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
263
// $args['search_for'] = array(
264
+ // $this->homeUrl,
265
// ABSPATH
266
// );
267
//
268
//
269
// $args['replace_with'] = array(
270
+ // rtrim( $this->homeUrl, '/' ) . '/' . $this->options->cloneDirectoryName,
271
// rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
272
// );
273
$args['replace_guids'] = 'off';
278
// Get a list of columns in this table.
279
list( $primary_key, $columns ) = $this->get_columns( $table );
280
281
// Bail out early if there isn't a primary key.
282
if( null === $primary_key ) {
283
return false;
295
$current_row++;
296
$update_sql = array();
297
$where_sql = array();
298
+ $upd = false;
299
300
foreach ( $columns as $column ) {
301
337
$i = 0;
338
foreach ( $args['search_for'] as $replace ) {
339
$dataRow = $this->recursive_unserialize_replace( $args['search_for'][$i], $args['replace_with'][$i], $dataRow, false, $args['case_insensitive'] );
340
$i++;
341
}
342
+ unset( $replace );
343
+ unset( $i );
344
345
// Something was changed
346
if( $row[$column] != $dataRow ) {
347
$update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
348
+ $upd = true;
349
}
350
}
351
352
// Determine what to do with updates.
353
if( $args['dry_run'] === 'on' ) {
354
// Don't do anything if a dry run
355
+ } elseif( $upd && !empty( $where_sql ) ) {
356
// If there are changes to make, run the query.
357
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
358
$result = $this->db->query( $sql );
359
360
if( !$result ) {
361
+ $this->log( "Error updating row {$current_row}", \WPStaging\Utils\Logger::TYPE_ERROR );
362
}
363
}
364
} // end row loop
365
+ unset( $row );
366
367
+ // if( $current_page >= $pages - 1 ) {
368
+ // $done = true;
369
+ // }
370
371
// DB Flush
372
$this->db->flush();
406
}
407
408
// Submitted by Tina Matter
409
+ elseif( $this->isValidObject($data) ) {
410
$_tmp = $data; // new $data_class( );
411
$props = get_object_vars( $data );
412
foreach ( $props as $key => $value ) {
436
return $data;
437
}
438
439
+ /**
440
+ * Check if the object is a valid one and not __PHP_Incomplete_Class_Name
441
+ * Can not use is_object alone because in php 7.2 it's returning true even though object is __PHP_Incomplete_Class_Name
442
+ * @return boolean
443
+ */
444
+ private function isValidObject($data){
445
+ if( !is_object( $data ) || gettype( $data ) != 'object' ) {
446
+ return false;
447
+ }
448
+
449
+ $invalid_class_props = get_object_vars( $data );
450
+
451
+ if (!isset($invalid_class_props['__PHP_Incomplete_Class_Name'])){
452
+ // Assume it must be an valid object
453
+ return true;
454
+ }
455
+
456
+ $invalid_object_class = $invalid_class_props['__PHP_Incomplete_Class_Name'];
457
+
458
+ if( !empty( $invalid_object_class ) ) {
459
+ return false;
460
+ }
461
+
462
+ // Assume it must be an valid object
463
+ return true;
464
+ }
465
+
466
/**
467
* Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net.
468
* @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248
470
* @param string $input The string to escape.
471
* @return string
472
*/
473
+ private function mysql_escape_mimic( $input ) {
474
if( is_array( $input ) ) {
475
return array_map( __METHOD__, $input );
476
}
489
*
490
* @return mixed, false on failure
491
*/
492
+ private static function unserialize( $serialized_string ) {
493
if( !is_serialized( $serialized_string ) ) {
494
return false;
495
}
510
*
511
* @return string
512
*/
513
+ private function str_replace( $from, $to, $data, $case_insensitive = false ) {
514
if( 'on' === $case_insensitive ) {
515
$data = str_ireplace( $from, $to, $data );
516
} else {
619
* Get the install sub directory if WP is installed in sub directory
620
* @return string
621
*/
622
+ private function getSubDir() {
623
+ $home = get_option( 'home' );
624
+ $siteurl = get_option( 'siteurl' );
625
626
+ if( empty( $home ) || empty( $siteurl ) ) {
627
return '/';
628
}
629
630
+ $dir = str_replace( $home, '', $siteurl );
631
+ return '/' . str_replace( '/', '', $dir );
632
}
633
634
}
apps/Backend/Modules/SystemInfo.php CHANGED
@@ -5,6 +5,7 @@ namespace WPStaging\Backend\Modules;
5
use WPStaging\DI\InjectionAware;
6
use WPStaging\Library\Browser;
7
use WPStaging\WPStaging;
8
9
// No Direct Access
10
if (!defined("WPINC"))
@@ -25,11 +26,18 @@ class SystemInfo extends InjectionAware
25
private $isMultiSite;
26
27
/**
28
* Initialize class
29
*/
30
public function initialize()
31
{
32
$this->isMultiSite = is_multisite();
33
}
34
35
/**
@@ -112,14 +120,14 @@ class SystemInfo extends InjectionAware
112
* Site Information
113
* @return string
114
*/
115
- public function site()
116
- {
117
- $output = "-- Site Info" . PHP_EOL . PHP_EOL;
118
- $output .= $this->info("Site URL:", site_url());
119
- $output .= $this->info("Home URL:", home_url());
120
- $output .= $this->info("Home Path:", get_home_path());
121
- $output .= $this->info("Installed in subdir:", ( $this->isSubDir() ? 'Yes' : 'No' )) ;
122
- $output .= $this->info("Multisite:", ($this->isMultiSite ? "Yes" : "No" ));
123
124
return apply_filters("wpstg_sysinfo_after_site_info", $output);
125
}
5
use WPStaging\DI\InjectionAware;
6
use WPStaging\Library\Browser;
7
use WPStaging\WPStaging;
8
+ use WPStaging\Utils;
9
10
// No Direct Access
11
if (!defined("WPINC"))
26
private $isMultiSite;
27
28
/**
29
+ *
30
+ * @var obj
31
+ */
32
+ private $helper;
33
+
34
+ /**
35
* Initialize class
36
*/
37
public function initialize()
38
{
39
$this->isMultiSite = is_multisite();
40
+ $this->helper = new Utils\Helper();
41
}
42
43
/**
120
* Site Information
121
* @return string
122
*/
123
+ public function site() {
124
+ $output = "-- Site Info" . PHP_EOL . PHP_EOL;
125
+ $output .= $this->info( "Site URL:", site_url() );
126
+ $output .= $this->info( "Home URL:", $this->helper->get_home_url() );
127
+ $output .= $this->info( "Home Path:", get_home_path() );
128
+ $output .= $this->info( "ABSPATH:", ABSPATH );
129
+ $output .= $this->info( "Installed in subdir:", ( $this->isSubDir() ? 'Yes' : 'No' ) );
130
+ $output .= $this->info( "Multisite:", ($this->isMultiSite ? "Yes" : "No" ) );
131
132
return apply_filters("wpstg_sysinfo_after_site_info", $output);
133
}
apps/Backend/Modules/Views/Forms/Settings.php CHANGED
@@ -74,8 +74,7 @@ class Settings {
74
);
75
76
$this->form["general"]->add(
77
- $element->setLabel( "File Copy Limit" )
78
- ->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : 1 )
79
);
80
81
@@ -104,7 +103,7 @@ class Settings {
104
);
105
106
$this->form["general"]->add(
107
- $element->setLabel( "CPU load priority" )
108
->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "fast" )
109
);
110
74
);
75
76
$this->form["general"]->add(
77
+ $element->setLabel( "File Copy Limit" )->setDefault( isset( $settings->fileLimit ) ? $settings->fileLimit : 1 )
78
);
79
80
103
);
104
105
$this->form["general"]->add(
106
+ $element->setLabel( "CPU Load Priority" )
107
->setDefault( isset( $settings->cpuLoad ) ? $settings->cpuLoad : "fast" )
108
);
109
apps/Backend/Upgrade/Upgrade.php CHANGED
@@ -4,14 +4,13 @@ namespace WPStaging\Backend\Upgrade;
4
5
use WPStaging\WPStaging;
6
use WPStaging\Utils\Logger;
7
-
8
9
/**
10
* Upgrade Class
11
* This must be loaded on every page init to ensure all settings are
12
* adjusted correctly and to run any upgrade process if necessary.
13
*/
14
-
15
// No Direct Access
16
if( !defined( "WPINC" ) ) {
17
die;
@@ -31,12 +30,6 @@ class Upgrade {
31
*/
32
private $clones;
33
34
- /**
35
- * Clone data
36
- * @var obj
37
- */
38
- //private $clonesBeta;
39
-
40
/**
41
* Cron data
42
* @var obj
@@ -55,7 +48,6 @@ class Upgrade {
55
*/
56
private $db;
57
58
-
59
public function __construct() {
60
61
// add wpstg_weekly_event to cron events
@@ -98,7 +90,7 @@ class Upgrade {
98
// Current options
99
$sites = get_option( "wpstg_existing_clones_beta", array() );
100
101
- if (false === $sites){
102
return;
103
}
104
@@ -118,8 +110,6 @@ class Upgrade {
118
}
119
}
120
121
-
122
-
123
/**
124
* Check and return prefix of the staging site
125
* @param string $directory
@@ -149,7 +139,6 @@ class Upgrade {
149
}
150
}
151
152
-
153
/**
154
* Upgrade method 2.0.3
155
*/
@@ -214,12 +203,10 @@ class Upgrade {
214
*/
215
private function upgradeClonesBeta() {
216
217
- // Get options
218
- $this->clones = get_option( "wpstg_existing_clones", array() );
219
220
- $new = array();
221
222
- if( empty( $this->clones ) ) {
223
return false;
224
}
225
@@ -233,11 +220,11 @@ class Upgrade {
233
234
$new[$value]['directoryName'] = $value;
235
$new[$value]['path'] = get_home_path() . $value;
236
- $new[$value]['url'] = get_home_url() . "/" . $value;
237
$new[$value]['number'] = $key + 1;
238
$new[$value]['version'] = $this->previousVersion;
239
- //$new[$value]['prefix'] = $value . '_';
240
-
241
}
242
unset( $value );
243
@@ -246,40 +233,6 @@ class Upgrade {
246
}
247
}
248
249
- /**
250
- * Convert clone data from wpstg 1.x to wpstg 2.x
251
- * Only use this later when wpstg 2.x is ready for production
252
- */
253
- // private function upgradeClones() {
254
- //
255
- // $new = array();
256
- //
257
- // // Get options
258
- // $this->clones = get_option( "wpstg_existing_clones", array() );
259
- //
260
- // if( empty( $this->clones ) ) {
261
- // return false;
262
- // }
263
- //
264
- // foreach ( $this->clones as $key => &$value ) {
265
- //
266
- // // Skip the rest of the loop if data is already compatible to wpstg 2.0.1
267
- // if( isset( $value['directoryName'] ) || !empty( $value['directoryName'] ) ) {
268
- // continue;
269
- // }
270
- // $new[$value]['directoryName'] = $value;
271
- // $new[$value]['path'] = get_home_path() . $value;
272
- // $new[$value]['url'] = get_home_url() . "/" . $value;
273
- // $new[$value]['number'] = $key + 1;
274
- // $new[$value]['version'] = $this->previousVersion;
275
- // }
276
- // unset( $value );
277
- //
278
- // if( empty( $new ) || false === update_option( 'wpstg_existing_clones', $new ) ) {
279
- // $this->logger->log( 'Failed to upgrade clone data from ' . $this->previousVersion . ' to ' . \WPStaging\WPStaging::VERSION );
280
- // }
281
- // }
282
-
283
/**
284
* Upgrade Notices db options from wpstg 1.3 -> 2.0.1
285
* Fix some logical db options
4
5
use WPStaging\WPStaging;
6
use WPStaging\Utils\Logger;
7
+ use WPStaging\Utils\Helper;
8
9
/**
10
* Upgrade Class
11
* This must be loaded on every page init to ensure all settings are
12
* adjusted correctly and to run any upgrade process if necessary.
13
*/
14
// No Direct Access
15
if( !defined( "WPINC" ) ) {
16
die;
30
*/
31
private $clones;
32
33
/**
34
* Cron data
35
* @var obj
48
*/
49
private $db;
50
51
public function __construct() {
52
53
// add wpstg_weekly_event to cron events
90
// Current options
91
$sites = get_option( "wpstg_existing_clones_beta", array() );
92
93
+ if( false === $sites || count($sites) === 0 ) {
94
return;
95
}
96
110
}
111
}
112
113
/**
114
* Check and return prefix of the staging site
115
* @param string $directory
139
}
140
}
141
142
/**
143
* Upgrade method 2.0.3
144
*/
203
*/
204
private function upgradeClonesBeta() {
205
206
207
+ $new = array();
208
209
+ if( empty( $this->clones ) || count( $this->clones ) === 0 ) {
210
return false;
211
}
212
220
221
$new[$value]['directoryName'] = $value;
222
$new[$value]['path'] = get_home_path() . $value;
223
+ $helper = new Helper();
224
+ $new[$value]['url'] = $helper->get_home_url() . "/" . $value;
225
$new[$value]['number'] = $key + 1;
226
$new[$value]['version'] = $this->previousVersion;
227
+ //$new[$value]['prefix'] = $value;
228
}
229
unset( $value );
230
233
}
234
}
235
236
/**
237
* Upgrade Notices db options from wpstg 1.3 -> 2.0.1
238
* Fix some logical db options
apps/Backend/views/_includes/header.php CHANGED
@@ -32,6 +32,6 @@
32
</div>
33
<div style="font-size:14px;">
34
Tutorial: <a href="https://wp-staging.com/docs/copy-staging-site-to-live-site/" target="_blank">Learn how to push changes to live website</a>
35
- <span style="float:right;"><a href="https://wordpress.org/support/plugin/wp-staging/reviews/?filter=5" target="_blank" rel="external noopener">Rate the plugin</a></span>
36
</div>
37
</div>
32
</div>
33
<div style="font-size:14px;">
34
Tutorial: <a href="https://wp-staging.com/docs/copy-staging-site-to-live-site/" target="_blank">Learn how to push changes to live website</a>
35
+ <span style="float:right;"><a href="https://wordpress.org/support/plugin/wp-staging/reviews/?filter=5" target="_blank" rel="external noopener">Rate it &#9733;&#9733;&#9733;</a></span>
36
</div>
37
</div>
apps/Backend/views/clone/includes/footer.php CHANGED
@@ -1,3 +1,4 @@
1
<div id="wpstg-error-wrapper">
2
<div id="wpstg-error-details"></div>
3
</div>
1
+ <div style="clear:both;">Something not working? Open a <a href="https://wp-staging.com/support" target="_blank" rel="external nofollow"> support ticket</a> and we help you fixing it quickly.</div>
2
<div id="wpstg-error-wrapper">
3
<div id="wpstg-error-details"></div>
4
</div>
apps/Backend/views/settings/index.php CHANGED
@@ -167,29 +167,6 @@
167
<?php echo $form->render( "wpstg_settings[cpuLoad]" ) ?>
168
</td>
169
</tr>
170
- <!-- Deactivated -->
171
- <tr class="row" style="display:none;">
172
- <td class="row th">
173
- <div class="col-title">
174
- <?php echo $form->label( "wpstg_settings[optimizer]" ) ?>
175
- <span class="description">
176
- Select the plugins that should be disabled during build process of the staging site.
177
- Some plugins slow down the copy process and add overhead to each request, requiring extra CPU and memory consumption.
178
- Some of them can interfere with cloning process and cause them to fail, so we recommend to select all plugins here.
179
-
180
- <br><br>
181
- <strong>Note:</strong> This does not disable plugins on your staging site. You have to disable them there separately.
182
- </span>
183
- </div>
184
- </td>
185
- <td>
186
- <?php echo $form->render( "wpstg_settings[optimizer]" ) ?>
187
- <div id="wpstg_pluginListing" style="display:none">
188
- <?php echo $form->render( "wpstg_settings[blackListedPlugins][]" ) ?>
189
- </div>
190
- </td>
191
- </tr>
192
-
193
<tr class="row">
194
<td class="row th">
195
<div class="col-title">
167
<?php echo $form->render( "wpstg_settings[cpuLoad]" ) ?>
168
</td>
169
</tr>
170
<tr class="row">
171
<td class="row th">
172
<div class="col-title">
apps/Core/DTO/Settings.php CHANGED
@@ -63,11 +63,13 @@ class Settings {
63
*/
64
protected $debugMode;
65
66
/**
67
* @var array
68
*/
69
protected $blackListedPlugins = array();
70
71
/**
72
* Settings constructor.
73
*/
@@ -98,19 +100,19 @@ class Settings {
98
/**
99
* @return bool
100
*/
101
- // public function save() {
102
- // $data = array();
103
- //
104
- // foreach ( get_object_vars( $this ) as $key => $value ) {
105
- // if( 0 == strpos( $key, '_' ) ) {
106
- // continue;
107
- // }
108
- //
109
- // $data[$key] = $value;
110
- // }
111
- //
112
- // return update_option( "wpstg_settings", $data );
113
- // }
114
115
/**
116
* @return array
63
*/
64
protected $debugMode;
65
66
+
67
/**
68
* @var array
69
*/
70
protected $blackListedPlugins = array();
71
72
+
73
/**
74
* Settings constructor.
75
*/
100
/**
101
* @return bool
102
*/
103
+ public function save() {
104
+ $data = array();
105
+
106
+ foreach ( get_object_vars( $this ) as $key => $value ) {
107
+ if( 0 == strpos( $key, '_' ) ) {
108
+ continue;
109
+ }
110
+
111
+ $data[$key] = $value;
112
+ }
113
+
114
+ return update_option( "wpstg_settings", $data );
115
+ }
116
117
/**
118
* @return array
apps/Core/Utils/Helper.php ADDED
@@ -0,0 +1,71 @@
1
+ <?php
2
+
3
+ namespace WPStaging\Utils;
4
+
5
+ // No Direct Access
6
+ if (!defined("WPINC"))
7
+ {
8
+ die;
9
+ }
10
+
11
+ class Helper {
12
+
13
+ /**
14
+ * Retrieves the URL for a given site where the front end is accessible.
15
+ * This is from WordPress source 4.9.5/src/wp-includes/link-template.php
16
+ * home_url filter has been removed here to make sure that wpml can not overwrite that value!
17
+ *
18
+ * Returns the 'home' option with the appropriate protocol. The protocol will be 'https'
19
+ * if is_ssl() evaluates to true; otherwise, it will be the same as the 'home' option.
20
+ * If `$scheme` is 'http' or 'https', is_ssl() is overridden.
21
+ *
22
+ * @since 3.0.0
23
+ *
24
+ * @global string $pagenow
25
+ *
26
+ * @param int $blog_id Optional. Site ID. Default null (current site).
27
+ * @param string $path Optional. Path relative to the home URL. Default empty.
28
+ * @param string|null $scheme Optional. Scheme to give the home URL context. Accepts
29
+ * 'http', 'https', 'relative', 'rest', or null. Default null.
30
+ * @return string Home URL link with optional path appended.
31
+ */
32
+ public function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
33
+ global $pagenow;
34
+
35
+ $orig_scheme = $scheme;
36
+
37
+ if( empty( $blog_id ) || !is_multisite() ) {
38
+ $url = get_option( 'home' );
39
+ } else {
40
+ switch_to_blog( $blog_id );
41
+ $url = get_option( 'home' );
42
+ restore_current_blog();
43
+ }
44
+
45
+ if( !in_array( $scheme, array('http', 'https', 'relative') ) ) {
46
+ if( is_ssl() && !is_admin() && 'wp-login.php' !== $pagenow )
47
+ $scheme = 'https';
48
+ else
49
+ $scheme = parse_url( $url, PHP_URL_SCHEME );
50
+ }
51
+
52
+ $url = set_url_scheme( $url, $scheme );
53
+
54
+ if( $path && is_string( $path ) )
55
+ $url .= '/' . ltrim( $path, '/' );
56
+
57
+ /**
58
+ * Filters the home URL.
59
+ *
60
+ * @since 3.0.0
61
+ *
62
+ * @param string $url The complete home URL including scheme and path.
63
+ * @param string $path Path relative to the home URL. Blank string if no path is specified.
64
+ * @param string|null $orig_scheme Scheme to give the home URL context. Accepts 'http', 'https',
65
+ * 'relative', 'rest', or null.
66
+ * @param int|null $blog_id Site ID, or null for the current site.
67
+ */
68
+ return $url;
69
+ }
70
+
71
+ }
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
/**
30
* Plugin version
31
*/
32
- const VERSION = "2.2.5";
33
34
/**
35
* Plugin name
29
/**
30
* Plugin version
31
*/
32
+ const VERSION = "2.2.6";
33
34
/**
35
* Plugin name
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: 4.9
12
- Stable tag: 2.2.5
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.
@@ -146,6 +146,17 @@ https://wp-staging.com
146
147
== Changelog ==
148
149
= 2.2.5 =
150
* New: Compatible to WP 4.9.5
151
* New: Allow to select and copy extra folders that are on the root level
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: 4.9
12
+ Stable tag: 2.2.6
13
Requires PHP: 5.3
14
15
A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
146
147
== Changelog ==
148
149
+ = 2.2.6 =
150
+ * Fix: If WPML is used the live site is not reachable
151
+ * Fix: Can not disable optimizer
152
+ * Fix: Stop cloning if wp_usermeta or wp_options can not be adapted
153
+ * Fix: All methods should be private in class SearchReplace
154
+ * Fix: PHP 7.2 is not countable warning
155
+ * Fix: PHP 7.2 can not replace data in objects when object is incomplete (__PHP_Incomplete_Class_Name)
156
+ * New: Use fully custom login form to prevent access denied issues on sites where access to wp-login.php is denied or redirection plugins are used
157
+ * New: Link to support section
158
+
159
+
160
= 2.2.5 =
161
* New: Compatible to WP 4.9.5
162
* New: Allow to select and copy extra folders that are on the root level
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.2.5
11
* Text Domain: wpstg
12
* Domain Path: /languages/
13
7
* Author: WP-Staging
8
* Author URI: https://wp-staging.com
9
* Contributors: ReneHermi, ilgityildirim
10
+ * Version: 2.2.6
11
* Text Domain: wpstg
12
* Domain Path: /languages/
13