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