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

Version Description

  • New: Compatible to WP 4.9.5
  • New: Allow to select and copy extra folders that are on the root level
  • 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
  • Fix: Incorrect login path to staging site if WordPress is installed in subdirectory
  • Fix: Login url is wrong if WP is installed in subfolder
  • Fix: If PHP 5.6.34 is used, the cloning process could be unfinished due to use of private member in protected class
  • Tweak: Only wp root folders are pre selected before cloning is starting
  • Tweak: Change WP_HOME or WP_SITEURL constants of staging site if they are defined in wp-config.php
Download this release

Release Info

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

Code changes from version 2.2.4 to 2.2.5

apps/Backend/Modules/Jobs/Data.php CHANGED
@@ -49,7 +49,7 @@ class Data extends JobExecutable
49
  */
50
  protected function calculateTotalSteps()
51
  {
52
- $this->options->totalSteps = 9;
53
  }
54
 
55
  /**
@@ -64,17 +64,6 @@ class Data extends JobExecutable
64
  // Save option, progress
65
  $this->saveOptions();
66
 
67
- // Prepare response
68
- // $this->response = array(
69
- // "status" => true,
70
- // "percentage" => 100,
71
- // "total" => $this->options->totalSteps,
72
- // "step" => $this->options->totalSteps,
73
- // "last_msg" => $this->logger->getLastLogMsg(),
74
- // "running_time" => $this->time() - time(),
75
- // "job_done" => true
76
- // );
77
-
78
  return (object) $this->response;
79
  }
80
 
@@ -174,29 +163,43 @@ class Data extends JobExecutable
174
  }
175
 
176
  /**
177
- * Replace "siteurl"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  * @return bool
179
  */
180
  protected function step1() {
181
- $this->log( "Search & Replace: Updating siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_INFO );
182
 
183
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
184
  return true;
185
  }
186
 
187
  // Installed in sub-directory
188
- //if( isset( $this->settings->wpSubDirectory ) && "1" === $this->settings->wpSubDirectory ) {
189
  if( $this->isSubDir() ) {
190
- $subDirectory = str_replace( get_home_path(), '', ABSPATH );
191
- $this->log( "Updating siteurl and homeurl to " . get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName );
192
  // Replace URLs
193
  $result = $this->db->query(
194
  $this->db->prepare(
195
- "UPDATE {$this->prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'", get_home_url() . '/' . $subDirectory . $this->options->cloneDirectoryName
196
  )
197
  );
198
  } else {
199
- $this->log( "Search & Replace:: Updating siteurl and homeurl to " . get_home_url() . '/' . $this->options->cloneDirectoryName );
200
  // Replace URLs
201
  $result = $this->db->query(
202
  $this->db->prepare(
@@ -211,7 +214,7 @@ class Data extends JobExecutable
211
  return true;
212
  }
213
 
214
- $this->log( "Search & Replace: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
215
  return false;
216
  }
217
 
@@ -222,7 +225,7 @@ class Data extends JobExecutable
222
  protected function step2()
223
  {
224
 
225
- $this->log( "Search & Replace: Updating row wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}" );
226
 
227
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
228
  return true;
@@ -252,7 +255,7 @@ class Data extends JobExecutable
252
  return true;
253
  }
254
 
255
- $this->log("Search & Replace: Failed to update wpstg_is_staging_site in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
256
  return false;
257
  }
258
 
@@ -263,7 +266,7 @@ class Data extends JobExecutable
263
  protected function step3()
264
  {
265
 
266
- $this->log("Search & Replace: Updating rewrite_rules in {$this->prefix}options {$this->db->last_error}");
267
 
268
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
269
  return true;
@@ -282,7 +285,7 @@ class Data extends JobExecutable
282
  return true;
283
  }
284
 
285
- $this->log("Failed to update rewrite_rules in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
286
  return true;
287
  }
288
 
@@ -291,7 +294,7 @@ class Data extends JobExecutable
291
  * @return bool
292
  */
293
  protected function step4() {
294
- $this->log( "Search & Replace: Updating {$this->prefix}usermeta db prefix {$this->db->last_error}" );
295
 
296
  if( false === $this->isTable( $this->prefix . 'usermeta' ) ) {
297
  return true;
@@ -304,11 +307,11 @@ class Data extends JobExecutable
304
  );
305
 
306
  if( !$resultOptions ) {
307
- $this->log( "Search & Replace: Failed to update usermeta meta_key database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
308
- return false;
309
  }
310
 
311
- $this->log( "Updating {$this->prefix}options, option_name database table prefixes; {$this->db->last_error}" );
312
 
313
  $resultUserMeta = $this->db->query(
314
  $this->db->prepare(
@@ -317,8 +320,8 @@ class Data extends JobExecutable
317
  );
318
 
319
  if( !$resultUserMeta ) {
320
- $this->log( "Search & Replace: Failed to update options, option_name database table prefixes; {$this->db->last_error}", Logger::TYPE_ERROR );
321
- return false;
322
  }
323
 
324
  return true;
@@ -332,10 +335,10 @@ class Data extends JobExecutable
332
  {
333
  $path = ABSPATH . $this->options->cloneDirectoryName . "/wp-config.php";
334
 
335
- $this->log("Search & Replace: Updating table_prefix in {$path} to " . $this->prefix);
336
  if (false === ($content = file_get_contents($path)))
337
  {
338
- $this->log("Search & Replace: Failed to update table_prefix in {$path}. Can't read contents", Logger::TYPE_ERROR);
339
  return false;
340
  }
341
 
@@ -347,7 +350,7 @@ class Data extends JobExecutable
347
 
348
  if (false === @file_put_contents($path, $content))
349
  {
350
- $this->log("Search & Replace: Failed to update $table_prefix in {$path} to " .$this->prefix . ". Can't save contents", Logger::TYPE_ERROR);
351
  return false;
352
  }
353
 
@@ -366,7 +369,7 @@ class Data extends JobExecutable
366
 
367
  if (!$this->isSubDir())
368
  {
369
- $this->debugLog("Search & Replace: WP installation is not in a subdirectory! All good, skipping this step");
370
  return true;
371
  }
372
 
@@ -374,7 +377,7 @@ class Data extends JobExecutable
374
 
375
  if (false === ($content = file_get_contents($path)))
376
  {
377
- $this->log("Search & Replace: Failed to reset {$path} for sub directory; can't read contents", Logger::TYPE_ERROR);
378
  return false;
379
  }
380
 
@@ -382,12 +385,12 @@ class Data extends JobExecutable
382
  if (!preg_match("/(require(.*)wp-blog-header.php' \);)/", $content, $matches))
383
  {
384
  $this->log(
385
- "Search & Replace: Failed to reset index.php for sub directory; wp-blog-header.php is missing",
386
  Logger::TYPE_ERROR
387
  );
388
  return false;
389
  }
390
- $this->log("Search & Replace: WP installation is in a subdirectory. Progressing...");
391
 
392
  $pattern = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
393
 
@@ -398,16 +401,16 @@ class Data extends JobExecutable
398
 
399
  if (null === ($content = preg_replace(array($pattern), $replace, $content)))
400
  {
401
- $this->log("Search & Replace: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR);
402
  return false;
403
  }
404
 
405
  if (false === @file_put_contents($path, $content))
406
  {
407
- $this->log("Search & Replace: Failed to reset index.php for sub directory; can't save contents", Logger::TYPE_ERROR);
408
  return false;
409
  }
410
- $this->Log("Search & Replace: Finished Step 6 successfully");
411
  return true;
412
  }
413
 
@@ -418,7 +421,7 @@ class Data extends JobExecutable
418
  protected function step7()
419
  {
420
 
421
- $this->log("Search & Replace: Updating wpstg_rmpermalinks_executed in {$this->prefix}options {$this->db->last_error}");
422
 
423
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
424
  return true;
@@ -434,7 +437,7 @@ class Data extends JobExecutable
434
  // All good
435
  if ($result)
436
  {
437
- $this->Log("Search & Replace: Finished Step 7 successfully");
438
  return true;
439
  }
440
 
@@ -449,7 +452,7 @@ class Data extends JobExecutable
449
  protected function step8()
450
  {
451
 
452
- $this->log("Search & Replace: Updating permalink_structure in {$this->prefix}options {$this->db->last_error}");
453
 
454
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
455
  return true;
@@ -465,7 +468,7 @@ class Data extends JobExecutable
465
  // All good
466
  if ($result)
467
  {
468
- $this->Log("Search & Replace: Finished Step 8 successfully");
469
  return true;
470
  }
471
 
@@ -479,7 +482,7 @@ class Data extends JobExecutable
479
  protected function step9()
480
  {
481
 
482
- $this->log("Search & Replace: Updating blog_public in {$this->prefix}options {$this->db->last_error}");
483
 
484
  if( false === $this->isTable( $this->prefix . 'options' ) ) {
485
  return true;
@@ -495,15 +498,112 @@ class Data extends JobExecutable
495
  // All good
496
  if ($result)
497
  {
498
- $this->Log("Search & Replace: Finished Step 9 successfully");
499
  return true;
500
  }
501
 
502
- $this->log("Failed to update blog_public in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR);
503
  return true;
504
  }
505
 
506
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  * Check if WP is installed in subdir
508
  * @return boolean
509
  */
49
  */
50
  protected function calculateTotalSteps()
51
  {
52
+ $this->options->totalSteps = 11;
53
  }
54
 
55
  /**
64
  // Save option, progress
65
  $this->saveOptions();
66
 
 
 
 
 
 
 
 
 
 
 
 
67
  return (object) $this->response;
68
  }
69
 
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' ) ) {
189
  return true;
190
  }
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(
214
  return true;
215
  }
216
 
217
+ $this->log( "Preparing Data Step1: Failed to update siteurl and homeurl in {$this->prefix}options {$this->db->last_error}", Logger::TYPE_ERROR );
218
  return false;
219
  }
220
 
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;
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
 
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;
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
 
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;
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(
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;
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
 
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
 
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
 
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
 
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
 
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
 
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;
437
  // All good
438
  if ($result)
439
  {
440
+ $this->Log("Preparing Data Step7: Finished Step 7 successfully");
441
  return true;
442
  }
443
 
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;
468
  // All good
469
  if ($result)
470
  {
471
+ $this->Log("Preparing Data Step8: Finished Step 8 successfully");
472
  return true;
473
  }
474
 
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;
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() );
517
+
518
+ if( false === ($content = file_get_contents( $path )) ) {
519
+ $this->log( "Preparing Data Step10: Failed to update WP_HOME in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
520
+ return false;
521
+ }
522
+
523
+
524
+ // Get WP_HOME from wp-config.php
525
+ preg_match( "/define\s*\(\s*'WP_HOME'\s*,\s*'(.*)'\s*\);/", $content, $matches );
526
+
527
+ if( !empty( $matches[1] ) ) {
528
+ $matches[1];
529
+
530
+ $pattern = "/define\s*\(\s*'WP_HOME'\s*,\s*'(.*)'\s*\);/";
531
+
532
+ $replace = "define('WP_HOME','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
533
+ $replace.= " // Changed by WP-Staging";
534
+
535
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
536
+ $this->log( "Preparing Data: Failed to reset index.php for sub directory; replacement failed", Logger::TYPE_ERROR );
537
+ return false;
538
+ }
539
+ } else {
540
+ $this->log( "Preparing Data Step10: WP_HOME not defined in wp-config.php. Skipping this step." );
541
+ }
542
+
543
+ if( false === @file_put_contents( $path, $content ) ) {
544
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
545
+ return false;
546
+ }
547
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
548
+ return true;
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() );
559
+
560
+ if( false === ($content = file_get_contents( $path )) ) {
561
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL in wp-config.php. Can't read wp-config.php", Logger::TYPE_ERROR );
562
+ return false;
563
+ }
564
+
565
+
566
+ // Get WP_SITEURL from wp-config.php
567
+ preg_match( "/define\s*\(\s*'WP_SITEURL'\s*,\s*'(.*)'\s*\);/", $content, $matches );
568
+
569
+ if( !empty( $matches[1] ) ) {
570
+ $matches[1];
571
+
572
+ $pattern = "/define\s*\(\s*'WP_SITEURL'\s*,\s*'(.*)'\s*\);/";
573
+
574
+ $replace = "define('WP_SITEURL','" . $this->getStagingSiteUrl() . "'); // " . $matches[1];
575
+ $replace.= " // Changed by WP-Staging";
576
+
577
+ if( null === ($content = preg_replace( array($pattern), $replace, $content )) ) {
578
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL", Logger::TYPE_ERROR );
579
+ return false;
580
+ }
581
+ } else {
582
+ $this->log( "Preparing Data Step11: WP_SITEURL not defined in wp-config.php. Skipping this step." );
583
+ }
584
+
585
+
586
+ if( false === @file_put_contents( $path, $content ) ) {
587
+ $this->log( "Preparing Data Step11: Failed to update WP_SITEURL. Can't save contents", Logger::TYPE_ERROR );
588
+ return false;
589
+ }
590
+ $this->Log( "Preparing Data: Finished Step 11 successfully" );
591
+ return true;
592
+ }
593
+
594
+ /**
595
+ * Return URL to staging site
596
+ * @return string
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
  */
apps/Backend/Modules/Jobs/Database.php CHANGED
@@ -147,7 +147,7 @@ class Database extends JobExecutable
147
  {
148
  $rows = $this->options->job->start+$this->settings->queryLimit;
149
  $this->log(
150
- "DB Copy: {$old} as {$new} between {$this->options->job->start} to {$rows} records"
151
  );
152
 
153
  $limitation = '';
@@ -193,7 +193,7 @@ class Database extends JobExecutable
193
  return true;
194
  }
195
 
196
- $this->log("DB Copy: Creating table {$new} like {$old}");
197
 
198
  $this->db->query("CREATE TABLE {$new} LIKE {$old}");
199
 
147
  {
148
  $rows = $this->options->job->start+$this->settings->queryLimit;
149
  $this->log(
150
+ "DB Copy: {$old} as {$new} from {$this->options->job->start} to {$rows} records"
151
  );
152
 
153
  $limitation = '';
193
  return true;
194
  }
195
 
196
+ $this->log("DB Copy: Creating table {$new}");
197
 
198
  $this->db->query("CREATE TABLE {$new} LIKE {$old}");
199
 
apps/Backend/Modules/Jobs/Directories.php CHANGED
@@ -3,12 +3,13 @@
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
- if (!defined("WPINC")) {
7
- die;
8
  }
9
 
10
- use WPStaging\WPStaging;
11
  use WPStaging\Utils\Logger;
 
12
  use WPStaging\Iterators\RecursiveDirectoryIterator;
13
  use WPStaging\Iterators\RecursiveFilterNewLine;
14
  use WPStaging\Iterators\RecursiveFilterExclude;
@@ -19,409 +20,509 @@ use WPStaging\Iterators\RecursiveFilterExclude;
19
  */
20
  class Directories extends JobExecutable {
21
 
22
- /**
23
- * @var array
24
- */
25
- private $files = array();
26
-
27
- /**
28
- * Total steps to do
29
- * @var int
30
- */
31
- private $total = 4;
32
- private $fileHandle;
33
-
34
- private $filename;
35
-
36
- /**
37
- * Initialize
38
- */
39
- public function initialize() {
40
- $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
41
- }
42
-
43
- /**
44
- * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
45
- * @return void
46
- */
47
- protected function calculateTotalSteps() {
48
- $this->options->totalSteps = $this->total;
49
- }
50
-
51
- /**
52
- * Start Module
53
- * @return object
54
- */
55
- public function start() {
56
-
57
- // Execute steps
58
- $this->run();
59
-
60
- // Save option, progress
61
- $this->saveProgress();
62
-
63
- return (object) $this->response;
64
- }
65
-
66
- /**
67
- * Step 1
68
- * Get WP Root files
69
- */
70
- private function getWpRootFiles() {
71
-
72
- // Skip it
73
- // if ($this->isDirectoryExcluded(ABSPATH)){
74
- // return true;
75
- // }
76
-
77
- // open file handle
78
- $files = $this->open($this->filename, 'a');
79
 
 
 
 
 
 
80
 
81
- try {
 
 
 
 
82
 
83
- // Iterate over wp root directory
84
- $iterator = new \DirectoryIterator(ABSPATH);
 
 
 
 
85
 
86
- $this->log( "Scanning ".ABSPATH." for its sub-directories and files" );
87
-
88
- // Write path line
89
- foreach ($iterator as $item) {
90
- if (!$item->isDot() && $item->isFile()) {
91
- if ($this->write($files, $iterator->getFilename() . PHP_EOL)) {
92
- $this->options->totalFiles++;
93
 
94
- // Add current file size
95
- $this->options->totalFileSize += $iterator->getSize();
96
- }
97
- }
98
- }
99
- } catch (\Exception $e) {
100
- $this->returnException('Error: ' . $e->getMessage());
101
- //throw new \Exception('Out of disk space.');
102
- } catch (\Exception $e) {
103
- // Skip bad file permissions
104
- }
105
-
106
- $this->close($files);
107
- return true;
108
-
109
- }
110
-
111
- /**
112
- * Step 2
113
- * Get WP Content Files
114
- */
115
- public function getWpContentFiles() {
116
-
117
- // Skip it
118
- if ($this->isDirectoryExcluded(WP_CONTENT_DIR)){
119
- return true;
120
- }
121
- // open file handle
122
- $files = $this->open($this->filename, 'a');
123
-
124
- $excludeWpContent = array(
125
- 'cache',
126
- 'wps-hide-login',
127
- 'node_modules',
128
- 'nbproject'
129
- );
130
-
131
- try {
132
-
133
- // Iterate over content directory
134
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(WP_CONTENT_DIR);
135
-
136
- // Exclude new line file names
137
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
138
-
139
- // Exclude uploads, plugins or themes
140
- $iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $excludeWpContent));
141
- // Recursively iterate over content directory
142
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
143
-
144
- $path = 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
145
- $this->log( "Scanning {$path} for its sub-directories and files" );
146
-
147
- // Write path line
148
- foreach ($iterator as $item) {
149
- if ($item->isFile()) {
150
- if ($this->write($files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
151
- $this->options->totalFiles++;
152
-
153
- // Add current file size
154
- $this->options->totalFileSize += $iterator->getSize();
155
- }
156
- }
157
- }
158
- } catch (\Exception $e) {
159
- //$this->returnException('Out of disk space.');
160
- throw new \Exception('Error: ' . $e->getMessage());
161
- } catch (\Exception $e) {
162
- // Skip bad file permissions
163
- }
164
-
165
- // close the file handler
166
- $this->close($files);
167
- return true;
168
- }
169
-
170
- /**
171
- * Step 3
172
- * @return boolean
173
- * @throws \Exception
174
- */
175
- public function getWpIncludesFiles() {
176
-
177
- // Skip it
178
- if ($this->isDirectoryExcluded(ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR)){
179
- return true;
180
- }
181
-
182
- // open file handle and attach data to end of file
183
- $files = $this->open($this->filename, 'a');
184
-
185
- try {
186
-
187
- // Iterate over wp-admin directory
188
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR);
189
-
190
- // Exclude new line file names
191
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
192
-
193
- // Exclude uploads, plugins or themes
194
- //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
195
- // Recursively iterate over wp-includes directory
196
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
197
-
198
- $path = ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR;
199
- $this->log( "Scanning {$path} for its sub-directories and files" );
200
-
201
- // Write files
202
- foreach ($iterator as $item) {
203
- if ($item->isFile()) {
204
- if ($this->write($files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
205
- $this->options->totalFiles++;
206
-
207
- // Add current file size
208
- $this->options->totalFileSize += $iterator->getSize();
209
- }
210
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  }
212
- } catch (\Exception $e) {
213
- //$this->returnException('Out of disk space.');
214
- throw new \Exception('Error: ' . $e->getMessage());
215
- } catch (\Exception $e) {
216
- // Skip bad file permissions
217
- }
218
-
219
- // close the file handler
220
- $this->close($files);
221
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  }
 
 
 
 
 
 
 
223
 
224
- /**
225
- * Step 4
226
- * @return boolean
227
- * @throws \Exception
228
- */
229
- public function getWpAdminFiles() {
230
 
231
- // Skip it
232
- if ($this->isDirectoryExcluded(ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR)) {
233
- return true;
234
- }
235
-
236
- // open file handle and attach data to end of file
237
- $files = $this->open($this->filename, 'a');
238
-
239
- try {
240
-
241
- // Iterate over wp-admin directory
242
- $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator(ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR);
243
-
244
- // Exclude new line file names
245
- $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine($iterator);
246
-
247
- // Exclude uploads, plugins or themes
248
- //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
249
- // Recursively iterate over content directory
250
- $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
251
-
252
- $path = ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR;
253
- $this->log("Scanning {$path} for its sub-directories and files");
254
-
255
- // Write path line
256
- foreach ($iterator as $item) {
257
- if ($item->isFile()) {
258
- if ($this->write($files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL)) {
259
- $this->options->totalFiles++;
260
- // Add current file size
261
- $this->options->totalFileSize += $iterator->getSize();
262
- }
 
 
 
 
 
 
263
  }
264
- }
265
- } catch (\Exception $e) {
266
- $this->returnException('Error: ' . $e->getMessage());
267
- //throw new \Exception('Error: ' . $e->getMessage());
268
- } catch (\Exception $e) {
269
- // Skip bad file permissions
270
- }
271
-
272
- // close the file handler
273
- $this->close($files);
274
- return true;
275
- }
276
-
277
- /**
278
- * Closes a file handle
279
- *
280
- * @param resource $handle File handle to close
281
- * @return boolean
282
- */
283
- public function close($handle) {
284
- return @fclose($handle);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  }
286
-
287
- /**
288
- * Opens a file in specified mode
289
- *
290
- * @param string $file Path to the file to open
291
- * @param string $mode Mode in which to open the file
292
- * @return resource
293
- * @throws Exception
294
- */
295
- public function open($file, $mode) {
296
-
297
- $file_handle = @fopen($file, $mode);
298
- if (false === $file_handle) {
299
- $this->returnException(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
300
- //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
301
- }
302
-
303
- return $file_handle;
304
- }
305
-
306
- /**
307
- * Write contents to a file
308
- *
309
- * @param resource $handle File handle to write to
310
- * @param string $content Contents to write to the file
311
- * @return integer
312
- * @throws Exception
313
- * @throws Exception
314
- */
315
- public function write($handle, $content) {
316
- $write_result = @fwrite($handle, $content);
317
- if (false === $write_result) {
318
- if (( $meta = \stream_get_meta_data($handle))) {
319
- //$this->returnException(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
320
- throw new \Exception(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
321
- }
322
- } elseif (strlen($content) !== $write_result) {
323
- //$this->returnException(__('Out of disk space.', 'wpstg'));
324
- throw new \Exception(__('Out of disk space.', 'wpstg'));
325
- }
326
-
327
- return $write_result;
328
- }
329
-
330
- /**
331
- * Execute the Current Step
332
- * Returns false when over threshold limits are hit or when the job is done, true otherwise
333
- * @return bool
334
- */
335
- protected function execute() {
336
-
337
- // No job left to execute
338
- if ($this->isFinished()) {
339
- $this->prepareResponse(true, false);
340
- return false;
341
- }
342
-
343
-
344
- if ($this->options->currentStep == 0) {
345
- $this->getWpRootFiles();
346
- $this->prepareResponse(false, true);
347
- return false;
348
- }
349
-
350
- if ($this->options->currentStep == 1) {
351
- $this->getWpContentFiles();
352
- $this->prepareResponse(false, true);
353
- return false;
354
- }
355
-
356
- if ($this->options->currentStep == 2) {
357
- $this->getWpIncludesFiles();
358
- $this->prepareResponse(false, true);
359
- return false;
360
- }
361
-
362
- if ($this->options->currentStep == 3) {
363
- $this->getWpAdminFiles();
364
- $this->prepareResponse(false, true);
365
- return false;
366
- }
367
-
368
-
369
- // Prepare response
370
- $this->prepareResponse(false, true);
371
- // Not finished
372
- return true;
373
- }
374
-
375
- /**
376
- * Checks Whether There is Any Job to Execute or Not
377
- * @return bool
378
- */
379
- protected function isFinished() {
380
- return (
381
- $this->options->currentStep > $this->total ||
382
- $this->options->currentStep == 4
383
- );
384
- }
385
-
386
-
387
-
388
-
389
- /**
390
- * Save files
391
- * @return bool
392
- */
393
- protected function saveProgress() {
394
- return $this->saveOptions();
395
- }
396
-
397
- /**
398
- * Get files
399
- * @return void
400
  */
401
- protected function getFiles() {
402
- $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
 
404
- if (false === ($this->files = @file_get_contents($fileName))) {
405
- $this->files = array();
406
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  }
 
 
 
 
 
 
 
408
 
409
- $this->files = explode(PHP_EOL, $this->files);
 
 
 
 
 
 
 
 
 
 
410
  }
411
 
412
- /**
413
- * Check if directory is excluded
414
- * @param string $directory
415
- * @return bool
416
- */
417
- protected function isDirectoryExcluded($directory) {
418
- foreach ($this->options->excludedDirectories as $excludedDirectory) {
419
- if (strpos($directory, $excludedDirectory) === 0) {
420
- return true;
421
- }
422
- }
423
 
424
- return false;
425
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
 
427
  }
3
  namespace WPStaging\Backend\Modules\Jobs;
4
 
5
  // No Direct Access
6
+ if( !defined( "WPINC" ) ) {
7
+ die;
8
  }
9
 
10
+ use WPStaging\WPStaging;
11
  use WPStaging\Utils\Logger;
12
+ use WPStaging\Utils\Strings;
13
  use WPStaging\Iterators\RecursiveDirectoryIterator;
14
  use WPStaging\Iterators\RecursiveFilterNewLine;
15
  use WPStaging\Iterators\RecursiveFilterExclude;
20
  */
21
  class Directories extends JobExecutable {
22
 
23
+ /**
24
+ * @var array
25
+ */
26
+ private $files = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ /**
29
+ * Total steps to do
30
+ * @var int
31
+ */
32
+ private $total = 4;
33
 
34
+ /**
35
+ * path to the cache file
36
+ * @var string
37
+ */
38
+ private $filename;
39
 
40
+ /**
41
+ * Initialize
42
+ */
43
+ public function initialize() {
44
+ $this->filename = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
45
+ }
46
 
47
+ /**
48
+ * Calculate Total Steps in This Job and Assign It to $this->options->totalSteps
49
+ * @return void
50
+ */
51
+ protected function calculateTotalSteps() {
 
 
52
 
53
+ $this->options->totalSteps = $this->total + count( $this->options->extraDirectories );
54
+ }
55
+
56
+ /**
57
+ * Start Module
58
+ * @return object
59
+ */
60
+ public function start() {
61
+
62
+ // Execute steps
63
+ $this->run();
64
+
65
+ // Save option, progress
66
+ $this->saveProgress();
67
+
68
+ return ( object ) $this->response;
69
+ }
70
+
71
+ /**
72
+ * Step 0
73
+ * Get WP Root files
74
+ * Does not collect any sub folders
75
+ */
76
+ private function getWpRootFiles() {
77
+
78
+ // open file handle
79
+ $files = $this->open( $this->filename, 'a' );
80
+
81
+
82
+ try {
83
+
84
+ // Iterate over wp root directory
85
+ $iterator = new \DirectoryIterator( ABSPATH );
86
+
87
+ $this->log( "Scanning / for files" );
88
+
89
+ // Write path line
90
+ foreach ( $iterator as $item ) {
91
+ if( !$item->isDot() && $item->isFile() ) {
92
+ if( $this->write( $files, $iterator->getFilename() . PHP_EOL ) ) {
93
+ $this->options->totalFiles++;
94
+
95
+ // Add current file size
96
+ $this->options->totalFileSize += $iterator->getSize();
97
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
+ }
100
+ } catch ( \Exception $e ) {
101
+ $this->returnException( 'Error: ' . $e->getMessage() );
102
+ //throw new \Exception('Out of disk space.');
103
+ } catch ( \Exception $e ) {
104
+ // Skip bad file permissions
105
+ }
106
+
107
+ $this->close( $files );
108
+ return true;
109
+ }
110
+
111
+ /**
112
+ * Step 2
113
+ * Get WP Content Files
114
+ */
115
+ private function getWpContentFiles() {
116
+
117
+ // Skip it
118
+ if( $this->isDirectoryExcluded( WP_CONTENT_DIR ) ) {
119
+ return true;
120
+ }
121
+ // open file handle
122
+ $files = $this->open( $this->filename, 'a' );
123
+
124
+ $excludeWpContent = array(
125
+ 'cache',
126
+ 'wps-hide-login',
127
+ 'node_modules',
128
+ 'nbproject'
129
+ );
130
+
131
+ try {
132
+
133
+ // Iterate over content directory
134
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( WP_CONTENT_DIR );
135
+
136
+ // Exclude new line file names
137
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
138
+
139
+ // Exclude uploads, plugins or themes
140
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, apply_filters( 'wpstg_exclude_content', $excludeWpContent ) );
141
+ // Recursively iterate over content directory
142
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
143
+
144
+ $this->log( "Scanning /wp-content for its sub-directories and files" );
145
+
146
+ // Write path line
147
+ foreach ( $iterator as $item ) {
148
+ if( $item->isFile() ) {
149
+ if( $this->write( $files, 'wp-content' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
150
+ $this->options->totalFiles++;
151
+
152
+ // Add current file size
153
+ $this->options->totalFileSize += $iterator->getSize();
154
+ }
155
  }
156
+ }
157
+ } catch ( \Exception $e ) {
158
+ //$this->returnException('Out of disk space.');
159
+ throw new \Exception( 'Error: ' . $e->getMessage() );
160
+ } catch ( \Exception $e ) {
161
+ // Skip bad file permissions
162
+ }
163
+
164
+ // close the file handler
165
+ $this->close( $files );
166
+ return true;
167
+ }
168
+
169
+ /**
170
+ * Step 2
171
+ * @return boolean
172
+ * @throws \Exception
173
+ */
174
+ private function getWpIncludesFiles() {
175
+
176
+ // Skip it
177
+ if( $this->isDirectoryExcluded( ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR ) ) {
178
+ return true;
179
+ }
180
+
181
+ // open file handle and attach data to end of file
182
+ $files = $this->open( $this->filename, 'a' );
183
+
184
+ try {
185
+
186
+ // Iterate over wp-admin directory
187
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( ABSPATH . 'wp-includes' . DIRECTORY_SEPARATOR );
188
+
189
+ // Exclude new line file names
190
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
191
+
192
+ // Exclude uploads, plugins or themes
193
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
194
+ // Recursively iterate over wp-includes directory
195
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
196
+
197
+ $this->log( "Scanning /wp-includes for its sub-directories and files" );
198
+
199
+ // Write files
200
+ foreach ( $iterator as $item ) {
201
+ if( $item->isFile() ) {
202
+ if( $this->write( $files, 'wp-includes' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
203
+ $this->options->totalFiles++;
204
+
205
+ // Add current file size
206
+ $this->options->totalFileSize += $iterator->getSize();
207
+ }
208
  }
209
+ }
210
+ } catch ( \Exception $e ) {
211
+ //$this->returnException('Out of disk space.');
212
+ throw new \Exception( 'Error: ' . $e->getMessage() );
213
+ } catch ( \Exception $e ) {
214
+ // Skip bad file permissions
215
+ }
216
 
217
+ // close the file handler
218
+ $this->close( $files );
219
+ return true;
220
+ }
 
 
221
 
222
+ /**
223
+ * Step 3
224
+ * @return boolean
225
+ * @throws \Exception
226
+ */
227
+ private function getWpAdminFiles() {
228
+
229
+ // Skip it
230
+ if( $this->isDirectoryExcluded( ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR ) ) {
231
+ return true;
232
+ }
233
+
234
+ // open file handle and attach data to end of file
235
+ $files = $this->open( $this->filename, 'a' );
236
+
237
+ try {
238
+
239
+ // Iterate over wp-admin directory
240
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR );
241
+
242
+ // Exclude new line file names
243
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
244
+
245
+ // Exclude uploads, plugins or themes
246
+ //$iterator = new \WPStaging\Iterators\RecursiveFilterExclude($iterator, apply_filters('wpstg_exclude_content', $exclude_filters));
247
+ // Recursively iterate over content directory
248
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
249
+
250
+ $this->log( "Scanning /wp-admin for its sub-directories and files" );
251
+
252
+ // Write path line
253
+ foreach ( $iterator as $item ) {
254
+ if( $item->isFile() ) {
255
+ if( $this->write( $files, 'wp-admin' . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
256
+ $this->options->totalFiles++;
257
+ // Add current file size
258
+ $this->options->totalFileSize += $iterator->getSize();
259
+ }
260
  }
261
+ }
262
+ } catch ( \Exception $e ) {
263
+ $this->returnException( 'Error: ' . $e->getMessage() );
264
+ //throw new \Exception('Error: ' . $e->getMessage());
265
+ } catch ( \Exception $e ) {
266
+ // Skip bad file permissions
267
+ }
268
+
269
+ // close the file handler
270
+ $this->close( $files );
271
+ return true;
272
+ }
273
+
274
+ /**
275
+ * Step 4 - x
276
+ * Get extra folders of the wp root level
277
+ * Does not collect wp-includes, wp-admin and wp-content folder
278
+ */
279
+ private function getExtraFiles( $folder ) {
280
+
281
+
282
+ // open file handle and attach data to end of file
283
+ $files = $this->open( $this->filename, 'a' );
284
+
285
+ try {
286
+
287
+ // Iterate over wp-admin directory
288
+ $iterator = new \WPStaging\Iterators\RecursiveDirectoryIterator( $folder );
289
+
290
+ // Exclude new line file names
291
+ $iterator = new \WPStaging\Iterators\RecursiveFilterNewLine( $iterator );
292
+
293
+ // Exclude wp core folders
294
+ // $exclude = array('wp-includes',
295
+ // 'wp-admin',
296
+ // 'wp-content');
297
+ //
298
+ // $excludeMore = array();
299
+ // foreach ($this->options->excludedDirectories as $key => $value){
300
+ // $excludeMore[] = $this->getLastElemAfterString('/', $value);
301
+ // }
302
+ //$exclude = array_merge($exclude, $excludeMore);
303
+
304
+ $exclude = array();
305
+
306
+ $iterator = new \WPStaging\Iterators\RecursiveFilterExclude( $iterator, $exclude );
307
+ // Recursively iterate over content directory
308
+ $iterator = new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD );
309
+
310
+ $strings = new Strings();
311
+ $this->log( "Scanning {$strings->getLastElemAfterString( '/', $folder )} for its sub-directories and files" );
312
+
313
+ // Write path line
314
+ foreach ( $iterator as $item ) {
315
+ if( $item->isFile() ) {
316
+ if( $this->write( $files, $strings->getLastElemAfterString( '/', $folder ) . DIRECTORY_SEPARATOR . $iterator->getSubPathName() . PHP_EOL ) ) {
317
+ $this->options->totalFiles++;
318
+ // Add current file size
319
+ $this->options->totalFileSize += $iterator->getSize();
320
+ }
321
  }
322
+ }
323
+ } catch ( \Exception $e ) {
324
+ $this->returnException( 'Error: ' . $e->getMessage() );
325
+ } catch ( \Exception $e ) {
326
+ // Skip bad file permissions
327
+ }
328
+
329
+ // close the file handler
330
+ $this->close( $files );
331
+ return true;
332
+
333
+
334
+ // Skip it
335
+ // if ($this->isDirectoryExcluded(ABSPATH)){
336
+ // return true;
337
+ // }
338
+ // // open file handle
339
+ // $files = $this->open($this->filename, 'a');
340
+ //
341
+ //
342
+ // try {
343
+ //
344
+ // // Iterate over wp root directory
345
+ // $iterator = new \DirectoryIterator(ABSPATH);
346
+ //
347
+ // $this->log( "Scanning ".ABSPATH." for its sub-directories and files" );
348
+ //
349
+ // // Write path line
350
+ // foreach ($iterator as $item) {
351
+ // if (!$item->isDot() && $item->isFile()) {
352
+ // if ($this->write($files, $iterator->getFilename() . PHP_EOL)) {
353
+ // $this->options->totalFiles++;
354
+ //
355
+ // // Add current file size
356
+ // $this->options->totalFileSize += $iterator->getSize();
357
+ // }
358
+ // }
359
+ // }
360
+ // } catch (\Exception $e) {
361
+ // $this->returnException('Error: ' . $e->getMessage());
362
+ // //throw new \Exception('Out of disk space.');
363
+ // } catch (\Exception $e) {
364
+ // // Skip bad file permissions
365
+ // }
366
+ //
367
+ // $this->close($files);
368
+ // return true;
369
+ }
370
+
371
+ /**
372
+ * Closes a file handle
373
+ *
374
+ * @param resource $handle File handle to close
375
+ * @return boolean
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  */
377
+ public function close( $handle ) {
378
+ return @fclose( $handle );
379
+ }
380
+
381
+ /**
382
+ * Opens a file in specified mode
383
+ *
384
+ * @param string $file Path to the file to open
385
+ * @param string $mode Mode in which to open the file
386
+ * @return resource
387
+ * @throws Exception
388
+ */
389
+ public function open( $file, $mode ) {
390
+
391
+ $file_handle = @fopen( $file, $mode );
392
+ if( false === $file_handle ) {
393
+ $this->returnException( sprintf( __( 'Unable to open %s with mode %s', 'wpstg' ), $file, $mode ) );
394
+ //throw new Exception(sprintf(__('Unable to open %s with mode %s', 'wpstg'), $file, $mode));
395
+ }
396
 
397
+ return $file_handle;
398
+ }
399
+
400
+ /**
401
+ * Write contents to a file
402
+ *
403
+ * @param resource $handle File handle to write to
404
+ * @param string $content Contents to write to the file
405
+ * @return integer
406
+ * @throws Exception
407
+ * @throws Exception
408
+ */
409
+ public function write( $handle, $content ) {
410
+ $write_result = @fwrite( $handle, $content );
411
+ if( false === $write_result ) {
412
+ if( ( $meta = \stream_get_meta_data( $handle ) ) ) {
413
+ //$this->returnException(sprintf(__('Unable to write to: %s', 'wpstg'), $meta['uri']));
414
+ throw new \Exception( sprintf( __( 'Unable to write to: %s', 'wpstg' ), $meta['uri'] ) );
415
  }
416
+ } elseif( strlen( $content ) !== $write_result ) {
417
+ //$this->returnException(__('Out of disk space.', 'wpstg'));
418
+ throw new \Exception( __( 'Out of disk space.', 'wpstg' ) );
419
+ }
420
+
421
+ return $write_result;
422
+ }
423
 
424
+ /**
425
+ * Execute the Current Step
426
+ * Returns false when over threshold limits are hit or when the job is done, true otherwise
427
+ * @return bool
428
+ */
429
+ protected function execute() {
430
+
431
+ // No job left to execute
432
+ if( $this->isFinished() ) {
433
+ $this->prepareResponse( true, false );
434
+ return false;
435
  }
436
 
 
 
 
 
 
 
 
 
 
 
 
437
 
438
+ if( $this->options->currentStep == 0 ) {
439
+ $this->getWpRootFiles();
440
+ $this->prepareResponse( false, true );
441
+ return false;
442
+ }
443
+
444
+ if( $this->options->currentStep == 1 ) {
445
+ $this->getWpContentFiles();
446
+ $this->prepareResponse( false, true );
447
+ return false;
448
+ }
449
+
450
+ if( $this->options->currentStep == 2 ) {
451
+ $this->getWpIncludesFiles();
452
+ $this->prepareResponse( false, true );
453
+ return false;
454
+ }
455
+
456
+ if( $this->options->currentStep == 3 ) {
457
+ $this->getWpAdminFiles();
458
+ $this->prepareResponse( false, true );
459
+ return false;
460
+ }
461
+
462
+ if( isset( $this->options->extraDirectories[$this->options->currentStep - $this->total] ) ) {
463
+ $this->getExtraFiles( $this->options->extraDirectories[$this->options->currentStep - $this->total] );
464
+ $this->prepareResponse( false, true );
465
+ return false;
466
+ }
467
+
468
+
469
+ // Prepare response
470
+ $this->prepareResponse( false, true );
471
+ // Not finished
472
+ return true;
473
+ }
474
+
475
+ /**
476
+ * Checks Whether There is Any Job to Execute or Not
477
+ * @return bool
478
+ */
479
+ protected function isFinished() {
480
+ if( $this->options->currentStep >= $this->options->totalSteps ) {
481
+ return true;
482
+ }
483
+
484
+ // return (
485
+ // //$this->options->currentStep > $this->total ||
486
+ // $this->options->currentStep >= $this->options->totalSteps
487
+ // );
488
+ }
489
+
490
+ /**
491
+ * Save files
492
+ * @return bool
493
+ */
494
+ protected function saveProgress() {
495
+ return $this->saveOptions();
496
+ }
497
+
498
+ /**
499
+ * Get files
500
+ * @return void
501
+ */
502
+ protected function getFiles() {
503
+ $fileName = $this->cache->getCacheDir() . "files_to_copy." . $this->cache->getCacheExtension();
504
+
505
+ if( false === ($this->files = @file_get_contents( $fileName )) ) {
506
+ $this->files = array();
507
+ return;
508
+ }
509
+
510
+ $this->files = explode( PHP_EOL, $this->files );
511
+ }
512
+
513
+ /**
514
+ * Check if directory is excluded
515
+ * @param string $directory
516
+ * @return bool
517
+ */
518
+ protected function isDirectoryExcluded( $directory ) {
519
+ foreach ( $this->options->excludedDirectories as $excludedDirectory ) {
520
+ if( strpos( $directory, $excludedDirectory ) === 0 ) {
521
+ return true;
522
+ }
523
+ }
524
+
525
+ return false;
526
+ }
527
 
528
  }
apps/Backend/Modules/Jobs/Files.php CHANGED
@@ -340,6 +340,11 @@ class Files extends JobExecutable {
340
  * @return bool
341
  */
342
  private function isDirectoryExcluded($directory) {
 
 
 
 
 
343
  foreach ($this->options->excludedDirectories as $excludedDirectory) {
344
  if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
345
  return true;
340
  * @return bool
341
  */
342
  private function isDirectoryExcluded($directory) {
343
+ // Make sure that wp-staging-pro directory / plugin is never excluded
344
+ if (false !== strpos( $directory, 'wp-staging' ) || false !== strpos( $directory, 'wp-staging-pro' ) ){
345
+ return false;
346
+ }
347
+
348
  foreach ($this->options->excludedDirectories as $excludedDirectory) {
349
  if (strpos($directory, $excludedDirectory) === 0 && !$this->isExtraDirectory($directory)) {
350
  return true;
apps/Backend/Modules/Jobs/Scan.php CHANGED
@@ -75,6 +75,7 @@ class Scan extends Job
75
 
76
  // Directories
77
  $this->options->includedDirectories = array();
 
78
  $this->options->excludedDirectories = array();
79
  $this->options->extraDirectories = array();
80
  $this->options->directoriesToCopy = array();
@@ -137,9 +138,8 @@ class Scan extends Job
137
  $output = '';
138
  foreach ($directories as $name => $directory)
139
  {
140
-
141
  if (!is_array($directory)) {
142
- // Not a directory, possibly a symlink, therefore we will skip it
143
  continue;
144
  }
145
 
@@ -152,17 +152,31 @@ class Scan extends Job
152
  in_array($data["path"], $this->options->includedDirectories)
153
  );
154
 
155
- $isDisabled = ($this->options->existingClones && isset($this->options->existingClones[$name]));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  $output .= "<div class='wpstg-dir'>";
158
- $output .= "<input type='checkbox' class='wpstg-check-dir'";
159
 
160
  if ($isChecked && !$isDisabled && !$forceDisabled) $output .= " checked";
161
- if ($forceDisabled || $isDisabled) $output .= " disabled";
162
 
163
  $output .= " name='selectedDirectories[]' value='{$data["path"]}'>";
164
 
165
- $output .= "<a href='#' class='wpstg-expand-dirs";
166
  if (!$isChecked || $isDisabled) $output .= " disabled";
167
  $output .= "'>{$name}";
168
  $output .= "</a>";
@@ -245,7 +259,7 @@ class Scan extends Job
245
  // continue;
246
  // }
247
  // Create array of unchecked tables
248
- if (0 !== strpos($table->Name, $wpDB->prefix))
249
  {
250
  $this->options->excludedTables[] = $table->Name;
251
  }
75
 
76
  // Directories
77
  $this->options->includedDirectories = array();
78
+ $this->options->includedExtraDirectories= array();
79
  $this->options->excludedDirectories = array();
80
  $this->options->extraDirectories = array();
81
  $this->options->directoriesToCopy = array();
138
  $output = '';
139
  foreach ($directories as $name => $directory)
140
  {
141
+ // Not a directory, possibly a symlink, therefore we will skip it
142
  if (!is_array($directory)) {
 
143
  continue;
144
  }
145
 
152
  in_array($data["path"], $this->options->includedDirectories)
153
  );
154
 
155
+ //$isDisabled = ($this->options->existingClones && isset($this->options->existingClones[$name]));
156
+
157
+ // Include wp core folders and their sub dirs.
158
+ // Exclude all other folders (default setting)
159
+ $isDisabled = ($name !== 'wp-admin' &&
160
+ $name !== 'wp-includes' &&
161
+ $name !== 'wp-content') &&
162
+ false === strpos( strrev($data["path"]), strrev("wp-admin") ) &&
163
+ false === strpos( strrev($data["path"]), strrev("wp-includes") ) &&
164
+ false === strpos( strrev($data["path"]), strrev("wp-content") )
165
+ ? true : false;
166
+
167
+ // Extra class to differentiate between wp core and non core folders
168
+ $class = !$isDisabled ? 'wpstg-root' : 'wpstg-extra';
169
+
170
 
171
  $output .= "<div class='wpstg-dir'>";
172
+ $output .= "<input type='checkbox' class='wpstg-check-dir " . $class . "'";
173
 
174
  if ($isChecked && !$isDisabled && !$forceDisabled) $output .= " checked";
175
+ //if ($forceDisabled || $isDisabled) $output .= " disabled";
176
 
177
  $output .= " name='selectedDirectories[]' value='{$data["path"]}'>";
178
 
179
+ $output .= "<a href='#' class='wpstg-expand-dirs ";
180
  if (!$isChecked || $isDisabled) $output .= " disabled";
181
  $output .= "'>{$name}";
182
  $output .= "</a>";
259
  // continue;
260
  // }
261
  // Create array of unchecked tables
262
+ if (!empty($wpDB->prefix) && 0 !== strpos($table->Name, $wpDB->prefix))
263
  {
264
  $this->options->excludedTables[] = $table->Name;
265
  }
apps/Backend/Modules/Jobs/SearchReplace.php CHANGED
@@ -44,6 +44,10 @@ class SearchReplace extends JobExecutable {
44
  }
45
 
46
  public function start() {
 
 
 
 
47
 
48
  $this->run();
49
 
@@ -104,13 +108,13 @@ class SearchReplace extends JobExecutable {
104
  return true;
105
  }
106
 
107
- private function convertExcludedTables() {
108
- $tmp = array();
109
- foreach ( $this->options->excludedTables as $table ) {
110
- $tmp[] = str_replace( $this->options->prefix, $this->tmpPrefix, $table );
111
- }
112
- $this->options->excludedTables = $tmp;
113
- }
114
 
115
  /**
116
  * Stop Execution immediately
@@ -220,15 +224,41 @@ class SearchReplace extends JobExecutable {
220
  $pages = $this->get_pages_in_table( $table );
221
  $done = false;
222
 
 
 
 
223
  // Search URL example.com/staging and root path to staging site /var/www/htdocs/staging
224
  $args['search_for'] = array(
225
- get_home_url(),
226
  ABSPATH
227
  );
 
228
  $args['replace_with'] = array(
 
 
 
 
 
 
 
 
 
229
  rtrim( get_home_url(), '/' ) . '/' . $this->options->cloneDirectoryName,
230
  rtrim( ABSPATH, '/' ) . '/' . $this->options->cloneDirectoryName
231
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  $args['replace_guids'] = 'off';
233
  $args['dry_run'] = 'off';
234
  $args['case_insensitive'] = false;
@@ -257,7 +287,7 @@ class SearchReplace extends JobExecutable {
257
  $current_row++;
258
  $update_sql = array();
259
  $where_sql = array();
260
- $upd = false;
261
 
262
  foreach ( $columns as $column ) {
263
 
@@ -309,23 +339,21 @@ class SearchReplace extends JobExecutable {
309
  // Something was changed
310
  if( $row[$column] != $dataRow ) {
311
  $update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $dataRow ) . '"';
312
- $upd = true;
 
313
  }
314
  }
315
 
316
  // Determine what to do with updates.
317
  if( $args['dry_run'] === 'on' ) {
318
  // Don't do anything if a dry run
319
- } elseif( $upd && !empty( $where_sql ) ) {
320
  // If there are changes to make, run the query.
321
  $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
322
-
323
  $result = $this->db->query( $sql );
324
 
325
  if( !$result ) {
326
- //$this->log("Error updating row {$current_row} {$sql}", \WPStaging\Utils\Logger::TYPE_ERROR);
327
- } else {
328
- // Do nothing
329
  }
330
  }
331
  } // end row loop
@@ -545,4 +573,31 @@ class SearchReplace extends JobExecutable {
545
  );
546
  }
547
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  }
44
  }
45
 
46
  public function start() {
47
+ // Skip job. Nothing to do
48
+ if ($this->options->totalSteps === 0){
49
+ $this->prepareResponse( true, false );
50
+ }
51
 
52
  $this->run();
53
 
108
  return true;
109
  }
110
 
111
+ // private function convertExcludedTables() {
112
+ // $tmp = array();
113
+ // foreach ( $this->options->excludedTables as $table ) {
114
+ // $tmp[] = str_replace( $this->options->prefix, $this->tmpPrefix, $table );
115
+ // }
116
+ // $this->options->excludedTables = $tmp;
117
+ // }
118
 
119
  /**
120
  * Stop Execution immediately
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';
263
  $args['dry_run'] = 'off';
264
  $args['case_insensitive'] = false;
287
  $current_row++;
288
  $update_sql = array();
289
  $where_sql = array();
290
+ $isUpdate = false;
291
 
292
  foreach ( $columns as $column ) {
293
 
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
573
  );
574
  }
575
 
576
+ /**
577
+ * Check if WP is installed in subdir
578
+ * @return boolean
579
+ */
580
+ private function isSubDir(){
581
+ if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
582
+ return true;
583
+ }
584
+ return false;
585
+ }
586
+
587
+ /**
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
  }
apps/Backend/Modules/SystemInfo.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  namespace WPStaging\Backend\Modules;
3
 
4
  use WPStaging\DI\InjectionAware;
@@ -23,7 +24,6 @@ class SystemInfo extends InjectionAware
23
  */
24
  private $isMultiSite;
25
 
26
-
27
  /**
28
  * Initialize class
29
  */
@@ -45,9 +45,8 @@ class SystemInfo extends InjectionAware
45
  * Get System Information as text
46
  * @return string
47
  */
48
- public function get()
49
- {
50
- $output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
51
 
52
  $output .= $this->wpstaging();
53
 
@@ -72,8 +71,6 @@ class SystemInfo extends InjectionAware
72
  return $output;
73
  }
74
 
75
-
76
-
77
  /**
78
  * @param string $string
79
  * @return string
@@ -167,6 +164,12 @@ class SystemInfo extends InjectionAware
167
  $output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
168
  }
169
 
 
 
 
 
 
 
170
  //$output .= PHP_EOL . PHP_EOL;
171
 
172
  $output .= $this->info( "Plugin Version:", get_option('wpstg_version', 'undefined') );
@@ -274,6 +277,16 @@ class SystemInfo extends InjectionAware
274
 
275
  $output .= $this->info("Table Prefix:", $tablePrefix);
276
 
 
 
 
 
 
 
 
 
 
 
277
  // WP Debug
278
  $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set");
279
  $output .= $this->info("Memory Limit:", WP_MEMORY_LIMIT);
1
  <?php
2
+
3
  namespace WPStaging\Backend\Modules;
4
 
5
  use WPStaging\DI\InjectionAware;
24
  */
25
  private $isMultiSite;
26
 
 
27
  /**
28
  * Initialize class
29
  */
45
  * Get System Information as text
46
  * @return string
47
  */
48
+ public function get() {
49
+ $output = "### Begin System Info ###" . PHP_EOL . PHP_EOL;
 
50
 
51
  $output .= $this->wpstaging();
52
 
71
  return $output;
72
  }
73
 
 
 
74
  /**
75
  * @param string $string
76
  * @return string
164
  $output .= $this->info( "Version:", isset( $clone['version'] ) ? $clone['version'] : 'undefined' ) . PHP_EOL . PHP_EOL;
165
  }
166
 
167
+
168
+ $output .= $this->info( "Raw Clones Data:", json_encode( get_option( 'wpstg_existing_clones_beta', 'undefined' ) ) );
169
+
170
+ $output .= '' . PHP_EOL;
171
+
172
+
173
  //$output .= PHP_EOL . PHP_EOL;
174
 
175
  $output .= $this->info( "Plugin Version:", get_option('wpstg_version', 'undefined') );
277
 
278
  $output .= $this->info("Table Prefix:", $tablePrefix);
279
 
280
+ // Constants
281
+ $output .= $this->info( "WP Content Path:", WP_CONTENT_DIR );
282
+ $output .= $this->info( "WP Plugin Dir:", WP_PLUGIN_DIR );
283
+ if (defined('UPLOADS'))
284
+ $output .= $this->info( "WP UPLOADS CONST:", UPLOADS );
285
+ $uploads = wp_upload_dir();
286
+ $output .= $this->info( "WP Uploads Dir:", wp_basename( $uploads['baseurl'] ) );
287
+ if (defined('WP_TEMP_DIR'))
288
+ $output .= $this->info( "WP Temp Dir:", WP_TEMP_DIR );
289
+
290
  // WP Debug
291
  $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? WP_DEBUG ? "Enabled" : "Disabled" : "Not set");
292
  $output .= $this->info("Memory Limit:", WP_MEMORY_LIMIT);
apps/Backend/public/js/wpstg-admin.js CHANGED
@@ -533,12 +533,12 @@ var WPStaging = (function ($)
533
  {
534
  var includedDirectories = [];
535
 
536
- $(".wpstg-dir input:checked").each(function () {
537
  var $this = $(this);
538
- if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
539
- {
540
  includedDirectories.push($this.val());
541
- }
542
  });
543
 
544
  return includedDirectories;
@@ -552,7 +552,8 @@ var WPStaging = (function ($)
552
  {
553
  var excludedDirectories = [];
554
 
555
- $(".wpstg-dir input:not(:checked)").each(function () {
 
556
  var $this = $(this);
557
  //if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
558
  //{
@@ -562,27 +563,43 @@ var WPStaging = (function ($)
562
 
563
  return excludedDirectories;
564
  };
565
-
566
  /**
567
- * Get Included Extra Directories
 
568
  * @returns {Array}
569
  */
570
  var getIncludedExtraDirectories = function ()
571
  {
572
  var extraDirectories = [];
573
 
574
- if (!$("#wpstg_extraDirectories").val()) {
575
- return extraDirectories;
576
- }
577
-
578
- var extraDirectories = $("#wpstg_extraDirectories").val().split(/\r?\n/);
579
- console.log(extraDirectories);
580
-
581
- //excludedDirectories.push($this.val());
582
 
583
  return extraDirectories;
584
  };
585
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586
 
587
 
588
  /**
@@ -994,7 +1011,6 @@ var WPStaging = (function ($)
994
  // Undefined Error
995
  if (false === response)
996
  {
997
- showError("Unknown Error, please try again");
998
  showError(
999
  "Something went wrong! Error: No response. Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low.'" +
1000
  "Than try again. If that does not help, " +
533
  {
534
  var includedDirectories = [];
535
 
536
+ $(".wpstg-dir input:checked.wpstg-root").each(function () {
537
  var $this = $(this);
538
+ //if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
539
+ //{
540
  includedDirectories.push($this.val());
541
+ //}
542
  });
543
 
544
  return includedDirectories;
552
  {
553
  var excludedDirectories = [];
554
 
555
+ //$(".wpstg-dir .wpstg-root input:not(:checked)").each(function () {
556
+ $(".wpstg-dir input:not(:checked).wpstg-root").each(function () {
557
  var $this = $(this);
558
  //if (!$this.parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled"))
559
  //{
563
 
564
  return excludedDirectories;
565
  };
 
566
  /**
567
+ * Get included extra directories of the root level
568
+ * All directories except wp-content, wp-admin, wp-includes
569
  * @returns {Array}
570
  */
571
  var getIncludedExtraDirectories = function ()
572
  {
573
  var extraDirectories = [];
574
 
575
+ $(".wpstg-dir input:checked.wpstg-extra").each(function () {
576
+ var $this = $(this);
577
+ extraDirectories.push($this.val());
578
+ });
 
 
 
 
579
 
580
  return extraDirectories;
581
  };
582
 
583
+ /**
584
+ * Get Included Extra Directories
585
+ * @returns {Array}
586
+ */
587
+ // var getIncludedExtraDirectories = function ()
588
+ // {
589
+ // var extraDirectories = [];
590
+ //
591
+ // if (!$("#wpstg_extraDirectories").val()) {
592
+ // return extraDirectories;
593
+ // }
594
+ //
595
+ // var extraDirectories = $("#wpstg_extraDirectories").val().split(/\r?\n/);
596
+ // console.log(extraDirectories);
597
+ //
598
+ // //excludedDirectories.push($this.val());
599
+ //
600
+ // return extraDirectories;
601
+ // };
602
+
603
 
604
 
605
  /**
1011
  // Undefined Error
1012
  if (false === response)
1013
  {
 
1014
  showError(
1015
  "Something went wrong! Error: No response. Go to WP Staging > Settings and lower 'File Copy Limit' and 'DB Query Limit'. Also set 'CPU Load Priority to low.'" +
1016
  "Than try again. If that does not help, " +
apps/Backend/views/clone/ajax/scan.php CHANGED
@@ -60,7 +60,7 @@
60
 
61
  <div class="wpstg-tab-section" id="wpstg-scanning-files">
62
  <h4 style="margin:0">
63
- <?php echo __("Uncheck the folders you do not want to copy. Click on them for expanding!", "wpstg")?>
64
  </h4>
65
 
66
  <?php echo $scan->directoryListing()?>
@@ -101,18 +101,13 @@
101
  <div class="wpstg-tab-section" id="wpstg-advanced-settings">
102
  <p>
103
  <?php
104
- _e('<strong>Important:</strong> Is your site using a custom login url?', 'wpstg');
105
  echo '<br/>';
106
- echo sprintf(__('Set up a <a href="%1$s" target="_blank" rel="noopener"><strong>Login Custom Link</strong></a> if login to the admin dashboard is <strong>not</strong> reachable from the default login url.', 'wpstg'),
107
- admin_url() . '/admin.php?page=wpstg-settings#wpstg_settings[loginSlug]'
 
108
  );
109
- echo '<br/>';
110
- sprintf(__('To test this you need to log out first! Than login again by using the link <a href="%1$s" target="_blank">12$s</a>.', 'wpstg'), admin_url());
111
- echo '<br/>';
112
- _e('If the login link does not work you <strong>must</strong> define the Login Custom Link option.', 'wpstg');
113
- echo '<br/>';
114
- echo '<br/>';
115
- _e('<strong>If you are using a custom login and you do not define that setting, the staging site will not be accessable!</strong>', 'wpstg');
116
  //$form = $this->di->get("forms")->get("general");
117
  //echo $form->label("wpstg_settings['loginPostId']");
118
  //echo $form->render("wpstg_settings['loginPostId']");
60
 
61
  <div class="wpstg-tab-section" id="wpstg-scanning-files">
62
  <h4 style="margin:0">
63
+ <?php echo __("Select folders to copy. Click on folder name to list subfolders!", "wpstg")?>
64
  </h4>
65
 
66
  <?php echo $scan->directoryListing()?>
101
  <div class="wpstg-tab-section" id="wpstg-advanced-settings">
102
  <p>
103
  <?php
104
+ _e('<strong>Important:</strong> Are you using a custom login url?', 'wpstg');
105
  echo '<br/>';
106
+ echo sprintf(__('Set up first <a href="%1$s"><strong>Login Custom Link</strong></a> if login to the admin dashboard is not reachable from the default url below:<pre>%2$s</pre>', 'wpstg'),
107
+ admin_url() . '/admin.php?page=wpstg-settings#wpstg_settings[loginSlug]',
108
+ admin_url()
109
  );
110
+ _e('<strong>If you do not do that step, the staging site could be unavailable!</strong>', 'wpstg');
 
 
 
 
 
 
111
  //$form = $this->di->get("forms")->get("general");
112
  //echo $form->label("wpstg_settings['loginPostId']");
113
  //echo $form->render("wpstg_settings['loginPostId']");
apps/Core/Utils/Cache.php CHANGED
@@ -8,7 +8,6 @@ if (!defined("WPINC"))
8
  }
9
 
10
  use WPStaging\WPStaging;
11
- use WPStaging\Backend\Modules\Jobs;
12
 
13
  /**
14
  * Class Cache
@@ -73,6 +72,7 @@ class Cache
73
  {
74
  throw new \Exception("Failed to create cache directory " . $this->cacheDir . '! Make sure folder permission is 755 and owner is correct. Should be www-data or similar.');
75
  }
 
76
  }
77
 
78
  /**
@@ -112,24 +112,16 @@ class Cache
112
  }
113
 
114
  // Save it to file
115
- return (file_put_contents($cacheFile, @serialize($value), LOCK_EX) !== false);
 
 
 
 
 
116
  //return (file_put_contents($cacheFile, @serialize($value)) !== false);
117
  }
118
 
119
  /**
120
- * Throw a errror message via json and stop further execution
121
- * @param string $message
122
- */
123
- protected function returnException($message = ''){
124
- wp_die( json_encode(array(
125
- 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
126
- 'status' => false,
127
- 'message' => $message,
128
- 'error' => true
129
- )));
130
- }
131
-
132
- /**
133
  * Checks if file is valid or not
134
  * @param $cacheFileName
135
  * @param bool $deleteFileIfInvalid
@@ -209,4 +201,18 @@ class Cache
209
  {
210
  return $this->cacheExtension;
211
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
8
  }
9
 
10
  use WPStaging\WPStaging;
 
11
 
12
  /**
13
  * Class Cache
72
  {
73
  throw new \Exception("Failed to create cache directory " . $this->cacheDir . '! Make sure folder permission is 755 and owner is correct. Should be www-data or similar.');
74
  }
75
+
76
  }
77
 
78
  /**
112
  }
113
 
114
  // Save it to file
115
+ //return (file_put_contents($cacheFile, @serialize($value), LOCK_EX) !== false);
116
+ if (!file_put_contents($cacheFile, @serialize($value))){
117
+ $this->returnException(" Can't save data to: " . $cacheFile);
118
+ return false;
119
+ }
120
+ return true;
121
  //return (file_put_contents($cacheFile, @serialize($value)) !== false);
122
  }
123
 
124
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  * Checks if file is valid or not
126
  * @param $cacheFileName
127
  * @param bool $deleteFileIfInvalid
201
  {
202
  return $this->cacheExtension;
203
  }
204
+
205
+
206
+ /**
207
+ * Throw a errror message via json and stop further execution
208
+ * @param string $message
209
+ */
210
+ protected function returnException($message = ''){
211
+ wp_die( json_encode(array(
212
+ 'job' => isset($this->options->currentJob) ? $this->options->currentJob : '',
213
+ 'status' => false,
214
+ 'message' => $message,
215
+ 'error' => true
216
+ )));
217
+ }
218
  }
apps/Core/Utils/Strings.php CHANGED
@@ -17,6 +17,10 @@ class Strings{
17
  * @return string
18
  */
19
  public function str_replace_first( $search, $replace, $subject ) {
 
 
 
 
20
  $pos = strpos( $subject, $search );
21
  if( $pos !== false ) {
22
  return substr_replace( $subject, $replace, $pos, strlen( $search ) );
@@ -24,4 +28,16 @@ public function str_replace_first( $search, $replace, $subject ) {
24
  return $subject;
25
  }
26
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
17
  * @return string
18
  */
19
  public function str_replace_first( $search, $replace, $subject ) {
20
+
21
+ if( empty( $search ) )
22
+ return $subject;
23
+
24
  $pos = strpos( $subject, $search );
25
  if( $pos !== false ) {
26
  return substr_replace( $subject, $replace, $pos, strlen( $search ) );
28
  return $subject;
29
  }
30
 
31
+ /**
32
+ * Get last string after last certain element in string
33
+ * Example: getLastElemAfterString('/', '/path/stagingsite/subfolder') returns 'subfolder'
34
+ * @param string $needle
35
+ * @param string $haystack
36
+ * @return string
37
+ */
38
+ public function getLastElemAfterString( $needle, $haystack ) {
39
+ $pos = strrpos( $haystack, $needle );
40
+ return $pos === false ? $haystack : substr( $haystack, $pos + 1 );
41
+ }
42
+
43
  }
apps/Core/WPStaging.php CHANGED
@@ -29,7 +29,7 @@ final class WPStaging {
29
  /**
30
  * Plugin version
31
  */
32
- const VERSION = "2.2.4";
33
 
34
  /**
35
  * Plugin name
@@ -44,7 +44,7 @@ final class WPStaging {
44
  /**
45
  * Compatible WP Version
46
  */
47
- const WP_COMPATIBLE = "4.9.4";
48
 
49
  /**
50
  * Slug: Either wp-staging or wp-staging-pro
29
  /**
30
  * Plugin version
31
  */
32
+ const VERSION = "2.2.5";
33
 
34
  /**
35
  * Plugin name
44
  /**
45
  * Compatible WP Version
46
  */
47
+ const WP_COMPATIBLE = "4.9.5";
48
 
49
  /**
50
  * Slug: Either wp-staging or wp-staging-pro
apps/Frontend/Frontend.php CHANGED
@@ -30,8 +30,7 @@ class Frontend extends InjectionAware {
30
 
31
  $this->settings = json_decode( json_encode( get_option( "wpstg_settings", array() ) ) );
32
 
33
- $this->loginSlug = isset($this->settings->loginSlug) ? $this->settings->loginSlug : '';
34
-
35
  }
36
 
37
  /**
@@ -65,18 +64,45 @@ class Frontend extends InjectionAware {
65
  /**
66
  * Check permissions for the page to decide whether or not to disable the page
67
  */
 
 
 
 
 
 
 
68
  public function checkPermissions() {
69
  $this->resetPermaLinks();
70
 
71
- if( $this->disableLogin() ) {
72
- wp_die( sprintf( __( 'Access denied. <a href="%1$s">Login</a> first to access this site', 'wpstg' ), $this->getLoginUrl() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  /**
75
  * Lines below are not used at the moment but are fully functional
76
  */
77
- //$login = new loginForm();
78
- //$login->renderForm();
79
- //die();
80
  }
81
  }
82
 
@@ -84,15 +110,24 @@ class Frontend extends InjectionAware {
84
  * Get login link
85
  * @return string
86
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  private function getLoginUrl() {
88
-
89
- if( empty( $this->loginSlug ) ) {
90
- return get_home_url() . '/wp-admin';
91
  }
92
 
93
- return get_home_url() . '/?' . $this->loginSlug;
94
- }
95
-
96
  /**
97
  * Check if the page should be blocked
98
  * @return bool
30
 
31
  $this->settings = json_decode( json_encode( get_option( "wpstg_settings", array() ) ) );
32
 
33
+ $this->loginSlug = isset( $this->settings->loginSlug ) ? $this->settings->loginSlug : '';
 
34
  }
35
 
36
  /**
64
  /**
65
  * Check permissions for the page to decide whether or not to disable the page
66
  */
67
+ // public function checkPermissions() {
68
+ // $this->resetPermaLinks();
69
+ //
70
+ // if( $this->disableLogin() ) {
71
+ // wp_die( sprintf( __( 'Access denied. <a href="%1$s">Login</a> first to access this site', 'wpstg' ), $this->getLoginUrl() ) );
72
+ // }
73
+ // }
74
  public function checkPermissions() {
75
  $this->resetPermaLinks();
76
 
77
+ if($this->disableLogin() ) {
78
+ //wp_die( sprintf( __( 'Access denied. <a href="%1$s">Login</a> first to access this site', 'wpstg' ), $this->getLoginUrl() ) );
79
+ //}
80
+ $args = array(
81
+ 'echo' => true,
82
+ // Default 'redirect' value takes the user back to the request URI.
83
+ 'redirect' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
84
+ 'form_id' => 'loginform',
85
+ 'label_username' => __( 'Username or Email Address' ),
86
+ 'label_password' => __( 'Password' ),
87
+ 'label_remember' => __( 'Remember Me' ),
88
+ 'label_log_in' => __( 'Log In' ),
89
+ 'id_username' => 'user_login',
90
+ 'id_password' => 'user_pass',
91
+ 'id_remember' => 'rememberme',
92
+ 'id_submit' => 'wp-submit',
93
+ 'remember' => true,
94
+ 'value_username' => '',
95
+ // Set 'value_remember' to true to default the "Remember me" checkbox to checked.
96
+ 'value_remember' => false,
97
+ );
98
+
99
 
100
  /**
101
  * Lines below are not used at the moment but are fully functional
102
  */
103
+ $login = new loginForm();
104
+ $login->renderForm($args);
105
+ die();
106
  }
107
  }
108
 
110
  * Get login link
111
  * @return string
112
  */
113
+ // private function getLoginUrl() {
114
+ //
115
+ // if( empty( $this->loginSlug ) ) {
116
+ // //return get_home_url() . '/wp-admin';
117
+ // return get_site_url() . '/wp-admin';
118
+ //
119
+ // }
120
+ //
121
+ // return get_home_url() . '/?' . $this->loginSlug;
122
+ // }
123
+ /**
124
+ * Get path to wp-login.php
125
+ * @return string
126
+ */
127
  private function getLoginUrl() {
128
+ return get_site_url() . '/wp-login.php';
 
 
129
  }
130
 
 
 
 
131
  /**
132
  * Check if the page should be blocked
133
  * @return bool
apps/Frontend/loginForm.php CHANGED
@@ -4,7 +4,57 @@ namespace WPStaging\Frontend;
4
 
5
  class loginForm {
6
 
7
- public function renderForm(){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  $this->getHeader();
9
  $this->getLoginForm();
10
  $this->getFooter();
@@ -139,27 +189,109 @@ class loginForm {
139
  * Render login form by using native wp function wp_login_form
140
  * return string
141
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  private function getLoginForm() {
143
- $this->getHeader();
144
- echo '<body id="error-page">';
145
- echo __( 'Access denied. Login to access this site', 'wpstg' );
146
-
147
- $args = array(
148
- 'redirect' => admin_url(),
149
- 'redirect' => admin_url(),
150
- 'form_id' => 'wpstg-loginform',
151
- 'label_username' => __( 'Username', 'wpstg' ),
152
- 'label_password' => __( 'Password', 'wpstg' ),
153
  'label_remember' => __( 'Remember Me' ),
154
- 'label_log_in' => __( 'Log In Staging Site' ),
155
- 'remember' => true
 
 
 
 
 
 
 
156
  );
157
- wp_login_form( $args );
158
- $this->getFooter();
159
- }
160
 
161
- private function getFooter() {
162
- echo '</html>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  }
4
 
5
  class loginForm {
6
 
7
+ /**
8
+ *
9
+ * @var type array
10
+ */
11
+ private $args = array();
12
+ private $error;
13
+
14
+ function __construct() {
15
+ $this->login();
16
+ }
17
+
18
+ private function login() {
19
+
20
+
21
+ if( is_user_logged_in() ) {
22
+ return false;
23
+ }
24
+
25
+ if(!isset($_POST['wpstg-username']) || !isset ($_POST['wpstg-pass'])){
26
+ return false;
27
+ }
28
+
29
+
30
+ if( isset( $_POST['wpstg-submit'] ) && (empty( $_POST['wpstg-username'] ) || empty( $_POST['wpstg-pass'] ) ) ) {
31
+ $this->error = 'No username or password given!';
32
+ return false;
33
+ }
34
+
35
+ $user_data = get_user_by( 'login', $_POST['wpstg-username'] );
36
+
37
+ if( !$user_data ) {
38
+ $user_data = get_user_by( 'email', $_POST['wpstg-username'] );
39
+ }
40
+
41
+ if( !$user_data ) {
42
+ return false;
43
+ }
44
+
45
+ if( wp_check_password( $_POST['wpstg-pass'], $user_data->user_pass, $user_data->ID ) ) {
46
+
47
+ $rememberme = isset($_POST['rememberme']) ? true : false;
48
+
49
+ wp_set_auth_cookie( $user_data->ID, $rememberme );
50
+ wp_set_current_user( $user_data->ID, $_POST['wpstg-username'] );
51
+ do_action( 'wp_login', $_POST['wpstg-username'], get_userdata( $user_data->ID ) );
52
+ header( 'Location:' . get_site_url() . '/wp-admin/' );
53
+ }
54
+ }
55
+
56
+ public function renderForm( $args = array() ) {
57
+ $this->args = $args;
58
  $this->getHeader();
59
  $this->getLoginForm();
60
  $this->getFooter();
189
  * Render login form by using native wp function wp_login_form
190
  * return string
191
  */
192
+ // private function getLoginForm() {
193
+ // $this->getHeader();
194
+ // echo '<body id="error-page">';
195
+ // echo __( 'Access denied. Login to access this site', 'wpstg' );
196
+ //
197
+ // $args = array(
198
+ // 'redirect' => admin_url(),
199
+ // 'redirect' => admin_url(),
200
+ // 'form_id' => 'wpstg-loginform',
201
+ // 'label_username' => __( 'Username', 'wpstg' ),
202
+ // 'label_password' => __( 'Password', 'wpstg' ),
203
+ // 'label_remember' => __( 'Remember Me' ),
204
+ // 'label_log_in' => __( 'Log In Staging Site' ),
205
+ // 'remember' => true
206
+ // );
207
+ // wp_login_form( $args );
208
+ // $this->getFooter();
209
+ // }
210
+
211
+ private function getFooter() {
212
+ echo '</html>';
213
+ }
214
+
215
+ /**
216
+ * Provides a simple login form for use anywhere within WordPress.
217
+ *
218
+ * The login format HTML is echoed by default. Pass a false value for `$echo` to return it instead.
219
+ *
220
+ * @since 3.0.0
221
+ *
222
+ * @param array $args {
223
+ * Optional. Array of options to control the form output. Default empty array.
224
+ *
225
+ * @type bool $echo Whether to display the login form or return the form HTML code.
226
+ * Default true (echo).
227
+ * @type string $redirect URL to redirect to. Must be absolute, as in "https://example.com/mypage/".
228
+ * Default is to redirect back to the request URI.
229
+ * @type string $form_id ID attribute value for the form. Default 'loginform'.
230
+ * @type string $label_username Label for the username or email address field. Default 'Username or Email Address'.
231
+ * @type string $label_password Label for the password field. Default 'Password'.
232
+ * @type string $label_remember Label for the remember field. Default 'Remember Me'.
233
+ * @type string $label_log_in Label for the submit button. Default 'Log In'.
234
+ * @type string $id_username ID attribute value for the username field. Default 'user_login'.
235
+ * @type string $id_password ID attribute value for the password field. Default 'user_pass'.
236
+ * @type string $id_remember ID attribute value for the remember field. Default 'rememberme'.
237
+ * @type string $id_submit ID attribute value for the submit button. Default 'wp-submit'.
238
+ * @type bool $remember Whether to display the "rememberme" checkbox in the form.
239
+ * @type string $value_username Default value for the username field. Default empty.
240
+ * @type bool $value_remember Whether the "Remember Me" checkbox should be checked by default.
241
+ * Default false (unchecked).
242
+ *
243
+ * }
244
+ * @return string|void String when retrieving.
245
+ */
246
  private function getLoginForm() {
247
+
248
+ $arguments = array(
249
+ 'echo' => true,
250
+ // Default 'redirect' value takes the user back to the request URI.
251
+ 'redirect' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
252
+ 'form_id' => 'loginform',
253
+ 'label_username' => __( 'Username or Email Address' ),
254
+ 'label_password' => __( 'Password' ),
 
 
255
  'label_remember' => __( 'Remember Me' ),
256
+ 'label_log_in' => __( 'Log In' ),
257
+ 'id_username' => 'user_login',
258
+ 'id_password' => 'user_pass',
259
+ 'id_remember' => 'rememberme',
260
+ 'id_submit' => 'wp-submit',
261
+ 'remember' => true,
262
+ 'value_username' => '',
263
+ // Set 'value_remember' to true to default the "Remember me" checkbox to checked.
264
+ 'value_remember' => false
265
  );
 
 
 
266
 
267
+ $args = empty( $this->args ) ? $arguments : $this->args;
268
+
269
+ $form = '
270
+ <form name="' . $args['form_id'] . '" id="' . $args['form_id'] . '" action="" method="post">
271
+ <p class="login-username">
272
+ <label for="' . esc_attr( $args['id_username'] ) . '">' . esc_html( $args['label_username'] ) . '</label>
273
+ <input type="text" name="wpstg-username" id="' . esc_attr( $args['id_username'] ) . '" class="input" value="' . esc_attr( $args['value_username'] ) . '" size="20" />
274
+ </p>
275
+ <p class="login-password">
276
+ <label for="' . esc_attr( $args['id_password'] ) . '">' . esc_html( $args['label_password'] ) . '</label>
277
+ <input type="password" name="wpstg-pass" id="' . esc_attr( $args['id_password'] ) . '" class="input" value="" size="20" />
278
+ </p>
279
+ ' . ( $args['remember'] ? '<p class="login-remember"><label><input name="rememberme" type="checkbox" id="' . esc_attr( $args['id_remember'] ) . '" value="forever"' . ( $args['value_remember'] ? ' checked="checked"' : '' ) . ' /> ' . esc_html( $args['label_remember'] ) . '</label></p>' : '' ) . '
280
+ <p class="login-submit">
281
+ <input type="submit" name="wpstg-submit" id="' . esc_attr( $args['id_submit'] ) . '" class="button button-primary" value="' . esc_attr( $args['label_log_in'] ) . '" />
282
+ <input type="hidden" name="redirect_to" value="' . esc_url( $args['redirect'] ) . '" />
283
+ </p>
284
+ <p>
285
+ ' . $this->error . '
286
+ </p>
287
+
288
+
289
+ </form>';
290
+
291
+ if( $args['echo'] )
292
+ echo $form;
293
+ else
294
+ return $form;
295
  }
296
 
297
  }
readme.txt CHANGED
@@ -9,7 +9,8 @@ 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.4
 
13
 
14
  A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
15
 
@@ -145,6 +146,17 @@ https://wp-staging.com
145
 
146
  == Changelog ==
147
 
 
 
 
 
 
 
 
 
 
 
 
148
  = 2.2.4 =
149
  * New: Replace even hardcoded links and server path by using search & replace through all staging site database tables
150
  * New: New and improved progress bar with elapsed time
@@ -215,35 +227,9 @@ https://wp-staging.com
215
  * Fix: Other staging site can be overwritten when Edit/Update clone function is executed
216
  * Fix: Several improvements to improve reliability and prevent timeouts and fatal errors during cloning
217
 
218
- = 2.1.2 =
219
- * Fix: Remove LOCK_EX parameter in file_put_contents(). LOCK_EX is not working on several systems which results in cloning process timeouts
220
- * Fix: Huge Performance improvement in copying process by removing duplicate file entries in the cache file. This also prevents weird timeout issues on some hosted websites
221
- * Fix: Error 500 when debug mode is activated
222
- * Fix: Limit maximum execution time to 30 seconds
223
- * Fix: Sanitize Clone Names and Keys to fix "clone not found" issue in upgrade routine
224
- * Fix: Do not clone the plugin wps-hide-login
225
- * Fix: Staging sites can not be deleted if they are very big
226
- * Fix: Link to staging site is undefined
227
- * Tweak: Better admin message for asking for a review
228
- * Tweak: Remove table wpstg_rmpermalinks_executed when plugin is uninstalled
229
- * New: New setting to specify the maximum amount of files copied within one ajax call to fix godaddy and bluehost ajax 404 errors. Default 10 per batch
230
-
231
-
232
- = 2.1.1 =
233
- * New: Add link to tutorial explaining the process of pushing modification to the live site
234
-
235
- = 2.1.0 =
236
- * New: Exclude unneccessary files from cloning process: .tmp, .log, .htaccess, .git, .gitignore, desktop.ini, .DS_Store, .svn
237
- * New: More details for debugging in Tools->System Info
238
- * Fix: Check if tables in staging site exists before attempting to modify them
239
- * Fix: WordPress in sub directories were not opening
240
- * Fix: Nonce check not working if nonce life time is filtered by another plugin WP Bug: https://core.trac.wordpress.org/ticket/41617#comment:1
241
- * Fix: Access to staging site not working, if WP_SITEURL and WP_HOME is defined in wp-config.php
242
- * Tweak: Exclude wp-content/cache folder from copying process
243
-
244
  Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
245
 
246
  == Upgrade Notice ==
247
 
248
- = 2.2.4 =
249
- 2.2.1 * New search & replace feature for serialized data
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.
16
 
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
152
+ * 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
153
+ * Fix: Incorrect login path to staging site if WordPress is installed in subdirectory
154
+ * Fix: Login url is wrong if WP is installed in subfolder
155
+ * Fix: If PHP 5.6.34 is used, the cloning process could be unfinished due to use of private member in protected class
156
+ * Tweak: Only wp root folders are pre selected before cloning is starting
157
+ * Tweak: Change WP_HOME or WP_SITEURL constants of staging site if they are defined in wp-config.php
158
+
159
+
160
  = 2.2.4 =
161
  * New: Replace even hardcoded links and server path by using search & replace through all staging site database tables
162
  * New: New and improved progress bar with elapsed time
227
  * Fix: Other staging site can be overwritten when Edit/Update clone function is executed
228
  * Fix: Several improvements to improve reliability and prevent timeouts and fatal errors during cloning
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  Complete changelog: [https://wp-staging.com/changelog.txt](https://wp-staging.com/changelog.txt)
231
 
232
  == Upgrade Notice ==
233
 
234
+ = 2.2.5 =
235
+ 2.2.5 * New: Compatible to WP 4.9.5
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.4
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.5
11
  * Text Domain: wpstg
12
  * Domain Path: /languages/
13